下面教程是打算在盡量牽涉可能少的以太坊的相關工具,主要使用web3.js這個以太坊提供的工具包于游,來完成合約的編譯毁葱,發(fā)布,合約方法調用的一整個流程贰剥。一方面來了解以太坊開發(fā)到底需要什么倾剿,另一方面來對web3.js的API有個基本的了解。由于所有其它工具都或多或少的是對web3.js的底層函數(shù)的包裝蚌成,所以對web3.js使用流程有個認識之后前痘,也能更好的入門,使用相關的工具担忧。
1. 準備工作
1.1 安裝Node.js
由于我們要使用web3.js[1]芹缔。這里使用Node來集成web3.js模塊(當然,你還可以使用其它的方式)瓶盛。你可以通過參考官網文檔安裝[2]乖菱。
1.1.1 Ubuntu
如果你使用ubuntu坡锡,可以使用下述命令:
//安裝Node
sudo apt-get install nodejs
//安裝Node的包管理器
sudo apt-get install npm
1.1.2 MAC
如果你使用Homebrew
,可以使用下述命令:
//安裝Node
brew install node
//安裝Node的包管理器
brew install npm
1.1.3 安裝檢查
安裝成功后窒所,可以查看下當前的版本鹉勒,確認正常安裝:
$ node -v
v7.2.0
1.2 以太坊的節(jié)點
由于整個合約代碼的執(zhí)行需要一個虛擬機環(huán)境,所以在開始之前吵取,我們不得不安裝一個實現(xiàn)了以太坊虛擬機的節(jié)點禽额。
可以選擇一個輕量級的節(jié)點,比如EtherumJS TestRPC
皮官,它是一個完整的在內存中的區(qū)塊鏈僅僅存在于你開發(fā)的設備上脯倒。它在執(zhí)行交易時是實時返回,而不等待默認的出塊時間捺氢,這樣你可以快速驗證你新寫的代碼藻丢,當出現(xiàn)錯誤時,也能即時反饋給你摄乒。
npm install -g ethereumjs-testrpc
安裝好后悠反,你就可以通過testrpc
命令來啟動了,啟動與大多數(shù)以太坊節(jié)點一樣馍佑,運行在localhost:8545
斋否。
如果你安裝geth
這樣的客戶端也是可以的。
1.3 Web3的支持
安裝web3
的模塊[1]:
npm install web3
2. 合約編譯
2.1 一個簡單的合約
我們打算用來測試的合約如下:
pragma solidity ^0.4.0;
contract Calc{
/*區(qū)塊鏈存儲*/
uint count;
/*執(zhí)行會寫入數(shù)據(jù)拭荤,所以需要`transaction`的方式執(zhí)行茵臭。*/
function add(uint a, uint b) returns(uint){
count++;
return a + b;
}
/*執(zhí)行不會寫入數(shù)據(jù),所以允許`call`的方式執(zhí)行舅世。*/
function getCount() constant returns (uint){
return count;
}
}
add()
方法用來返回輸入兩個數(shù)據(jù)的和旦委,并會對add()
方法的調用次數(shù)進行計數(shù)。需要注意的是這個計數(shù)是存在區(qū)塊鏈上的雏亚,對它的調用需要使用transaction
缨硝。
getCount()
返回add()
函數(shù)的調用次數(shù)。由于這個函數(shù)不會修改區(qū)塊鏈的任何狀態(tài)评凝,對它的調用使用call
就可以了。
2.2 編譯合約
由于合約是使用Solidity
編寫腺律,所以我們可以使用web3.eth.compile.solidity
來編譯合約[3]:
//編譯合約
let source = "pragma solidity ^0.4.0;contract Calc{ /*區(qū)塊鏈存儲*/ uint count; /*執(zhí)行會寫入數(shù)據(jù)奕短,所以需要`transaction`的方式執(zhí)行。*/ function add(uint a, uint b) returns(uint){ count++; return a + b; } /*執(zhí)行不會寫入數(shù)據(jù)匀钧,所以允許`call`的方式執(zhí)行翎碑。*/ function getCount() returns (uint){ return count; }}";
let calc = web3.eth.compile.solidity(source);
如果編譯成功,結果如下:
{
code: '0x606060405234610000575b607e806100176000396000f3606060405260e060020a6000350463771602f781146026578063a87d942c146048575b6000565b3460005760366004356024356064565b60408051918252519081900360200190f35b3460005760366077565b60408051918252519081900360200190f35b6000805460010190558181015b92915050565b6000545b9056',
info: {
source: 'pragma solidity ^0.4.0;contract Calc{ /*區(qū)塊鏈存儲*/ uint count; /*執(zhí)行會寫入數(shù)據(jù)之斯,所以需要`transaction`的方式執(zhí)行日杈。*/ function add(uint a, uint b) returns(uint){ count++; return a + b; } /*執(zhí)行不會寫入數(shù)據(jù),所以允許`call`的方式執(zhí)行。*/ function getCount() returns (uint){ return count; }}',
language: 'Solidity',
languageVersion: '0.4.6+commit.2dabbdf0.Emscripten.clang',
compilerVersion: '0.4.6+commit.2dabbdf0.Emscripten.clang',
abiDefinition: [
[
Object
],
[
Object
]
],
userDoc: {
methods: {
}
},
developerDoc: {
methods: {
}
}
}
}
3. 發(fā)布合約
web3.js
其實也像框架一樣對合約的操作進行了封裝莉擒。發(fā)布合約時酿炸,可以使用web3.eth.contract
的new
方法[4]。
let myContractReturned = calcContract.new({
data: deployCode,
from: deployeAddr
}, function(err, myContract) {
if (!err) {
// 注意:這個回調會觸發(fā)兩次
//一次是合約的交易哈希屬性完成
//另一次是在某個地址上完成部署
// 通過判斷是否有地址涨冀,來確認是第一次調用填硕,還是第二次調用。
if (!myContract.address) {
console.log("contract deploy transaction hash: " + myContract.transactionHash) //部署合約的交易哈希值
// 合約發(fā)布成功
} else {
}
});
部署過程中需要主要的是鹿鳖,new
方法的回調會執(zhí)行兩次扁眯,第一次是合約的交易創(chuàng)建完成,第二次是在某個地址上完成部署翅帜。需要注意的是只有在部署完成后姻檀,才能進行方法該用,否則會報錯TypeError: myContractReturned.add is not a function
涝滴。
4. 調用合約
由于web3.js
封裝了合約調用的方法绣版。我們可以使用可以使用web3.eth.contract
的里的sendTransaction
來修改區(qū)塊鏈數(shù)據(jù)。在這里有個坑狭莱,有可能會出現(xiàn)Error: invalid address
僵娃,原因是沒有傳from
,交易發(fā)起者的地址腋妙。在使用web3.js
的API都需留意默怨,出現(xiàn)這種找不到地址的,都看看from字段吧骤素。
//使用transaction方式調用匙睹,寫入到區(qū)塊鏈上
myContract.add.sendTransaction(1, 2,{
from: deployeAddr
});
console.log("after contract deploy, call:" + myContract.getCount.call());
需要注意的是,如果要修改區(qū)塊鏈上的數(shù)據(jù)济竹,一定要使用sendTransaction
痕檬,這會消耗gas
。如果不修改區(qū)塊鏈上的數(shù)據(jù)送浊,使用call
梦谜,這樣不會消耗gas
。
5. 使用web3.js編譯袭景,發(fā)布唁桩,調用的完整源碼
let Web3 = require('web3');
let web3;
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
// set the provider you want from Web3.providers
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
let from = web3.eth.accounts[0];
//編譯合約
let source = "pragma solidity ^0.4.0;contract Calc{ /*區(qū)塊鏈存儲*/ uint count; /*執(zhí)行會寫入數(shù)據(jù),所以需要`transaction`的方式執(zhí)行耸棒。*/ function add(uint a, uint b) returns(uint){ count++; return a + b; } /*執(zhí)行不會寫入數(shù)據(jù)荒澡,所以允許`call`的方式執(zhí)行。*/ function getCount() constant returns (uint){ return count; }}";
let calcCompiled = web3.eth.compile.solidity(source);
console.log(calcCompiled);
console.log("ABI definition:");
console.log(calcCompiled["info"]["abiDefinition"]);
//得到合約對象
let abiDefinition = calcCompiled["info"]["abiDefinition"];
let calcContract = web3.eth.contract(abiDefinition);
//2. 部署合約
//2.1 獲取合約的代碼与殃,部署時傳遞的就是合約編譯后的二進制碼
let deployCode = calcCompiled["code"];
//2.2 部署者的地址单山,當前取默認賬戶的第一個地址碍现。
let deployeAddr = web3.eth.accounts[0];
//2.3 異步方式,部署合約
let myContractReturned = calcContract.new({
data: deployCode,
from: deployeAddr
}, function(err, myContract) {
if (!err) {
// 注意:這個回調會觸發(fā)兩次
//一次是合約的交易哈希屬性完成
//另一次是在某個地址上完成部署
// 通過判斷是否有地址米奸,來確認是第一次調用昼接,還是第二次調用。
if (!myContract.address) {
console.log("contract deploy transaction hash: " + myContract.transactionHash) //部署合約的交易哈希值
// 合約發(fā)布成功后躏升,才能調用后續(xù)的方法
} else {
console.log("contract deploy address: " + myContract.address) // 合約的部署地址
//使用transaction方式調用辩棒,寫入到區(qū)塊鏈上
myContract.add.sendTransaction(1, 2,{
from: deployeAddr
});
console.log("after contract deploy, call:" + myContract.getCount.call());
}
// 函數(shù)返回對象`myContractReturned`和回調函數(shù)對象`myContract`是 "myContractReturned" === "myContract",
// 所以最終`myContractReturned`這個對象里面的合約地址屬性也會被設置。
// `myContractReturned`一開始返回的結果是沒有設置的膨疏。
}
});
//注意一睁,異步執(zhí)行,此時還是沒有地址的佃却。
console.log("returned deployed didn't have address now: " + myContractReturned.address);
//使用非回調的方式來拿到返回的地址者吁,但你需要等待一段時間,直到有地址饲帅,建議使用上面的回調方式复凳。
/*
setTimeout(function(){
console.log("returned deployed wait to have address: " + myContractReturned.address);
console.log(myContractReturned.getCount.call());
}, 20000);
*/
//如果你在其它地方已經部署了合約,你可以使用at來拿到合約對象
//calcContract.at(["0x50023f33f3a58adc2469fc46e67966b01d9105c4"]);
關于作者
專注基于以太坊(Ethereum)的相關區(qū)塊鏈(Blockchain)技術灶泵,了解以太坊育八,Solidity,Truffle赦邻,web3.js髓棋。
個人博客: http://me.tryblockchain.org
版權所有,轉載注明出處