以太坊智慧合約開發環境搭建
以太坊合約開發最快速上手是使用remix-ide,用瀏覽器開啟即可使用。不過喜歡折騰的話,就需要手動搭環境了,本文簡單介紹了以太坊開發環境的搭建。
一、 搭建環境
本文使用的作業系統為Ubuntu 16.04。
以太坊開發需要安裝:geth、solc、nodejs、web3.js。
- geth:用來挖礦、處理交易,執行合約程式碼。
- solc:用來將合約程式碼編譯為EVM可執行的操作碼。
- nodejs:提供javascript本地執行環境,web3即執行其上。
- web3.js:封裝好了一系列以太坊相關的介面。
安裝geth、solc:
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum solc
安裝nodejs:
sudo apt-get install nodejs
預設安裝的node版本為v4.x.x,這個版本比較低,會有少部分web3的功能無法使用,推薦從官網下載最新穩定版,如v8.x.x。
安裝web3:
cd ~
npm install web3
因為網路原因,國內通過npm安裝模組容易失敗,可以使用淘寶映象。
二、建立私有節點
合約程式碼需要執行在以太坊節點上,公鏈成本高速度慢,不適合開發測試,因此我們需要搭建私有節點來開發除錯。
首先,新建私有節點配置檔案genesis.json,輸入以下內容:
{
"config": {
"chainId": 123,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc" : {},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x20000",
"extraData" : "",
"gasLimit" : "0x2fefd8",
"nonce" : "0x0000000000000042",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00"
}
使用genesis.json生成私有節點
geth init genesis.json --datadir=.ethereum
會生成兩個資料夾./.ethereum/geth
、./.ethereum/keystore
。前者存放區塊資料,後者存放賬戶資料。
啟動挖礦程式,並對外提供訪問介面
geth --datadir=.ethereum --identity "lixp" --networkid 54321 --nodiscover --rpc --rpcaddr 0.0.0.0 --rpcport 44444 --rpccorsdomain '*' --rpcapi 'db,eth,net,web3,personal,admin,mine' console
在geth終端命令列中輸入下列測試命令。
新建賬戶,檢視餘額:
personal.newAccount()
account = eth.accounts[0]
balance = eth.getBalance(account)
web3.fromWei(balance)
設定賬戶為挖礦賬戶,並開啟挖礦:
miner.setEtherbase(account)
miner.start()
每次啟動後第一次挖礦需要初始化,耗時比較長,當看到新塊列印資訊後,停止挖礦,檢視餘額:
miner.stop()
balance = eth.getBalance(account)
web3.fromWei(balance)
三、 使用web3訪問私有節點
新建test.js,輸入下列程式碼,檢視餘額:
function web3Init()
{
Web3 = require('web3');
Net = require('net');
web3 = new Web3();
web3.setProvider(".ethereum/geth.ipc", Net);
D = console.log;
}
function onBalance(mesg)
{
D("_onBalance:" + mesg);
balance = mesg;
D("eth:" + web3.utils.fromWei(balance));
}
function onAccounts(mesg)
{
D("_onAccounts:" + mesg);
account = mesg[0];
web3.eth.getBalance(account).then(onBalance);
}
function getBalance()
{
web3.eth.getAccounts().then(onAccounts);
}
web3Init();
getBalance();
執行node test.js
檢視餘額。
四、 編寫一個簡單的智慧合約
新建people.sol,輸入以下內容:
pragma solidity ^0.4.0;
contract People
{
string name;
event newname(string);
constructor(string _name) public
{
rename(_name);
}
function rename(string _name) public
{
name = _name;
emit newname(name);
}
}
編譯:
solc --combined-json=abi,bin,interface people.sol > people.sol.js
編譯輸出為people.sol.js
,是一個json格式的資料,包含了合約的元資料(合約名、類方法、引數等)以及釋出到節點的EVM操作碼資料。
五、 釋出合約
新建deploy.js,輸入如下程式碼:
web3Init('.database/geth.ipc'); // 初始化相關模組
cJson = Json(File('people.sol.js')); // 讀取編譯後的json資料檔案
cAbi = cJson.contracts["people.sol:People"].abi; // 合約的ABI
cBin = cJson.contracts["people.sol:People"].bin; // 合約的BIN
cTemp = new eth.Contract(Json(cAbi)); // 新建合約物件
cTrans = cTemp.deploy({ // ‘合約釋出’交易物件
data: "0x"+cBin,
arguments: ['lixp']
});
eth.getCoinbase().then( function(mesg){ // 獲取coinbase
D("coinbase: " + mesg);
ethCoinbase = mesg;
sendTranscation(cTrans, ethCoinbase, '', onDeploy); // 使用coinbase部署合約,第三個引數為新建賬戶時的密碼
});
function onDeploy(mesg){
D('onDeploy');
cDeploy = mesg; // cDeploy中可以檢視合約地址
cDeploy.events.newname().on('data', function(event){
D('event newname:');
D(event.returnValues);
});
trans = cDeploy.methods.rename("xiaoming");
sendTranscation(trans, ethCoinbase, '', D);
}
function sendTranscation(trans, account, accountKey, callback) {
// 評估交易手續費
eth.estimateGas( {data: trans.encodeABI()} ).then(
function (mesg) {
gas = mesg;
D("gas:" + gas);
// 解鎖用於釋出合約的賬戶,否則會出錯
eth.personal.unlockAccount(account, accountKey).then(
function (result) {
if(result != true){
D('unlockAccount failed:' + result);
return;
}
// 將交易傳送到節點,但是如果節點停止挖礦,則交易無法打包到區塊中,
// 也就是說交易無法得到執行,callback也不會被觸發。
// 需要在節點終端執行miner.start(),開啟挖礦。
trans.send({from: account, gas: gas, gasPrice: '30000'}).then(callback);
}
);
}
)
}
function web3Init(url){
Web3 = require('web3');
Net = require('net');
web3 = new Web3(url, Net);
Fs=require('fs');
eth = web3.eth;
D = console.log;
File = (name) => {return Fs.readFileSync(name);}
Json = JSON.parse;
}
執行node deploy.js
來發布合約。
- 釋出合約所需要的賬戶必須有一定的餘額,因為合約釋出、呼叫需要消耗手續費。
- 釋出合約後,在geth終端可以看到交易的列印, 為了將交易打包到區塊中,需要執行miner.start()。
六、 在瀏覽器中顯示
生成cJson.js,方便瀏覽器呼叫:
echo "cJson = $(cat people.sol.js)" > cJson.js
新建people.html, 輸入如下:
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
<script src=cJson.js></script>
<script>
Web3 = require('web3');
Net = require('net');
web3 = new Web3();
web3.setProvider(".ethereum/geth.ipc", Net);
eth = web3.eth;
Json = JSON.parse;
D = console.log;
cAbi = cJson.contracts["people.sol:People"].abi;
address = "0x1234..."; // 釋出合約時得到的地址
cDeploy = new eth.contract(Json(cAbi)).at(address);
cDeploy.newname({},{fromBlock:0}).watch(
(error, log) => {
D('newname!');
D(error);
D(log);
}
);
</script>
開啟瀏覽器訪問people.html即可在偵錯程式控制檯看到列印資訊。
node中引入的web3版本為v1.x,網頁中引入的為v0.2.x,這兩個版本的API有一定的差異。