利用工具加速Dapp建置和測試

藉由Testrpc和Truffle加速Ethereum Dapp的開發

快速介紹:
開發Dapp你需要有鏈(你的資料庫)和一個互動的介面(網頁、手機app等等):
1. 鏈的部分有提供各種不同 client software(Geth、Parity等等),下載後就可以對鏈進行操作,不過麻煩的是所有過程(挖礦、建立帳戶、帳戶間轉錢)都要親自動手做。
如果需要測試的智能合約非常複雜,那就會花很多時間成本在這些瑣事上面,所以這裡首先介紹的Testrpc便是用來加速這個過程的。
Testrpc提供一個快速方便的測試環境:每次執行它都會幫你創建十組帳號且裡面都有充足的ether讓你做測試、挖礦是自動的(每個transaction執行都是即時的),但缺點是它的鏈的資料是暫存在記憶體而不是寫在硬碟裡,所以Testrpc關掉之後,所有鏈的資料(帳戶餘額、合約等)都會消失,因此比較適合用來快速測試你的智能合約的功能。

2. Truffle則是自動化從合約的編譯(truffle compile)到部署合約(truffle deploy)再到app建置(truffle build)的過程。其中不含鏈的建置和操作,所以要搭配Testrpc(或用client software自建一個節點並開放rpc)才能完成。

介紹完後,開始安裝truffle和testrpc:

sudo npm install -g ethreumjs-testrpc
sudo npm install -g truffle

接著先跑起testrpc:

testrpc

不額外指定的話每次都會建立新的十組帳戶,每組裡面都會有一百ether。其中HD Wallet欄位裡的Mnemonic如果之後需要產生同樣一組帳戶,可以記下來。每次都會產生不同的帳戶(對應到不同的Mnemonic),但如果透過 -m 來指定Mnemonic,即會產生其對應的帳戶。

testrpc -m "truck sand amateur oak trigger soft helmet jump explain resource exchange tree"

Listening on localhost:8545 表示testrpc的rpc開的port為8545。
跑起testrpc後,就可以開啟一個truffle專案了。

mkdir test123
cd test123
truffle init

初始化會有一個Metacoin的範例,部署這個合約的人會有Meta幣,可以利用合約裡的sendCoin函式將Meta幣轉給其他人。
我們直接使用這個範例合約。

首先要先編譯合約:

truffle compile

sol合約會存在contracts資料夾裡,truffle要求合約名稱和檔案名稱要一致。
編譯完後的執行檔(.js檔)會放在/build/contracts資料夾裡。

接下來是部署合約:

truffle migrate

migrate會執行migration資料夾裡的js檔,js檔名開頭必須是數字(truffle會按照這順序執行,數字後的名稱可任意指定)。truffle要求第一個執行的必須是部署一個Migrations合約(如果有init,contracts資料夾裡會有一個Migrations.sol)才能開始migrate。
每個migration的js檔裡格式如下,會有一個deployer參數:

module.exports = function(deployer){
deployer.deploy(A, arg1);
deployer.deploy(B);
};

如此會照順序部署合約A及B,deploy的第一個參數為合約名稱,如果合約constructor有參數則加在其後。
當合約之間部署有相依性如B合約的constructor會用到A合約部署的地址:

module.exports = function(deployer){
deployer.deploy(A).then(function(){
return deployer.deploy(B, A.address);
});
};

或是建立一個新的A合約,然後在已部署的B合約裡呼叫setA函式並傳入新的A合約地址來更新資料:

deployer.then(function() {
// Create a new version of A
return A.new();
}).then(function(instance) {
// Set the new instance of A's address on B.
var b = B.deployed();
return b.setA(instance.address);
});

如果有library,則需要將合約與library建立link:

// Deploy library LibA, then link LibA to contract B
deployer.deploy(LibA);
deployer.link(LibA, B);

// Link LibA to many contracts
deployer.link(LibA, [B, C, D]);

或是使用autolink(),前提是library合約都必須已經部署:

// Assume A depends on a LibB and LibC
deployer.deploy([LibB, LibC]);
deployer.autolink(A);

如果有不同條鏈的話,可以使用network參數來判斷:

module.exports = function(deployer, network) {
if (network == "test_env") {
deployer.deploy(A);
deployer.deploy(B);
}
else if(network == "testnet"){
deployer.deploy(C);
}
else {
deployer.exec("../path/to/file/live_demo.js)
}
}

deployer.exec()會執行相對於migration檔的路徑的js檔,如果固定要在部署過程中做一些小測試會方便許多。
network的設定待會會做說明。

註:執行過的migration檔不會再執行,如果有更改過已經執行過的migration檔,要在migrate指令加上--reset,它會重新部署一次:

truffle migrate --reset

部署完後,可以進入console來操作:

truffle console

可以直接使用合約名稱去操作:

truffle console
>Metacoin.deployed().address
‘0x4c9fd534add127c89886788c96e246abb570fc12’

最後再build起來,然後用serve來跑起build完的app:

truffle build
truffle serve

執行serve後便可在localhost:8080看到跑起來的app

serve預設port是8080,可以用-p來指定。

最後是設定檔 truffle.js,有init的話會如下:

module.exports = {
build: {
"index.html": "index.html",
"app.js": [
"javascripts/app.js"
],
"app.css": [
"stylesheets/app.css"
],
"images/": "images/"
},
rpc: {
host: "localhost",
port: 8545
}
};

如果需要區分不同鏈的話,在其中加入networks設定值:

networks: {   
"live": {
network_id: 1, // Ethereum public network
// optional config values
// host - defaults to "localhost"
// port - defaults to 8545
// gas
// gasPrice
// from
},
"testnet": {
network_id: 2, // Official Ethereum test network
host: "61.66.218.xxx",
port: 8000
},
"private": {
network_id: 5566, // custom private network
host: "localhost",
port: 8001
},
"test_env": {
network_id: "default"
}
}

不同鏈給定不同名稱來區分,裡面可以指定連接到的rpc相關設定,都不指定就是使用rpc預設的localhost:8545,其中from是指定部署時要使用哪個帳戶當部署者。
network_id可以有一個是設為default,如果network名稱沒指定或不在設定值裡面,那就會連到default的那個,一般用來和Testrpc搭配使用。

要注意的是,要能連接到指定的鏈,你必須確定對應的rpc有開放。

指令如console、migrate、serve及exec都可加入--network選項來指定執行在哪條鏈上:

truffle migrate --network "testnet"

最後介紹目前還在開發階段的Metamask

在開發Dapp時,可能會遇到使用者私鑰管理的問題。如果Dapp是以網頁的形式呈現,意味使用者把私鑰交給開發者存在伺服器,透過伺服器登入來做交易的操作;如果是以app的形式呈現,則使用者便可以在自己的裝置上管理自己的私鑰,自己製作交易並簽名。

現在許多Dapp仍以網頁形式來運作,Metamask或許是一個暫時可用的解法。
1. 它將使用者私鑰儲存在本地端(瀏覽器),可以在本地端製作交易
2. 它可以選擇連接到Main net、Testnet或是自己設定rpc位址,前兩者透過Infura提供rpc服務
Metamask目前只能透過Chrome plugin來安裝。

首先,可以選擇創立新帳戶(CREATE NEW VAULT)或使用Mnemonic來使用同一組帳戶(RESTORE EXISTING VAULT):

左上方可選擇要連接的是Main net、tesenet或是自己設定rpc位址:

右上方可以選擇切換帳戶:

BUY會連接到Coinbase購買ether(但前提是要連到Main net)、SEND可以製作交易

不過安全性(XSS或是任何能取得瀏覽器暫存的人)是使用上需要考量的一個重要因素。