动手编写一个以太坊智能合约
如何部署、調(diào)用智能合約
RPC
之前的章節(jié)中講到了怎么寫、部署合約以及與合約互動?,F(xiàn)在該講講與以太坊網(wǎng)絡和智能合約溝通的細節(jié)了。
一個以太坊節(jié)點提供一個RPC界面。這個界面給Dapp(去中心化應用)訪問以太坊區(qū)塊鏈的權限和節(jié)點提供的功能,比如編譯智能合約代碼,它用JSON-RPC 2.0規(guī)范(不支持提醒和命名的參數(shù)) 的子集作為序列化協(xié)議,在HTTP和IPC (linux/OSX上的unix域接口,在Windows上叫pipe’s)上可用。
慣例
RPC界面會使用一些慣例,但它們不是JSON-RPC 2.0規(guī)范的一部分,這些慣例如下:
-
數(shù)字是十六進制編碼。做這個決定是因為有些語言對運行極大的數(shù)字沒有或有很少的限制。為了防止這些錯誤數(shù)字類型是十六進制編碼,由開發(fā)者來分析這些數(shù)字并正確處理它們。在維基頁百科查看十六進制編碼章節(jié)查看案例。
-
默認區(qū)塊數(shù)字。幾個RPC 方法接受區(qū)塊數(shù)字。在一些情況下,給出區(qū)塊數(shù)字是不可能的或者不太方便。在那樣的情況下,默認區(qū)塊數(shù)字可以是以下字符串中的一個[”earliest”, “l(fā)atest”, “pending”]。在維基頁面可查看使用默認區(qū)塊參數(shù)的RPC方法列表。
部署合約
我們會通過不同的步驟來部署下面的合約,但只用到RPC界面。
contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input * 7); return input * 7; } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
要做的第一件事是確保HTTP RPC界面可用。這意味著我們在開始為geth供應—rpc標志,為eth提供-j標志。在這個例子中,用的是私有開發(fā)鏈上的geth節(jié)點。通過這種方法,我們就不需要真實網(wǎng)絡上的以太幣了。
\> geth --rpc --dev --mine --minerthreads 1 --unlock 0 console 2>>geth.log- 1
這會在http://localhost:8545上啟動HTTP RPC界面。
注意:geth支持CORS查看—rpccorsdomain標志了解更多。?
我們可以通過用curl檢索coinbase地址和余額來證明界面正在運行。請注意這些例子中的數(shù)據(jù)在你本地的節(jié)點上會有所不同。如果你想要試試這些參數(shù),視情況替換需要的參數(shù)。
- 1
- 2
- 3
- 4
記不記得前面說過數(shù)字是十六進制編碼?在這個情況下,余額作為十六進制字符串以Wei的形式返還。如果希望余額作為數(shù)字以太幣為單位,可以從控制臺用web3,示例如下:
\> web3.fromWei("0x1639e49bba16280000", "ether") "410"- 1
- 2
現(xiàn)在我們在私有開發(fā)鏈上有一些以太幣,就可以部署合約了。第一步是驗證solidity編譯器可用,可以用eth_getCompilers RPC method方法來檢索可用的編譯器,示例如下:
\> curl --data '{"jsonrpc":"2.0","method": "eth_getCompilers", "id": 3}' localhost:8545 {"id":3,"jsonrpc":"2.0","result":["Solidity"]}- 1
- 2
我們可以看到solidity編譯器可用。
下一步是把Multiply7合約編譯到可以發(fā)送給以太坊虛擬機的字節(jié)代碼中,示例如下:
\> curl --data '{"jsonrpc":"2.0","method": "eth_compileSolidity", "params": ["contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input {"id":4,"jsonrpc":"2.0","result":{"Multiply7":{"code":"0x6060604052605f8060106000396000f360606040- 1
- 2
現(xiàn)在我們有了編譯代碼,需要決定花多少gas去部署它。RPC界面有eth_estimateGas方法,會給我們一個預估數(shù)量,如下:
\> curl --data '{"jsonrpc":"2.0","method": "eth_estimateGas", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "data": "0x6060604052605f8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b60586004356007810260609081526000907f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da90602090a15060070290565b5060206060f3"}], "id": 5}' localhost:8545 {"id":5,"jsonrpc":"2.0","result":"0xb8a9"}- 1
- 2
最后部署合約。
\> curl --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "gas": "0xb8a9", "data": "0x6060604052605f8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b60586004356007810260609081526000907f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da90602090a15060070290565b5060206060f3"}], "id": 6}' localhost:8545 {"id":6,"jsonrpc":"2.0","result":"0x3a90b5face52c4c5f30d507ccf51b0209ca628c6824d0532bcd6283df7c08- 1
- 2
交易由節(jié)點接受,交易散表被返還。我們可以用這個散表來跟蹤交易。
下一步是決定部署合約的地址。每個執(zhí)行的交易都會創(chuàng)建一個接收。這個接收包含交易的各種信息,比如交易被包含在哪個區(qū)塊,以太坊虛擬機用掉多少gas。如果交易創(chuàng)建了一個合約,它也會包含合約地址。我們可以用eth_getTransactionReceipt RPC方法檢索接收,示例如下:
\> curl --data '{"jsonrpc":"2.0","method": "eth_getTransactionReceipt", "params": ["0x3a90b5face52c4c5f30d507ccf51b0209ca628c6824d0532bcd6283df7c08a7c"], "id": 7}' localhost:8545 {"id":7,"jsonrpc":"2.0","result":{"transactionHash":"0x3a90b5face52c4c5f30d507ccf51b0209ca628c682- 1
- 2
可以看到,合約在0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d上被創(chuàng)建。如果你得到了零而不是接收,說明還沒有被納入?yún)^(qū)塊。這時,要檢查看看你的礦工是否在運行,然后重新試一遍。
和智能合約互動
現(xiàn)在已經(jīng)部署了合約,我們可以和它互動了。有兩種方法進行互動,即發(fā)送交易或調(diào)用。在本節(jié)的例子中,將會發(fā)送交易到合約的multiply方法里。
在我們的實例中,需要具體說明from、to 和data參數(shù)。From是我們賬戶的公共地址,to是合約地址,Data參數(shù)有一點復雜,它包括了規(guī)定調(diào)用哪個方法和哪個參數(shù)的負載量。這就需要ABI發(fā)揮作用了,ABI規(guī)定了如何為以太坊虛擬機規(guī)定和編碼數(shù)據(jù)。
負載量的字節(jié)是功能選擇符,規(guī)定了調(diào)用哪個方法。它取Keccak散表的頭4個字節(jié),涵蓋功能名稱參數(shù)類型,并進行十六進制編碼。multiply功能接受一個參數(shù)。示例如下:
\> web3.sha3("multiply(uint256)").substring(0, 8) "c6888fa1"- 1
- 2
下一步是編碼參數(shù)。我們只有一個unit256,假定提供了值6。ABI有一個章節(jié)規(guī)定了編碼uint字節(jié)的方法,如下:
int<M>: enc(X) is the big-endian two’s complement encoding of X, padded on the higher-oder (left) side with 0xff for negative X and with zero 字節(jié)s for positive X such that the length is a multiple of 32 bytes.- 1
它會編碼到?
0000000000000000000000000000000000000000000000000000000000000006。?
將功能選擇符和編碼參數(shù)結(jié)合起來,數(shù)據(jù)就會變成0xc6888fa10000000000000000000000000000000000000000000000000000000000000006。
我們來試一下:
\> curl --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "to": "0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d", "data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}], "id": 8}' localhost:8545 {"id":8,"jsonrpc":"2.0","result":"0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869- 1
- 2
由于我們發(fā)送了交易,于是有交易散表返回。如果檢索接收,可以看到一些新內(nèi)容,如下:
{ blockHash: "0xbf0a347307b8c63dd8c1d3d7cbdc0b463e6e7c9bf0a35be40393588242f01d55", blockNumber: 268, contractAddress: null, cumulativeGasUsed: 22631, gasUsed: 22631, logs: [{ address: "0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d", blockHash: "0xbf0a347307b8c63dd8c1d3d7cbdc0b463e6e7c9bf0a35be40393588242f01d55", blockNumber: 268, data: "0x000000000000000000000000000000000000000000000000000000000000002a", logIndex: 0, topics: ["0x24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"], transactionHash: "0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869d74", transactionIndex: 0 }], transactionHash: "0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869d74", transactionIndex: 0 }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
接收包含一個日志。日志由以太坊虛擬機在交易執(zhí)行時生成,包含接收。如果我們看Multiply功能,可以看到打印事件和輸入次數(shù)7一起被提出。由于打印事件的參數(shù)是uint256,因此可以根據(jù)ABI規(guī)則對它進行編碼,這樣就會得到預期的十進制42。
\> web3.sha3("Print(uint256)") "24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"- 1
- 2
這只是對一些最常見任務的簡單介紹。在RPC維基頁面查看可用RPC方法的完整列表。
Web3.js
正如在之前的案例所見,使用JSON-RPC界面相當單調(diào)乏味且容易出錯,尤其是在處理ABI的時候。Web3.js是Javascript庫,它的目標是提供更友好的界面,減少出錯機會。
用web3部署Multiply7合約看起來是這樣:
var source = 'contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input var compiled = web3.eth.compile.solidity(source); var code = compiled.Multiply7.code; var abi = compiled.Multiply7.info.abiDefinition; web3.eth.contract(abi).new({from: "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", data: code}, function (err, contract) { if (!err && contract.address) console.log("deployed on:", contract.address); } ); deployed on: 0x0ab60714033847ad7f0677cc7514db48313976e2- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
裝載一個部署的合約,發(fā)送交易:
var source = 'contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input var compiled = web3.eth.compile.solidity(source); var Multiply7 = web3.eth.contract(compiled.Multiply7.info.abiDefinition); var multi = Multiply7.at("0x0ab60714033847ad7f0677cc7514db48313976e2") multi.multiply.sendTransaction(6, {from: "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"})- 1
- 2
- 3
- 4
- 5
注冊一個回調(diào),打印事件創(chuàng)建日志的時候會被調(diào)用。
multi.Print(function(err, data) { console.log(JSON.stringify(data)) }) {"address":"0x0ab60714033847ad7f0677cc7514db48313976e2","args": {"":"21"},"blockHash":"0x259c7dc0- 1
- 2
在web3.js維基頁面可查看更多信息。
控制臺
geth控制臺提供命令行界面和Javascript執(zhí)行時間。它可以連接到本地或遠程的geth或eth節(jié)點。它會裝載用戶能使用的web3.js庫,從而方便用戶從控制臺通過web3.js部署智能合約,并和智能合約互動。實際上Web3.js章節(jié)的例子可以被復制進控制臺并且調(diào)用。
查看合約與交易
有幾個可用的在線區(qū)塊鏈瀏覽器,能讓你查詢以太坊區(qū)塊鏈,它們分別是:
- EtherChain
- EtherCamp
- EtherScan
其他可查看節(jié)點或交易的資源
- EtherNodes :節(jié)點的地理分配,由客戶端區(qū)分。
- EtherListen:實時以太坊交易可視器和可聽器。
智能合約案例實戰(zhàn)
以太坊是區(qū)塊鏈開發(fā)領域最好的編程平臺,而truffle是以太坊(Ethereum)最受歡迎的一個開發(fā)框架,這也是介紹truffle的原因。實戰(zhàn)是最重要的事情,這篇文章不講原理,只搭建環(huán)境,運行第一個區(qū)塊鏈程序(Dapp)。
1. 安裝truffle
安裝truffle的命令如下:
$ npm install -g truffle- 1
2. 依賴環(huán)境
可用的系統(tǒng)包括:Windows、Linux和Mac OS X,推薦Mac OS X,不建議使用Windows,會碰到各種各樣的問題,很可能導致放棄。首先,訪問https://nodejs.org?官方網(wǎng)站下載安裝NodeJS。
此外,需要安裝Ethereum客戶端,來支持JSON RPC API調(diào)用。
至于開發(fā)環(huán)境,推薦使用EthereumJS TestRPC,地址為:?https://github.com/ethereumjs/testrpc。
安裝命令如下:
$ npm install -g ethereumjs-testrpc- 1
3. 新建第一個項目
通過以下命令新建一個項目:
$ mkdir zhaoxi $ cd zhaoxi $ truffle init- 1
- 2
- 3
默認會生成一個MetaCoin的demo,可以從這個demo中學習truffle的架構。
項目的目錄結(jié)構如圖5-3所示。
?
圖5-3 項目的目錄結(jié)構
項目所有文件的目錄如圖5-4所示。
?
圖5-4 項目文件目錄目錄結(jié)構
現(xiàn)在,通過以下命令編譯項目。
$ truffle compile- 1
圖5-5是運行以上命令后的結(jié)果。?
圖5-5 Truffle compile執(zhí)行結(jié)果圖
下面介紹部署項目的方式。
部署之前先啟動TestRPC,命令如下:
$ testrpc $ truffle deploy(在Truffle 2.0以上版本中,命令變成了:truffle migrate)- 1
- 2
圖5-6是運行truffle deploy后的結(jié)果。
?
圖5-6 truffle deploy執(zhí)行結(jié)果圖
$ truffle migrate migrate的執(zhí)行結(jié)果見圖5-7。
?
圖5-7 truffle migrate migrate執(zhí)行結(jié)果圖
現(xiàn)在,可以啟動服務了,命令如下:
$ truffle serve- 1
圖5-8是truffle serve執(zhí)行結(jié)果圖
圖5-8 truffle serve執(zhí)行結(jié)果圖
啟動服務后,可以在瀏覽器訪問項目了,地址是:http://localhost:8080/?,網(wǎng)頁界面如圖5-9所示。
?
圖5-9智能合約運行界面
好了,第一個區(qū)塊鏈程序跑起來了,后面可以不斷地實踐深入學習了。
http://blog.csdn.net/blockchain_lemon/article/details/77983873總結(jié)
以上是生活随笔為你收集整理的动手编写一个以太坊智能合约的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【附代码】如何在私有链上编写、部署与以太
- 下一篇: 在geth客户端调用已部署的智能合约