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

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

在上文中已經使用了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),命令列如下:

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

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

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

npm install

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

image.png

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

npm start

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

image.png

Remix服務預設的埠號是8080,如果在瀏覽器位址列中輸入下面的Url,就可以使用本地的Remix環境編寫和測試智慧合約。

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服務的效果。

image.png

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

image.png

我們可以看到,不管是在哪一個平臺上啟動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”,如下圖所示。

image.png

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

image.png

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

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

image.png

(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]20.6

(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的官方推薦程式語言。

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

image.png

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

image.png

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

image.png

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

image.png

單擊Smart contract選單項,會顯示如下圖所示的New Solidity File視窗,從Kind列表框可以選擇Solidity檔案型別(Smart contract或Solidity library),本例選擇Smart contract。

image.png

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

image.png

儘管可以在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按鈕建立擴充套件工具。

image.png

建立擴充套件工具應該瞭解如下幾點。

  • 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目錄或其子目錄中。

image.png

如果在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檔案。

image.png

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

image.png