這是區(qū)塊鏈專家K. C. Tam關(guān)于智能合約開發(fā)工具和環(huán)境的詳細分析與總結(jié)。通過此文,相信我們將獲得關(guān)于這些工具的一些深入理解,對使用Solidity開展智能合約將由非常大的幫助。
首先簡要介紹本文選擇的智能合約。然后在四種環(huán)境中部署此合約。以下是在這里展示的四種環(huán)境的簡要總結(jié)。

下圖為合約編譯和部署的簡化流程

智能合約范例:收益共享
此應(yīng)用程序摘自A. Bahga和V. Madisetti 的書籍“ 區(qū)塊鏈與應(yīng)用實踐方法 ” 第4章:以太坊帳戶中的智能合約。它已被修改以符合最新版本的Solidity的一些要求。
這個應(yīng)用程序被稱為收益共享。簡而言之,部署合約時會給出地址列表。任何人都可以發(fā)送一定數(shù)量的資金(這里使用ethers或ethers的面額),這筆資金平均分配到清單上的地址。
一個相當(dāng)簡單的Solidity合約如下:
pragma solidity ^0.4.8;contract RevenueSharing {address public creator;mapping(uint => address) public shareholders;uint public numShareholders;event Disburse(uint _amount, uint _numShareholders);function RevenueSharing(address[] addresses) {creator = msg.sender;numShareholders = addresses.length;for (uint i=0; i< addresses.length; i++) {shareholders[i] = addresses[i];}}function shareRevenue() payable returns (bool success) {uint amount = msg.value / numShareholders;for (uint i=0; i
關(guān)于這份智能合約的簡單說明:
該合約名為RevenueSharing。
函數(shù)RevenueSharing()與合約本身具有相同的名稱。它是構(gòu)造函數(shù),并且只在合約部署時調(diào)用一次。我們在這份合約中看到提供了一系列地址,這個地址數(shù)組存儲在另一個名為股東的數(shù)組中。
函數(shù)shareRevenue()是此聯(lián)系人中唯一的主要功能。當(dāng)用一定數(shù)量的ethers(以msg.value)執(zhí)行這個函數(shù)時,金額被分成股東數(shù)量(股東數(shù)量),股東陣列中的每個地址將得到該部分。我們將在演示中執(zhí)行此功能。
函數(shù)kill()用于刪除合約。我們不會在演示中使用這個功能。
請注意,所有變量都是使用public定義的。這有助于我們觀察合約中的更多細節(jié)。在現(xiàn)實生活中,由于安全考慮,我們在公開變量或功能時應(yīng)該小心。
Remix概述
Remix是一套工具,用于與以太坊區(qū)塊鏈交互以調(diào)試交易(直接引用此處)。有一個IDE版本(Remix IDE)和一個在線版本(如下圖)。

Remix中有很多工具,但以下工具符合我們的興趣,
? Solidity編譯器。這會產(chǎn)生很多有用的信息,我們將在另一個環(huán)境中使用
? 運行環(huán)境。Remix提供了三種:
? Injected Web3:Mist或MetaMask
? Web3 Provider:通過ipc到本地主機
? JavaScript VM:一個模擬環(huán)境
在運行時環(huán)境中,我們使用JavaScript VM。在JavaScript VM中,Remix附帶五個以太坊帳戶,每個帳戶都存放100個ethers。這足以測試我們的智能合約。另外,由于它是自動完成的,因此不需要挖掘。
您可以從任何瀏覽器輕松訪問Remix(網(wǎng)址:http://remix.ethereuem.org)。以上是Remix的截圖。
屏幕分為幾個模塊區(qū)域。
? 智能合約區(qū):我們在此粘貼合約的合約代碼。
? 編譯區(qū)和運行區(qū):在編譯標(biāo)簽中,此處顯示任何編譯錯誤或警告。在運行標(biāo)簽中,我們部署合約并執(zhí)行合約功能。
? 事務(wù)日志區(qū):此處可以觀察所有事務(wù)細節(jié)。
編譯合約
我們將智能合約代碼粘貼到Remix中。

我們注意到代碼是自動編譯的,并且有一些警告。由于它們不是嚴重錯誤,因此我們可以繼續(xù)前進。

如果我們點擊詳細信息,我們會看到很多關(guān)于此合約的信息。其中他們是,
? 字節(jié)碼
? ABI
? Web3部署
在另一個環(huán)境中部署此合約時需要使用它們,我們稍后會回顧這一點。
由于我們在編譯后看到?jīng)]有錯誤,所以我們可以將此合約運行到Remix JavaScript環(huán)境。
部署合約
1.運行(Run)一覽
下圖為運行菜單界面一覽。
從環(huán)境中選擇JavaScript VM。
選擇JavaScript虛擬機后,我們會在帳戶字段中看到一些帳戶。
如前所述,每一種都是預(yù)先充入100 ethers用于測試。因為我們稍后會使用這些帳戶,所以我們可以先復(fù)制它們。
Gas limit是指明我們可以在任何交易中需要多少GAS。當(dāng)我們處于測試環(huán)境中時,我們并不擔(dān)心這一點。我已經(jīng)嘗試了一些大的合約部署,默認設(shè)置Gas limit不足(anyway,它可以在需要時調(diào)整到任何值)。
Value 是我們在合約部署和執(zhí)行功能期間發(fā)送ethers的數(shù)量。在我們的案例中,我們沒有在合約部署中放置任何Value,但是在執(zhí)行該功能時放置了一些ethers。請參閱下面的更多細節(jié)。
2.部署合約
現(xiàn)在我們看到已經(jīng)選擇了RevenueSharing合約(我們的代碼中只有一個合約)。我們將使用“創(chuàng)建”按鈕將此合約部署到JavaScript VM上。
需要的東西,如輸入?yún)^(qū)域中所暗示的:“地址[]地址”,當(dāng)部署合約時。記得這份合約要求將地址列表作為共享目標(biāo)嗎?為了演示目的,我們將使用上面列出的第3,第4和第5個地址作為地址列表。因此,將其粘貼到創(chuàng)建按鈕旁邊:
現(xiàn)在確保我們已經(jīng)選擇:
? 環(huán)境:JavaScript VM
? 帳戶:部署此合約時的第一個帳戶(以0xca3 ...開頭)
? 將地址數(shù)組粘貼到Create按鈕旁邊
按下Create,我們將看到以下情況。
3.合約部署后
該合約現(xiàn)在部署在JavaScript VM(內(nèi)存)中,并顯示合約地址(0x692 ...)。我們不在演示中使用此地址。在需要時可以在其他情況下提及該地址。
此外,我們看到標(biāo)記為“public”的變量現(xiàn)在顯示,它們是,
? shareholders
? numShareholders
? creator
我們在本合約中定義的兩項功能,
? shareRevenue()
? Kill()
在此之前,我們觀察到帳戶余額減少了少量的ethers。差額(417,626 weis,1 wei = 10-18 ether)是部署此合約的成本。在現(xiàn)實生活中,這是在您部署合約時從您的帳戶中扣除的真正的ethers。
與已部署的合約進行交互
1.檢查變量
我們可以首先通過按下變量按鈕來檢查變量。在這里我們檢查numShareholder和creator。對股東(shareholders)來說,因為它是一個數(shù)組,所以我們需要指定一個索引(0,1或2),對應(yīng)于我們在部署(創(chuàng)建)合約時放置的地址。
所有的變量都是我們所期望的。
2.執(zhí)行shareRevenue()函數(shù)
現(xiàn)在我們執(zhí)行shareRevenue()。在執(zhí)行此功能時,我們使用第一個帳戶來存放30個ethers(這僅用于此功能,在很多情況下這不是必需的)。根據(jù)契約邏輯,30個ethers將被分配到賬戶列表中,即我們賬戶列表中的第3,第4和第5個賬戶。截至目前,他們每個人的余額仍然是100個ethers。
我們使用相同的地方來執(zhí)行該功能。在這里我們確保,
? 在帳戶字段中,選擇第一個帳戶(以0xca3 ...開頭)
? 在價值中放置30個ethers

然后按shareRevenue。
函數(shù)執(zhí)行后,我們檢查每個帳戶的余額,并根據(jù)我們的設(shè)計查看它是否被執(zhí)行。

首先,我們看到30個ethers被從第1個帳戶中扣除,并且名單上的所有三個帳戶現(xiàn)在都有110個ethers。因此,從第一個賬戶中扣除的30個賬戶現(xiàn)在分配到這三個賬戶中。該部分按照合約進行完善。
另外,如果我們仔細檢查第一個帳戶的余額,則會扣除一些額外的ethers。差額為47,776 wei,這是此次交易的成本。每筆交易,功能的執(zhí)行或合約的部署都需要花費一定數(shù)量的ethers。
事務(wù)日志
我們在測試過程中沒有碰到事務(wù)日志,但是一切都保存在日志中,甚至是變量的查詢。讓我們來看看兩個選定日志的細節(jié)。
1.合約部署

我們可以看到誰已經(jīng)部署了此合約,合約地址以及部署它所需的交易成本。
2.執(zhí)行shareRevenue()函數(shù)
我們再次將其視為交易成本。在shareRevenue()中,有一個返回 boolean值,我們看到“ecoded out”有一個“True”返回。此外,我們有一個成功發(fā)行的動作(我們在“LOG”中可以看到它)
總結(jié):
這就是Remix如何幫助測試我們開發(fā)的代碼。它具有非常方便的功能和直觀的用戶界面。在下一篇文章中,我們將使用另一個環(huán)境testrpc來處理同一個合約并了解它是如何工作的。
用于智能合約開發(fā)的最佳工具第2部分:關(guān)于TestRPC的Web3
概述
TestRPC是以太坊區(qū)塊鏈的模擬,它帶有10個預(yù)定義的以太坊帳戶并支持助記符(即,可以使用相同的一組助記符生成相同的帳戶集合)。它沒有帶有用戶界面作為Remix,我們需要節(jié)點控制臺加上web3庫來與這個區(qū)塊鏈進行交互。
前期準(zhǔn)備
演示通過命令行或終端完成。使用支持屏幕分割的終端工具。我在我的Mac上使用iTerm2。
安裝節(jié)點和npm:請參考此處在您的平臺上進行安裝。
備注:我最近發(fā)現(xiàn)用npm安裝web3時,安裝了1.0.0 beta版本,之前使用的命令(基于0.20.4)不起作用。因此我們改為指定web3的版本。
以下所有命令都在版本0.20.0中。
打開一個終端并將屏幕分成兩部分。左側(cè)是節(jié)點控制臺,我們將在那里工作大部分時間。右側(cè)是我們運行TestRPC的地方。
啟動TestRPC
在右側(cè),啟動TestRPC
這里我們可以看到:
? TestRPC是一個節(jié)點應(yīng)用程序,模擬內(nèi)存中的以太坊區(qū)塊鏈。
? 10個帳戶是預(yù)先定義的。
? 這些帳戶是通過助記符生成的,每次啟動TestRPC時都不相同。為了保持同一組帳戶,我們可以在運行TestRPC時使用上面顯示的助記符作為參數(shù)。
? 此外,RPC在localhost:8545上打開。Web3正在通過此訪問區(qū)塊鏈。
我們不會再觸及這一部分,假設(shè)在以太坊區(qū)塊鏈中一切正常?,F(xiàn)在我們更關(guān)注節(jié)點控制臺(左側(cè))。在測試過程中,我們不斷看到命令和日志發(fā)布到TestRPC端顯示的區(qū)塊鏈。
Web3對象
我們需要指示節(jié)點控制臺,我們正在使用web3并指向區(qū)塊鏈web3進行連接。
這正是TestRPC中創(chuàng)建的帳戶。
顯示余額的方便功能
我找到了一個方便的功能(鏈接),可以顯示所有帳戶的余額。
只需將此功能復(fù)制粘貼到節(jié)點控制臺即可。現(xiàn)在我們可以隨時調(diào)用函數(shù)checkAllBalances(),并且它會以ether的形式顯示所有賬戶的余額。請注意,在我們退出節(jié)點控制臺后,此功能將消失,但我們可以隨時將其添加回去。
部署合約
1.編譯合約
現(xiàn)在一切都準(zhǔn)備好了。我們可以部署我們的收益分享合約。
我們需要重新打開Remix,因為我們正在利用Remix上的編譯器。在我們將合約代碼粘貼到Remix后,它會自動編譯。這里我們使用的是合約部署的結(jié)果。
單擊編譯標(biāo)簽上的詳細信息,那里有很多信息。
在這些信息中,有三個是我們感興趣的:字節(jié)碼,ABI和Web3Deploy
字節(jié)碼是編譯后的合約的二進制版本,以及要在以太坊虛擬機(EVM)中運行的指令集,ABI(應(yīng)用程序二進制接口)是我們與合約字節(jié)碼交互的接口。
Remix足以準(zhǔn)備Web3Deploy中的代碼,其中字節(jié)碼和ABI已包含在命令中。因此我們只需要使用Web3Deploy部分。
2.部署合約
首先,根據(jù)合約的要求,我們需要定義一個目標(biāo)賬戶列表。為了演示目的,使用從第二個帳戶開始的三個帳戶,即從eth.accounts [1]到eth.accounts [3]。

然后我們按照Web3Deploy的建議。
根據(jù)ABI創(chuàng)建一個收入分成合約類。只需從Web3Deploy復(fù)制該行即可。
node console> var revenuesharingContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"creator","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"numShareholders","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"shareholders","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"shareRevenue","outputs":[{"name":"success","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_amount","type":"uint256"},{"indexed":false,"name":"_numShareholders","type":"uint256"}],"name":"Disburse","type":"event"}]);
現(xiàn)在用字節(jié)碼來部署契約,加上必要的信息。再次,我們可以從Web3Deploy復(fù)制該行。已部署的合約是一個名為收益共享的對象。
node console> var revenuesharing = revenuesharingContract.new(addresses,{from: web3.eth.accounts[0], data: '0x6060604052341561000f57600080fd5b60405161049d38038061049d833981016040528080518201919050506000336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508151600281905550600090505b81518110156100f957818181518110151561009157fe5b906020019060200201516001600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808060010191505061007a565b50506103938061010a6000396000f30060606040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806302d05d3f1461007257806341c0e1b5146100c757806368eca613146100dc578063ab377daa14610105578063e579a0bd14610168575b600080fd5b341561007d57600080fd5b61008561018a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100d257600080fd5b6100da6101af565b005b34156100e757600080fd5b6100ef610240565b6040518082815260200191505060405180910390f35b341561011057600080fd5b6101266004808035906020019091905050610246565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610170610279565b604051808215151515815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023e576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b60025481565b60016020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060006002543481151561028b57fe5b049150600090505b60025481101561031d576001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050151561031057600080fd5b8080600101915050610293565b7f9c26340b8d01b4e039192edfd25f4a56ed070d45afe866b8685658b1ed3cd74d34600254604051808381526020018281526020019250505060405180910390a1600192505050905600a165627a7a72305820f0e717ba935e00c43896cc9266a85af91a519061c044503be0a52b93f721d1610029', gas: '4700000'}, function (e, contract){console.log(e, contract);if (typeof contract.address !== 'undefined') {console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);}})
我們將會(幾乎立即)看到合約已經(jīng)開采。
現(xiàn)在我們可以使用對象收益共享與此已部署的合約進行交互。
與已部署的合約進行交互
已部署的合約通過對象收益共享進行訪問。
1.檢查公共變量

我們可以檢查那些標(biāo)記為“公開”的變量。
2.執(zhí)行shareRevenue()函數(shù)
在我們執(zhí)行shareRevenue()函數(shù)之前,讓我們看看余額。

請注意,某些金額在已部署合約的帳戶[0]中扣除。為部署付費的ethers數(shù)量為417,626枚。當(dāng)我們在Remix中這樣做時,您可以檢查它是確切的交易成本。
現(xiàn)在我們執(zhí)行該功能。
node console> revenuesharing.shareRevenue({from: web3.eth.accounts[0], value: web3.toWei(30), gas: 4700000});
在這里,我們調(diào)用函數(shù)shareRevenue(),并指定它由賬戶[0]執(zhí)行,其中30個ethers(wei是web3中的函數(shù),用于將30個ethers轉(zhuǎn)換為wei,因為wei是命令中接受的單位)。我們還把我們允許花費的氣體(這是超過要求的方式,但執(zhí)行后我們會得到退款)。
交易執(zhí)行后,我們可以再次查看余額。

我們已經(jīng)實現(xiàn)了我們所預(yù)期的目的:從賬戶[0]中扣除30個賬戶并分配到賬戶[1]到賬戶[3](現(xiàn)在每個賬戶都有110個賬戶)。除此之外,還有一部分金額用于執(zhí)行此次交易。它仍然是47,776 weis,與我們在Remix中觀察到的相匹配。
總結(jié)
我們已經(jīng)在TestRPC上成功完成了同樣的任務(wù)。除了我們必須在節(jié)點控制臺和web3上與TestRPC中的區(qū)塊鏈進行交互之外,整體流程與Remix中的流程幾乎相同。
來源:微信公眾號:區(qū)塊鏈大小姐