1. 程式人生 > >第一行代碼:以太坊(3)-使用更多的工具編寫和測試智能合約

第一行代碼:以太坊(3)-使用更多的工具編寫和測試智能合約

ppr 3.2 mage ethereum output con 來看 led ron

《第一行代碼:以太坊》開始連載了

在上文中已經使用了Remix環境運行和測試了本書編寫的第一個智能合約程序,不過編寫和測試智能合約的測試方式很多,例如,在testrpc環境測試;在Intellij IDEA集成開發環境中用Solidity語言編寫智能合約;在純Web環境中測試智能合約;使用AJAX方式測試智能合約等。本文將詳細介紹這些用於編寫和測試智能合約的方法。

1.安裝本地remix環境(Windows、Mac OS X和Linux)

在本節使用Remix環境運行和測試了Calc智能合約,不過使用的是在線Remix環境。由於某些原因(如沒有網絡,或網絡速度很慢),我們希望使用本地的Remix環境運行和測試智能合約,這就要就將Remix環境安裝在本地。Remix是跨平臺的,所以本節介紹的安裝方法同時適用於Windows、Mac OS X和Linux。

不管是在什麽操作系統下安裝Remix,都必須安裝Node.js,讀者可以到https://nodejs.org下載Node.js的最新版直接安裝即可。

安裝完Node.js後,需要使用git命令下載Remix的代碼庫(browser-solidity),命令行如下:

git clone https://github.com/ethereum/browser-solidity

在Mac OS X和Linux下,一般會集成git命令,但在Windows下,默認是沒有git命令的,所以需要到下面的頁面下載Windows版的git工具,下載完後直接安裝即可。

https://git-scm.com/download/win

使用git命令下載完Remix的代碼庫後,使用cd命令進入browser-solidity目錄,該目錄在下載Remix代碼庫的過程中自動在當前目錄中創建。

在browser-solidity目錄中執行下面的命令安裝browser-solidity。

npm install

安裝browser-solidity的過程比較漫長,讀者要耐心等待。下圖是在Windows下安裝browser-solidity環境的效果。

技術分享圖片

如果成功安裝了browser-solidity,可以使用下面的命令啟動Remix服務。

npm start

下圖是Mac OS X下啟動Remix服務後的輸出信息,Windows和Linux會輸出類似的信息。

技術分享圖片

Remix服務默認的端口號是8080,如果在瀏覽器地址欄中輸入下面的Url,就可以使用本地的Remix環境編寫和測試智能合約。

http://localhost:8080

2. 安裝testrpc

testrpc與geth不同,geth是真正的以太坊環境,而testrpc是在本地模擬的一個以太坊環境,主要用於開發調試。當智能合約使用testrpc調試通過後,可以部署在真正的以太坊環境中。
安裝testrpc仍然需要Node.js環境,所以讀者應該事先安裝好Node.js,然後使用下面的命令安裝testrpc。
npm install -g ethereumjs-testrpc

安裝好testrpc後,可以使用testrpc命令運行testrpc。下圖是Mac OS X下啟動testrpc服務的效果。

技術分享圖片

下圖是Windows下啟動testrpc服務的效果。

技術分享圖片

我們可以看到,不管是在哪一個平臺上啟動testrpc服務,都會自動生成10個賬號(Accounts)和10個私鑰(Private Keys)。這些賬號和私鑰都是用於測試的,而且每一個賬號擁有的以太幣幾乎是無限大的,因此,不用擔心進行某些操作後沒有以太幣可用。

testrpc本身是一個服務,默認的端口號是8545,這個端口號是用於像web3.js、web3.py一樣的程序庫連接以太坊節點的,testrpc其實也相當於一個用於測試的以太坊節點。

3.使用testrpc測試智能合約

本節會將智能合約部署到testrpc服務上,然後使用web3.js連接testrpc服務,並調用智能合約中的函數。具體的操作步驟如下:

(1)編寫智能合約

啟動本地的Remix環境,然後在Remix環境中輸入下面的智能合約代碼。

本例編寫了一個名為Factorial的智能合約程序,在該智能合約中有一個factorial函數,用於計算n的階乘。

pragma solidity ^0.4.0;
contract Factorial 
{
    /*  計算n的階乘  */
   function factorial(uint n) returns (uint)
   {
        if (n == 0 || n == 1)
            return 1;
        else
            return n * factorial(n - 1);
    }
}

這個智能合約用於計算n的階乘。

(2)將智能合約部署在testrpc節點上

在Remix環境的右側進入“Run”頁面,並在“Environment”列表中選擇“Web3 Provider”,如下圖所示。

技術分享圖片

在Web3 Provider環境下,Remix可以將智能合約直接部署到testrpc服務上。進入Web3 Provider之前,會彈出一個對話框,詢問是否連接以太坊節點,單擊“OK”按鈕,會彈出如下圖所示的對話框。在該對話框中有一個文本框,默認值是http://localhost:8545,如果要連接本地的testrpc節點或以太坊節點,直接單擊“OK”按鈕即可。如果testrpc節點已經啟動,那麽Remix本地環境會成功連接到testrpc節點上。

技術分享圖片

單擊“Run”頁面的“Deploy”按鈕,會將Factorial智能合約部署到testrpc上。部署成功後,會在“Run”頁面的下方出現“factorial”按鈕,如下圖所示。在按鈕右側的文本框輸入要計算階乘的n的值,然後點擊該按鈕即可在以太坊測試環境(testrpc)下執行factorial函數,不過在日誌區域點擊“Details”按鈕後,並沒有看到factorial函數的輸出結果,這是因為factoria函數是直接在以太坊網絡中運行的,所有的數據都存在於以太坊網絡中,並不會直接將數據返回給以太坊客戶端。

在“factorial”按鈕的上方是Factorial智能合約的地址,如果在客戶端要訪問這個智能合約,需要使用這個地址。

技術分享圖片

(3)安裝Solidity編譯器

Solidity編譯器是用於編譯Solidity源代碼文件(.sol文件)的,可以將Solidity源代碼文件編譯成多種目標文件。使用下面的命令行可以安裝Solidity編譯器。

npm install -g solc

(4)編譯Solidity源代碼文件

在當前目錄創建一個Factorial.sol文件,然後將例3.2中的代碼復制到Factorial.sol文件中。接下來會使用上一步安裝的Solidity編譯器對Factorial.sol文件進行編譯。要註意,盡管安裝的是solc,但編譯器命令行工具是solcjs。這個工具可以將Solidity源代碼文件編譯成多種目標文件,對於本例來說,只需要abi文件即可,該文件是智能合約的接口文件。也就是說,使用Web3.js調用智能合約,需要使用abi文件才能調用智能合約中函數。

使用下面的命令可以將Factorial.sol文件編譯生成abi文件。其中--abi是命令行參數,表示生成的目標文件類型是abi。

solcjs --abi Factorial.sol

執行完上面的命令後,會在當前目錄生成一個Factorial_sol_Factorial.abi文件,該文件就是Factorial.sol對應的abi文件。

(5)安裝Web3.js

在使用Web3.js之前必須安裝Web3.js,Web3.js是Node.js的一個模塊,所以需要使用下面的命令安裝。

npm install web3

使用上面的命令會安裝web3的最新版,如果讀者使用web3最新版不太習慣,可以使用下面的命令安裝指定版本。

npm install [email protected]

(6)用Web3.js連接testrpc節點

現在執行node命令進入Node.js的REPL環境(命令行交互環境),然後在Node的REPL環境執行下面的命令。要註意,在執行這些命令之前,要先啟動testrpc節點,並且利用Remix環境將例3.2中的智能合約部署到testrpc節點上。

> var Web3 = require("web3");
> var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); 
undefined
> var eth = web3.eth
undefined
> var abi = JSON.parse(fs.readFileSync("Factorial_sol_Factorial.abi").toString());
undefined
> var contract = eth.contract(abi);
undefined
> var instance = contract.at(‘0x371f45db1a077bbcbeb50d2a21bc85e4e18c1f1f‘)
undefined
> instance.factorial.call(3)
{ [String: ‘6‘] s: 1, e: 0, c: [ 6 ] }
> instance.factorial(10, {from:eth.accounts[0]})
‘0xbb291fec53c4c5aefc87e2d7e8475c4abd4c54d03ef06e857665a10db0c1a3ff‘

上面的內容中“>”表示命令提示符,後面是輸入的代碼,下面是輸出值,undefined是Node輸出的,表示當前語句什麽也沒有輸出(定義變量的JavaScript語句不會輸出任何東西)。從這幾行代碼可以了解通過Web3.js連接testrpc節點的核心步驟(與連接以太坊節點的步驟相同)如下。

(1)導入web3模塊,代碼如下:

var Web3 = require("web3");

(2)創建Web3類的實例,並通過該類的構造方法參數指定testrpc節點的Url(IP和端口號),代碼如下:

var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

(3)讀取Factorial_sol_Factorial.abi文件的內容,並將文件的內容轉換為JSON對象,代碼如下:

var abi = JSON.parse(fs.readFileSync("Factorial_sol_Factorial.abi").toString());

(4)使用abi創建智能合約對象,代碼如下:

var contract = eth.contract(abi);

(5)將智能合約與testrpc中部署的智能合約綁定,代碼如下:

var instance = contract.at(‘0x371f45db1a077bbcbeb50d2a21bc85e4e18c1f1f‘)

其中at方法的參數值就是圖3-12所示的factorial方法上方的智能合約地址,也是以太坊中唯一能定位特定智能合約的標識。點擊地址右側的按鈕可以將該地址復制到剪貼板上。

(6)本地調用智能合約中的factorial函數,代碼如下:

instance.factorial.call(3)

本地調用智能合約,不會對以太坊網絡造成任何影響。本地調用智能合約中的函數會直接輸出函數的返回值,如果函數返回的是數值類型,會以BigNumber類型返回,這是一個JavaScript擴展,允許JavaScript操作任何的數值,BigNumber類型會在本書後面的章節詳細講解。

執行上面的代碼,會輸出如下內容,很明顯,3的階乘是6。

[String: ‘6‘] s: 1, e: 0, c: [ 6 ] }

(7)在以太坊網絡上調用智能合約,代碼如下:

instance.factorial(10, {from:eth.accounts[0]})

在以太坊網絡上調用智能合約的函數不會在客戶端直接得到函數的返回值,而會得到一個如下的交易地址。

0xbb291fec53c4c5aefc87e2d7e8475c4abd4c54d03ef06e857665a10db0c1a3ff

因為任何在以太坊網絡上進行的操作都被視作一次交易,既然有交易,就需要有交易地址,可以通過相應的API根據交易地址查詢交易情況。在以太坊網絡中有很多類型的地址,如礦工地址、智能合約地址、交易地址等。每一類地址都由若幹位十六進制數組成,但不同類型地址的位數可能不同。

在真正的以太坊網絡中,任何交易都需要礦工挖礦進行處理,同時每一筆交易會給與完成工作的礦工一定的獎勵,也就是礦工的挖礦所得。不過在testrpc節點中由於是模擬以太坊網絡和挖礦,所以不需要挖礦,直接會執行以太坊網絡上的操作,因此,如果客戶端連接的是testrpc節點,發起交易後,會立刻執行。另外,在以太坊網絡上調用智能合約,需要指定是誰(一個表示用戶的地址)發起的交易,因為在實際的以太坊網絡中,要從這個地址扣除相應的以太幣給礦工。本例使用eth.accounts[0]指定的地址。其中eth.accounts可以獲取testrpc節點啟動時生成的10個測試賬戶的地址,eth.accounts[0]就是第一個測試賬戶的地址。

從本節的案例來看,客戶端訪問以太坊網絡的步驟就是連接以太坊節點和發起交易兩步,當然,以太坊網絡要處理交易,就需要礦工挖礦(爭奪處理交易的權利,同時獲得回報)了。
3.2.4 Intellij IDEA Solidity插件
不管是Remix,還是Windows記事本,或是其他的文本編輯器,都不會用於開發復雜的智能合約,一是界面並不友好,二是也沒有必要的智能提示功能,而且如果智能合約的代碼量很大,可能會造成Remix死掉。所以前面介紹的工具只是為了測試智能合約的,並不是用來開發實際的智能合約項目的。如果要開發大型的智能合約項目,通常會使用本地的IDE,如Intellij IDEA。這款IDE最初是為開發Java項目推出的,不過由於Intellij IDEA支持第三方插件,所以從理論上,Intellij IDEA可以支持任何的編程語言。

可能很多讀者對Intellij IDEA並不熟悉,實際上,這款IDE就是大名鼎鼎的JetBrains公司推出的,如果不了解JetBrains以及它的產品,那麽對Android和Google推出的Android開發工具Android Studio一定不陌生,Android Studio就是在Intellij IDEA社區版的基礎上開發的。而且JetBrains公司還開發出了大名鼎鼎的Kotlin語言,現在已經成為開發Android App的官方推薦編程語言。

讀者可以到下面的頁面下載Intellij IDEA的免費版本(社區版)。
https://www.jetbrains.com/idea/download

Solidity語言同樣提供了Intellij IDEA插件,建議使用在線安裝方式。如果是Mac OSX版本的Intellij IDEA,單擊左上角的IntelliJ IDEA菜單的Preferences菜單項,如下圖所示。

技術分享圖片

如果是Windows版的Intellij IDEA,需要單擊File菜單的Settings菜單項。單擊該菜單項後,會彈出偏好(設置)窗口,如下圖所示。

技術分享圖片

Preferences窗口中間的列表列出了Intellij IDEA已經安裝的所有插件。單擊窗口下方的Browse repositories按鈕,會彈出Browse Repositories窗口,在窗口左上角的文本框中輸入Solidity,會在線搜索相關的插件,如下圖所示,如果找到,會在右側顯示當前選中插件的詳細信息,如果沒有安裝該插件,會在右側顯示install按鈕,單擊install按鈕即可安裝插件。

技術分享圖片

安裝完插件後,在Intellij IDEA中創建一個Java或其他工程(Solidity插件並沒有提供Solidity工程),然後在工程右鍵菜單中單擊new菜單項,會顯示如下圖所示的子菜單。在子菜單上會找到一個Smart contract菜單項。

技術分享圖片

單擊Smart contract菜單項,會顯示如下圖所示的New Solidity File窗口,從Kind列表框可以選擇Solidity文件類型(Smart contract或Solidity library),本例選擇Smart contract。

技術分享圖片

在Name文本框中輸入Solidity文件名後,單擊OK按鈕創建Solidity文件。然後在Intellij IDEA左側的工程樹中雙擊剛才創建的Solidity文件,會在右側顯示代碼編輯區域,並輸入如下圖所示的Solidity代碼。

技術分享圖片

盡管可以在Intellij IDEA中編寫Solidity代碼,也支持代碼高亮顯示和智能提示,但編譯Solidity源代碼文件仍然需要切換到終端,使用solcjs命令編譯,很麻煩,所以在下一節會教大家如何將solcjs命令集成進Intellij IDEA,無需切換到終端就可以編譯Solidity源代碼文件。

5.將Solidity編譯工具與Intellij IDEA集成

Intellij IDEA有一個擴展工具功能,可以將可執行程序與Intellij IDEA集成,也就是說,不用切換到終端,就可以執行這些程序。

現在打開偏好窗口(Windows中是設置窗口),在左側區域找到Tools > External Tools節點,單擊該節點後,會在右側顯示當前集成的擴展工具列表,默認是空。然後單擊該區域下方“+”按鈕,會彈出一個Create Tool窗口,在該窗口需要填寫如下4個字段。

  • Name:solidity
  • Program:solcjs
  • Parameters:--abi --bin $FileName$ -o $OutputPath$
  • Working directory:$FileDir$

填寫後的效果如下圖所示,最後單擊OK按鈕創建擴展工具。

技術分享圖片

創建擴展工具應該了解如下幾點。

  • Name只是用於顯示的擴展工具名字,可以任意指定,甚至可以與已經存在的擴展工具重名。
  • Program指定的solcjs命令要在終端可以直接執行,否則會出現無法執行該命令的錯誤。所以在創建擴展工具之前,先要使用npm install -g solc命令安裝solcjs。
  • Parameters表示solcjs的命令行參數,其中--abi表示將Solidity源代碼文件編譯成接口文件(.abi文件),--bin表示將Solidity源代碼文件編譯成二進制文件(.bin文件),用於發布智能合約。盡管這兩類文件並不是在任何時候都需要,但為了省事,幹脆將它們一起生成吧。
  • -o表示生成的目標文件(.abi和.bin文件)的路徑。
  • $FileName$、$OutputPath$和$FileDir$都是Intellij IDEA提供的環境變量,$FileName$表示當前選擇的文件名,$OutputPath$表示文件的輸出目錄,$FileDir$表示當前選擇文件所在的目錄。

如果是在Mac OS X下,$OutputPath$指向工程目錄的out子目錄,與工程相關的生成文件都放在這個目錄中,目錄結構與src目錄相同。圖3-20是out目錄的結構,註意,讀者機器上的目錄結構可能有差異,但.abi和.bin文件都在out/production目錄或其子目錄中。

技術分享圖片

如果在Windows下,並不能執行solcjs文件,因為這個文件是在Mac OS X和Linux使用的,Windows下是solcjs.cmd,所以要將Program改成solcjs.cmd。而Windows版的Intellij IDEA並沒有內置的$OutputPath$變量,所以可以將這個變量改成其他的值,如$FileDir$,這樣以來,就會在.sol文件同一個目錄生成.abi和.bin文件。所以Windows版的Intellij IDEA需要按下面的內容設置擴展工具。

  • Name:solidity
  • Program:solcjs.cmd
  • Parameters:--abi --bin $FileName$ -o $FileDir$
  • Working directory:$FileDir$

按前面的方式設置完擴展工具後,選中一個.sol文件(假設文件名是MyCalc.sol,裏面的智能合約名是Calc),在Intellij IDEA的Tools > External Tools 菜單中出現了一個solidity菜單項,如圖3-21所示,單擊該菜單項,就會調用solcjs編譯MyCalc.sol文件,並在相應的目錄生成MyCalc_sol_Calc.abi和MyCalc_sol_Calc.bin文件。

技術分享圖片

其實在工程的右鍵菜單中也可以找到External Tools > solidity菜單項,如下圖所示,單擊該菜單項,效果是一樣的。

技術分享圖片

第一行代碼:以太坊(3)-使用更多的工具編寫和測試智能合約