Ethereum 上的 EVM(Ethereum Virtual Machine)可以執行程式,而 EVM 上的可執行程式基本上是 Bytecode 的形式,所以所謂的 Smart Contract 就是存放在 Ethereum 上的 Bytecode,然後可由 EVM 來執行。

Bytecode Smart Contract

直接用 Bytecode 寫 Smart Contract

我們來嘗試一下直接用 Bytecode 來寫 Smart Contract,以下這段程式碼主要內容是執行運算後,將運算結果存放在 0 這個位置:

PUSH1 0x03
PUSH1 0x05
ADD        // 3 + 5 -> 8
PUSH1 0x02
MUL        // 8 * 2 -> 16
PUSH1 0x00
SSTORE     // 將 16 存到 0 這個位置

這段程式轉成 Bytecode 就是:

0x60 0x03
0x60 0x05
0x01
0x60 0x02
0x02
0x60 0x00
0x55

也就是:

0x6003600501600202600055

接下來讓我們連上在上一篇文章中所建立的私有鏈 net42,我們藉由發送一個交易,但不要指定 to,如此就是告訴 EVM,我們要建立一個 Smart Contract,請在 Geth 輸入:

> var bytecode = "0x6003600501600202600055"

> var createTx = eth.sendTransaction({ from: eth.accounts[0], data: bytecode })

接下來請進行挖礦,以完成交易,交易完成後我們就可以看結果:

> miner.start(1)

> miner.stop()

> var created = eth.getTransactionReceipt(createTx).contractAddress

> eth.getStorageAt(created, 0)
"0x0000000000000000000000000000000000000000000000000000000000000010"

我們可以看到位置 0 存放了 0x10 這個值,這也就是運算結果 16 的 16 進位換算結果。

操作 Smart Contract

我們繼續來對 Smart Contract 做一些操作,我們使用以下的程式:

PUSH1 0x11 // 17
PUSH1 0x00
SSTORE     // 將 17 存放在 0 這個位置

然後在 Geth 送出交易,請注意這時要指定 to 為我們剛剛創建的 Smart Contract 位址,也就是 created:

> var bytecode2 = "0x6011600055"

> eth.sendTransaction({ from: eth.accounts[0], to: created, data: bytecode2 })

然後我們挖礦,完成交易後查看結果:

> eth.getStorageAt(created, 0)
"0x0000000000000000000000000000000000000000000000000000000000000010"

沒想到結果還是 0x10!為什麼?

我們來看看真實記在鏈上 Smart Contract 的原始碼好了:

> eth.getCode(created)
"0x"

我們並沒有存下程式碼在鏈上,一開始的 Bytecode 程式碼只是進行運算,並存放結果到位置 0 而已,我們要想辦法將我們之後要繼續使用的 function bytecode 存下來,以便之後能繼續使用。

撰寫「真的」Smart Contract

假設我們希望 Smart Contract 有一個 function 可在存取位置 0 存取資料,這段程式碼是:

PUSH1 0x00 CALLDATALOAD
PUSH1 0x00
SSTORE

Bytecode 就是:

0x600035600055

但要把這段 function 程式碼發佈成為真正可用的 Smart Contract 還需要做一些事來將這段程式碼「打包」,大概的程式碼如下:

00: PUSH1 0x06
02: PUSH1 0x0c
04: PUSH1 0x00
06: CODECOPY
07: PUSH1 0x06
09: PUSH1 0x00
0b: RETURN
0c: 600035600055

Bytecode 就是:

0x6006600c60003960066000f3600035600055

接下來我們到 Geth 試試看:

> var bytecode3 = "0x6006600c60003960066000f3600035600055"

> var createTx3 = eth.sendTransaction({ from: eth.accounts[0], data: bytecode3 })

> miner.start(1)

> miner.stop()

> var created3 = eth.getTransactionReceipt(createTx3).contractAddress

> eth.getCode(created3)
"0x600035600055"

這次我們有成功將 function 的原始碼存到 Ethereum 了!

操作「真的」Smart Contract

接下來我們就來操作看看這個 Smart Contract 的 function:

> eth.getStorageAt(created3, 0)
"0x0000000000000000000000000000000000000000000000000000000000000000"

> eth.sendTransaction({ from: eth.accounts[0], to: created3, data: "0x67" })

> miner.start(1)

> miner.stop()

> eth.getStorageAt(created3, 0)
"0x6700000000000000000000000000000000000000000000000000000000000000"

我們可以看到原本位置 0 是 0x00,經過操作之後就變成 0x67 了!

Solidity Smart Contract

使用 Bytecode 來寫 Smart Contract 實在太 Hardcore 了!我們身為普通人還是用普通人的方式來寫 Smart Contact 好了,這邊提供一個用來寫 Smart Contract 的程式語言 — Solidity。

我們用 Solidity 提供的一些平易近人的語法,然後在同過編譯器來將 Solidity 轉譯成 Bytecode,如此寫 Smart Contract 就不會像之前用 Bytecode 寫這麼麻煩了!這邊我們稍微先簡單介紹一下 Solidity,之後我們會慢慢深入。

資料型態

Solidity 主要的資料型態有以下:

  • bool
  • int
  • uint(Unsigned)
  • address

其中 address 有 .balace、.transfer(unit) 及.send(unit)這些 function 可以使用,unit 以Wei 為單位。

Functions

Solidity 可以定義 function:

  • 要使用保留字 function
  • function 可以有參數
  • function 可設為 public 或 private
  • function 可以回傳資料(或不回傳),如:returns (unit)

另外像是 for、do while、break、continue、if then else、return 等語法也有提供,如果 function 有要收取 Ether 的話,需要加上保留字 payable。

特殊變數

Solidity 有一些特殊的變數是與目前的交易相關的,例如:

  • msg.sender,代表目前呼叫這個 function 的 address
  • msg.value,代表目前呼叫這個 function 時所付的 Ether,以 Wei 為單位

簡單範例

我們先用一個簡單的範例示範一下如何用 Solidity 撰寫 Smart Contract 吧,範例程式碼如下:

pragma solidity ^0.4.22;

contract Owned {
  address public owner;

  constructor() public {
    owner = msg.sender;
  }
}

這個範例程式碼很簡單,在 Smart Contract 發佈時,建構子就會將 owner 成員變數設成是發佈 Smart Contract 的位址。

由於 Solidity 程式 EVM 看不懂,我們需要將 Solidity 程式碼轉成 Bytecode,我們這邊先使用 Remix 這個工具來編譯看看 Solidity。請在編輯區複製貼上我們的範例程式碼,Remix 會自動編譯 Solidity,接下來請選擇 ”Compile“ “Owned” “Detail”,你可以在 ”Assembly“ 區塊看到編譯完後的 Solidity 實際做了什麼。

結語

我們從最底層的 Bytecode 介紹起,了解了用 Bytecode 撰寫 Smart Contract,但實在太麻煩,所以我們改使用 Solidity 來撰寫 Smart Contract,如此寫 Smart Contract 就不會像之前用 Bytecode 寫這麼麻煩了!後續我們會使用 Solidity 來撰寫功能更豐富的 Smart Contract,慢慢深入了解 Solidity。

之前說過,Blockchain 基本上是因為金流帳本這樣的問題而被創造出來的,也就是說區塊鏈非常適合運用在金流的應用上,我們也可以建立自己的 Blockchain 來搭建自己的金流系統,不過在 Ethereum 上 Smart Contract 這種設計讓我們擁有可以在 Ethereum 區塊鏈上創造自己金流系統的能力,如此我們就不需要自己建一條鏈了。

我們使用 Smart Contract 仿造貨幣性質創造了數位資產(說穿了其實就是在 Smart Contract 上紀錄的變數而已),而這種具貨幣性質的數位資產又被稱作 Token,如此我們就可以在應用程式中使用這個去中心化的金流系統,由於 Token 的應用很普遍,大部分的功能都已經標準化了,我們只要仿造標準來實作就可以發行自己的數位貨幣了。

在這邊我們就練習一下怎麼使用 Mist 發佈 Token Smart Contract 來發行自己的數位貨幣。(目前我們還沒有學習過如何撰寫 Smart Contract,因此這邊會先直接提供範例程式碼,實作的部分我們之後再慢慢學習)

以下是我們的範例程式碼:

請打開 Mist,如下圖點擊 Contract,然後點擊 Deploy New Contract。

你會看到如下圖的頁面,請在 Solidity Contract Source Code 中貼上我們上面提供的範例程式碼。

貼上範例程式碼之後,Mist 會自動編譯程式,檢查是否有語法上的錯誤,如果沒問題,右方的 Select Contract to Deploy 就會出現選項,在這邊我們選擇 Token ERC 20。

選擇 Token ERC 20 之後,右方會出現要初始化 Contract 的參數表單,有 Initial supply、Token name、Token symbol 需要填寫。Initial supply 代表 Token 的總發行量是多少,我這邊設定成 7777777777,你可以設成你想要的數字。Token name 就是這個 Token 要叫什麼名字,這邊我設定成 7 Token,你想要取 Dog Coin 或是 Cat Coin 也都可以。Token symbol 就是這個 Token 要用什麼代號,像是美金就是用 $、Ether 是用 ETH,這邊我設定成 7token,你可以取自己覺得帥的代號。

借下來捲動頁面到底下,這邊你可以設定 Gas Fee 要用多少,這邊就看自己高興,我是沒有做任何調整。最後按下 Deploy!

與區塊鏈互動基本上就是做交易,所以發佈 Smart Contract 也就需要發出一個交易,Mist 會彈出視窗顯示交易資訊及可能的 Gas Fee,請輸入密碼進行交易。

等待一下子就可以看到我們的 Smart Contract 發佈交易已經出現在頁面底端了,只要等待交易被確認,那一個新的數位貨幣就誕生了!

Smart Contract 發佈完成後,請點擊你的帳戶,如下圖所示。

你會發現你的帳戶底下多了一個 Token 紀錄,在這邊我擁有了 7 Token 共 7,777,777,777 顆!如果這個 Token 被承認,那我就是超級有錢人啦!

接下來我們來實際轉一些 Token 給朋友看看,在區塊鏈的世界我們不需要銀行及任何中心化的系統就可以將錢轉給朋友了,也就是我們現在擁有了一個去中心化的金流系統!讓我們來實際感受一下吧!

請點擊 7 Token 選項右邊的 Send,如下圖所示:

填入朋友的 Ethereum 帳戶位址到 To 這個欄位,Amount 填入你想要匯出的 Token 數量,在這邊我填 40,然後捲動頁面到底端送出交易。

等待一下子交易確認後,40 個 Token 就完成匯出了!

我們可以到 Etherscan上確認交易是否真的完成:https://ropsten.etherscan.io/address/0xed29cd5a72b06793601da5f0c4ec3ef5224037c7#tokentxns

的確有 40 個 7 Token 轉到朋友帳戶了!

在這個練習中,我們了解了 Token 到底是什麼,然後我們也實際發行了自己的數位貨幣,完成了自己的去中心化的金流系統,當我們想要轉帳時,我們再也不需要銀行及任何中心化的系統就可以將錢轉給朋友了,只要我們雙方都信任這個數位貨幣,價值的交換就能無遠弗屆地進行了!

Fukuball

我是林志傑,網路上常用的名字是 Fukuball。我使用 PHP 及 Python,對機器學習及區塊鏈技術感到興趣。 https://www.fukuball.com

Staff Engineer

Taiwan