




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
以太坊Solidity智能合約開發(fā)教程以太網(wǎng)JavaScriptAPI——Web3.js學(xué)前提示W(wǎng)eb3.js是以太坊提供的JavaScriptAPI。使用Web3.js可以與以太坊節(jié)點進行通信,例如獲取節(jié)點狀態(tài),獲取賬號信息,調(diào)用合約、監(jiān)聽合約事件等。知識要點6.1Web3.js概述6.2區(qū)塊編程6.3以太坊賬戶與交易編程6.4智能合約編程基礎(chǔ)6.5在Web3.js中與智能合約進行交互6.6Web3.js開發(fā)實例:明日之星投票6.1Web3.js概述6.1.1什么是Web3.js6.1.2安裝Web3.js6.1.3第一個Web3.js的小實例6.1.4在網(wǎng)頁中使用Web3.js6.1.5本章實例的執(zhí)行環(huán)境6.1.6JavaScriptPromise對象
6.1.1什么是Web3.jsWeb3.js是一個函數(shù)庫,其中將函數(shù)按照特定功能劃分為如下幾個模塊·web3-eth:包含與以太坊區(qū)塊鏈和智能合約相關(guān)的函數(shù)?!?web3-shh:包含與whisper協(xié)議相關(guān)的函數(shù)。whisper是以太坊的分布式消息協(xié)議,可以實現(xiàn)智能合約間的消息互通。· web3-bzz:包含與swarm協(xié)議相關(guān)的函數(shù)。swarm是以太坊的分布式存儲協(xié)議,可以提供去中心化的內(nèi)容存儲和分發(fā)服務(wù)。。· web3-utils:包含一些很實用的helper函數(shù)。6.1.2安裝Web3.js(1)安裝gcc和gcc-c++。gcc是由GNU開發(fā)的編程語言編譯器,用于對npm的源代碼進行編譯。yuminstallgccgcc-c++(2)從國內(nèi)鏡像下載node.js,命令如下:
cd/usr/local/wget/mirrors/node/v10.14.1/node-v10.14.1-linux-x64.tar.gz
(3)解壓縮下載的安裝包,并將其重命名為node,命令如下:tar-xvfnode-v10.14.1-linux-x64.tar.gzmvnode-v10.14.1-linux-x64node(4)配置環(huán)境變量vi/etc/profile然后在文件最后添加以下配置:exportNODE_HOME=/usr/local/nodeexportPATH=$NODE_HOME/bin:$PATH保存退出后,執(zhí)行下面的命令應(yīng)用配置。source/etc/profile(5)驗證安裝結(jié)果node-vnpm-v安裝npm后,執(zhí)行可以下面的命令將npm更新到最新版本npminstall-gnpm安裝Web3.jsnpminstallweb3@^0.20.0對npm進行初始化如果看到如下的提示,則說明npm沒有進行初始化。npmWARNsaveErrorENOENT:nosuchfileordirectory,open'/root/package.json'執(zhí)行如下命令對npm進行初始化。npminit發(fā)現(xiàn)一個低嚴重性的漏洞錯誤如果看到如下的提示,則說明nmp反饋發(fā)現(xiàn)一個低嚴重性的漏洞錯誤。found1lowseverityvulnerabilityrun`npmauditfix`tofixthem,or`npmaudit`fordetails根據(jù)提示,可以執(zhí)行下面的命令修復(fù)此漏洞。npmauditfix6.1.3第一個Web3.js的小實例執(zhí)行下面的命令在/usr/local/目錄下創(chuàng)建web3子目錄,用于保存本章實例。cd/usr/local/mkdirweb3在/usr/local/web3目錄下創(chuàng)建connect.jsvarWeb3=require('web3')varweb3=newWeb3(newWviders.HttpProvider('01:8545'))varversion=web3.version.node;console.log(version);程序的具體說明如下·require('web3'):用于引入Web3.js腳本?!viders.HttpProvider():用于連接到以太坊節(jié)點。當(dāng)Web3連接到以太坊節(jié)點時需要指定服務(wù)提供器(Provider)。Wviders.HttpProvider()是常用的Provider,它通過HTTP連接到以太坊節(jié)點。·console.log():JavaScript函數(shù),用于在瀏覽器的控制臺中打印日志信息?!eb3.version.node:返回以太坊節(jié)點的版本號。與RPC連接有關(guān)的參數(shù)說明·-rpc:啟用RPC。·--rpcaddr01:指定以太坊私有鏈節(jié)點的IP地址,這里為CentOS虛擬機的IP地址?!?-rpcport8545:在8545端口上監(jiān)聽RPC連接?!?-rpccorsdomain"*":指定接受遠程RPC連接的地址范圍,*表示任意地址?!?-rpcapi="db,eth,net,web3,personal":指定可以調(diào)用的HTTP-RPCAPI接口,默認只有eth,net,web3。運行connect.js的結(jié)果數(shù)據(jù)庫啟動以太坊私有鏈后,打開一個新的putty終端窗口,執(zhí)行下面的命令:cd/usr/local/web3/nodeconnect.js6.1.4在網(wǎng)頁中使用Web3.js首先需要下載Web3.js腳本??梢宰孕兴阉飨螺d網(wǎng)址,也可以從SourceF下載,URL如下:/projects/web3-js.mirror/files/latest/download下載得到Web3.js1.3.0.zip,也可能是更高的版本。解壓后在dist文件夾下可以找到web3.min.js。使用時將其復(fù)制到存放HTML文件的目錄下。在HTML文件中引用web3.min.js的代碼如下:
<scriptsrc="web3.min.js"></script>【例6-1】<html><head><metacharset="UTF-8"><title>演示使用web3.js創(chuàng)建合約對象的方法</title>
<scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script>
<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(providers);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider("01:8545"));}console.log(Web3.version);});</script></head></html>
在Chrome瀏覽器的控制臺中查看Web3.js的版本信息6.1.5本章實例的執(zhí)行環(huán)境本章實例需要事先參照2.4節(jié)搭建測試環(huán)境。本章實例中HTML文件都部署在CentOS虛擬機的/var/www/html目錄下,并確保在CentOS虛擬機中已經(jīng)安裝并啟動Apache服務(wù)。在CentOS虛擬機中應(yīng)參照6.1.2節(jié)安裝Web.js。執(zhí)行下面的命令啟動以太坊私有鏈cd/usr/local/go-ethereum/build/bin./geth--datadirethchain--nodiscoverconsole2-dev--dev.period1--password'./password.txt'--rpcaddr01--rpcport8545--rpccorsdomain"*"-rpc--rpcapi="db,eth,net,web3,personal"6.1.6JavaScriptPromise對象Promise是異步編程的一種解決方法,在Web3.js中大量使用。1.手動創(chuàng)建Promise對象Promise代表將來要發(fā)生的事件,用來傳遞異步操作的消息。Promise對象通常表示異步操作的結(jié)果,有下面3種狀態(tài)。?pending:初始狀態(tài),表示待定狀態(tài),即不確定成功還是失敗。?fulfilled:表示操作完成。?rejected:表示操作失敗。在實例化Promise對象時可以指定一個回調(diào)函數(shù)varpromise=newPromise(function(resolve,reject){//異步處理,處理結(jié)束后調(diào)用resolve或reject});演示實例化Promise對象的代碼varpromise=newPromise(function(resolve,reject){//異步操作的代碼
if(//異步操作成功){resolve(value);}else{reject(error);}});使用then()方法來指定resolve()函數(shù)和reject()函數(shù)<promise對象>.then(function(result){//操作成功
……},function(result){//操作失敗
……});【例6-2】<html><head><metacharset="UTF-8"><title>演示手動創(chuàng)建Promise對象的方法</title><script>varmyFirstPromise=newPromise(function(resolve,reject){setTimeout(function(){resolve("成功!");},10000);});myFirstPromise.then(function(successMessage){document.write("返回信息:"+successMessage);});</script></head></html>例6-2的瀏覽結(jié)果2.作為異步操作的結(jié)果在很多情況下,Promise對象不需要手動創(chuàng)建,而是作為異步操作的結(jié)果返回??梢岳肞romise對象處理異步操作的結(jié)果。Web3.js中就有很多函數(shù)返回Promise對象。web3.eth.getProtocolVersion()方法就是這樣的。web3.eth.getProtocolVersion()方法可以返回節(jié)點旳以太坊協(xié)議版本?!纠?-3】<html><head><metacharset="UTF-8"><title>web3.eth.getProtocolVersion</title><scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script>
<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(providers);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider(01:8545));}web3.eth.getProtocolVersion().then(console.log);});</script></head></html>返回結(jié)果將sample6-3.html上傳至CentOS虛擬機的/var/www/html目錄下。然后打開Chrome瀏覽器,訪問如下URL:01/sample6-3.html然后按下F12鍵,可以在Console窗口中看到web3.eth.getProtocolVersion()方法的返回結(jié)果。筆者的返回結(jié)果是0x41。6.2區(qū)塊編程6.2.1標識一個區(qū)塊6.2.2獲取當(dāng)前區(qū)塊編號6.2.3獲取默認區(qū)塊6.2.4獲取指定區(qū)塊詳情6.2.5獲取指定叔區(qū)塊6.2.1標識一個區(qū)塊在以太坊中,可以通過區(qū)塊號或者區(qū)塊哈希來標識一個區(qū)塊。也可以使用如下字符串標識區(qū)塊。?"pending":表示等待打包的區(qū)塊。?"genesis":表示創(chuàng)世區(qū)塊。?"latest":表示最新產(chǎn)生的區(qū)塊。在本章后面介紹的Web3.js方法中,很多會通過以上方法標識一個區(qū)塊。6.2.2獲取當(dāng)前區(qū)塊編號web3.eth.getBlockNumber([(function(error,result){console.log(result);});]);web3.eth.getBlockNumber()方法返回一個Promise對象如果沒有使用回調(diào)函數(shù),也可以通過Promise對象獲取返回結(jié)果,方法如下:web3.eth.getBlockNumber().then(console.log);
【例6-4】創(chuàng)建一個名為sample6-4.html的網(wǎng)頁文件,網(wǎng)頁中定義一個id="current_block_number"的a元素,用于顯示獲取到的當(dāng)前區(qū)塊編號,其定義代碼如下:
<aid="current_block_number">獲取當(dāng)前區(qū)塊號...</a>獲取當(dāng)前區(qū)塊編號的代碼<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider("01:8545"));}varcurrentBlock="";web3.eth.getBlockNumber(function(error,result){$("#current_block_number").text("當(dāng)前區(qū)塊號:"+result); });});</script>例6-4的瀏覽結(jié)果
6.2.3獲取默認區(qū)塊通過web3.eth.defaultBlock屬性設(shè)置和獲取默認區(qū)塊號??梢允褂?.2.1節(jié)介紹的方法標識一個區(qū)塊。web3.eth.defaultBlock的默認值為"latest"。6.2.4獲取指定區(qū)塊詳情使用web3.eth.getBlock()方法可以返回指定編號或哈希的區(qū)塊詳情。具體方法如下:web3.eth.getBlock(<區(qū)塊哈希或區(qū)塊索引>[,<是否返回交易詳情>][,<回調(diào)函數(shù)>])【例6-5】<html><head><metacharset="UTF-8"><title>演示使用web3.eth.getBlock方法</title><scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script>
<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(providers);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider("01:8545"));}web3.eth.getBlock("latest",true,function(error,result){if(error){console.log(error);}else{console.log(result);}})});</script></head></html>瀏覽sample6-5.html的結(jié)果6.2.5獲取指定叔區(qū)塊調(diào)用web3.eth.getUncle()方法可以返回指定區(qū)塊的叔區(qū)塊。一個區(qū)塊的叔區(qū)塊的父區(qū)塊與它自己的爺爺區(qū)塊(父區(qū)塊的父區(qū)塊)是同一個區(qū)塊。web3.eth.getUncle(<區(qū)塊號或區(qū)塊哈希>,<叔區(qū)塊的索引位置>[,<是否返回區(qū)塊中的交易詳情>][,<回調(diào)函數(shù)>])6.3以太坊賬戶與交易編程6.3.1獲取賬戶列表6.3.2默認賬戶和幣基賬戶6.3.3獲取賬戶余額6.3.4獲取指定區(qū)塊中的交易數(shù)量6.3.5獲取指定區(qū)塊中的交易詳情6.3.6根據(jù)交易哈希獲取交易對象6.3.7獲取交易的收據(jù)對象6.3.8向以太坊網(wǎng)絡(luò)提交交易6.3.9估算交易的gas用量6.3.1獲取賬戶列表web3.eth.getAccounts(callback(error,result){...})【例6-6】<html><head><metacharset="UTF-8"><title>演示使用web3.js創(chuàng)建合約對象的方法</title>
<scriptsrc="/jquery-3.1.1.min.js"></script><scriptsrc="web3.min.js"></script>
<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(providers);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider(01:8545));}web3.eth.getAccounts(callback(error,result){if(error){console.log(error);}else{console.log(result);}})});</script></head></html>瀏覽sample6-6.html的結(jié)果6.3.2默認賬戶和幣基賬戶使用web3.eth.defaultAccount屬性可獲取或設(shè)置默認賬戶地址。在一些涉及交易的方法中如果不指定from參數(shù),將會使用默認賬戶作為from參數(shù)值。使用web3.eth.getCoinbase()方法可獲取幣基賬戶地址,也就是接收挖礦獎勵的賬戶地址。具體方法如下:web3.eth.getCoinbase([<回調(diào)函數(shù)>])【例6-7】創(chuàng)建一個名為sample6-7.html的網(wǎng)頁文件,網(wǎng)頁中定義一個id="coinbase"的a元素,用于顯示獲取到的幣基賬戶地址,其定義代碼如下:
<aid="coinbase">獲取幣基賬戶地址...</a>
獲取幣基賬戶地址的代碼<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(providers);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider("01:8545"));}web3.eth.getCoinbase().then(function(result,error){if(error)console.log(error)else{$("#coinbase").text("幣基賬戶地址:"+result);}});});</script>
例6-7的瀏覽結(jié)果
6.3.3獲取賬戶余額調(diào)用web3.eth.getBalance()方法可以獲取指定賬戶的余額,具體方法:web3.eth.getBalance(<賬戶地址>[,<默認區(qū)塊>][,<回調(diào)函數(shù)>])【例6-8】創(chuàng)建一個名為sample6-8.html的網(wǎng)頁文件,網(wǎng)頁中定義一個id="coinbase"的a元素,用于顯示獲取到的幣基賬戶余額,其定義代碼如下:
<aid="coinbase">獲取幣基賬戶余額...</a>獲取幣基賬戶余額的代碼如下<script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(providers);}else{//settheprovideryouwantfromWvidersweb3=newWeb3(newWviders.HttpProvider("01:8545"));}web3.eth.getBalance("0xd169d4387c2dcd1ec4e6952bddf0a22c1f8c2b2f").then(function(result,error){if(error)console.log(error)else{$("#coinbase").text("幣基賬戶余額:"+result);}
});});</script>例6-8的瀏覽結(jié)果6.3.4獲取指定區(qū)塊中的交易數(shù)量web3.eth.getBlockTransactionCount(<區(qū)塊哈希或區(qū)塊索引>[,回調(diào)函數(shù)])
【例6-9】創(chuàng)建一個名為sample6-9.html的網(wǎng)頁文件,網(wǎng)頁中定義一個id="log"的a元素,用于顯示獲取到的當(dāng)前區(qū)塊編號代碼如下:
<aid="log">獲取區(qū)塊中的交易數(shù)量...</a>
getBlockTransactionCount()函數(shù)functiongetBlockTransactionCount(web3,blockno){console.log("entergetBlockTransactionCount");web3.eth.getBlockTransactionCount(blockno,function(error,result){if(error){console.log("web3.eth.getBlockTransactionCount返回錯誤:"+error);}else{if(result>0){//alert("區(qū)塊"+blockno+"的交易數(shù)量:"+result);$("#log").html($("#log").html()+"區(qū)塊"+blockno+"的交易數(shù)量:"+result+"<br>");}}});}getBlockTransactionCount()函數(shù)有2個參數(shù)
web3:表示連接到以太坊節(jié)點的Web3對象。
blockno:用于指定區(qū)塊號。在網(wǎng)頁sample6-9.html中調(diào)用getBlockTransactionCount()函數(shù)$(function(){console.log("enter");varsleep=function(time){varstartTime=newDate().getTime()+parseInt(time,10);while(newDate().getTime()<startTime){}};if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);}else{//settheprovideryouwantfromWvidersvarweb3=newWeb3(newWviders.HttpProvider('01:8545')); console.log(web3);}varcurrentBlock="";console.log("start");for(vari=110000;i<118910;i++){ getBlockTransactionCount(web3,i);sleep(100);}});例6-9的瀏覽結(jié)果在console窗口中查看調(diào)用getBlockTransactionCount()函數(shù)的次數(shù)6.3.5獲取指定區(qū)塊中的交易詳情web3.eth.getTransactionFromBlock(<區(qū)塊哈?;騾^(qū)塊號>,<交易索引位置>[,<回調(diào)函數(shù)>])【例6-10】<html><head><metacharset="UTF-8"><title>web3.eth.getTransactionFromBlock</title> <scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script><script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);}else{//settheprovideryouwantfromWvidersvarweb3=newWeb3(newWviders.HttpProvider('01:8545'));}web3.eth.getTransactionFromBlock(117952,0,function(error,result){if(error){
console.log("web3.eth.getTransactionFromBlock返回錯誤:"+error);}else{console.log(result);}});});</script></head></html>在console窗口中查看調(diào)用getTransactionFromBlock()方法的日志輸出日志中顯示了交易的詳情,具體如下?blockHash,區(qū)塊哈希。?blockNumber:區(qū)塊號。?from,發(fā)起交易的賬戶地址。?gas,交易花費的Gas數(shù)量。?gasPrice,交易花費的Gas數(shù)量。?hash,交易的哈希值。?input,交易的輸入?yún)?shù)。?nonce,隨機數(shù)。?r,s,v,交易的簽名。?nonce,隨機數(shù)。?to,交易的目標賬戶地址。?transactionIndex,交易在區(qū)塊中的位置索引。?value,交易的金額。6.3.6根據(jù)交易哈希獲取交易對象web3.eth.getTransaction(<交易哈希>[,<回調(diào)函數(shù)>])【例6-11】<html><head><metacharset="UTF-8"><title>web3.eth.getTransaction</title> <scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script><script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);}else{//settheprovideryouwantfromWvidersvarweb3=newWeb3(newWviders.HttpProvider('01:8545'));}web3.eth.getTransaction("0xf3982b3798ace0953621ca7b415f94a0652805a826c72b93cb5e33e0aa961937",function(error,result){if(error){console.log("web3.eth.getTransaction返回錯誤:"+error);}else{console.log(result);}});});</script></head></html>在console窗口中查看調(diào)用web3.eth.getTransaction()方法的日志輸出6.3.7獲取交易的收據(jù)對象web3.eth.getTransactionReceipt(<交易哈希>[,<回調(diào)函數(shù)>])【例6-12】<html><head><metacharset="UTF-8"><title>web3.eth.getTransactionReceipt</title> <scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script><script>$(function(){varsleep=function(time){varstartTime=newDate().getTime()+parseInt(time,10);while(newDate().getTime()<startTime){}};if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);}else{//settheprovideryouwantfromWvidersvarweb3=newWeb3(newWviders.HttpProvider('01:8545'));}console窗口中查看調(diào)用web3.eth.getTransactionReceipt()方法的日志輸出web3.eth.getTransactionReceipt()方法返回結(jié)果中的主要字段字段名說
明blockHash區(qū)塊哈希blockNumber區(qū)塊號contractAddress合約地址cumulativeGasUsed交易所在區(qū)塊中在指定交易完成時累計使用的Gas值from發(fā)起交易的賬戶地址gasUsed交易花費的Gaslogs交易的日志logsBloom用于快速搜索和判斷一個日志是否存在于收據(jù)MPT樹中的數(shù)據(jù)status交易的狀態(tài)to交易的目標賬戶地址transactionHash交易哈希transactionIndex交易索引6.3.8向以太坊網(wǎng)絡(luò)提交交易web3.eth.sendTransaction(<交易對象>[,callback])交易對象包含如下字段?from,發(fā)起交易的賬戶地址。如果不設(shè)置該字段,則會使用web3.eth.defaultAccount屬性值。?to,消息的目標地址。?value,交易的金額。?gas,用于交易的Gas總量,未用完會退回。通??梢圆恢付?。?gasPrice,交易的Gas價格,單位為wei。通常可以不指定。默認值為web3.eth.gasPrice屬性值。?data,可選參數(shù),可以是包含合約方法數(shù)據(jù)的ABI字符串。?nonce,可選參數(shù),可以使用該字段覆蓋使用相同nonce值的掛起交易?!纠?-13】(1)首先參照2.4.8節(jié)中例2-1的方法執(zhí)行web3.eth.accounts命令查看私有鏈中包含的賬戶。假定已經(jīng)按照2.4.5節(jié)中介紹的內(nèi)容創(chuàng)建了私有鏈賬戶,選擇一個非幣基賬戶作為轉(zhuǎn)入賬戶,這里假定為"0xcc130bc18dd3abb060b1ea5451a5f09abc2634cd"。讀者可以根據(jù)實際情況選擇轉(zhuǎn)入賬戶。參照6.3.2節(jié)查看幣基賬戶地址,本例假定為0xd169d4387c2dcd1ec4e6952bddf0a22c1f8c2b2f。(2)查看幣基賬戶和"0xcc130bc18dd3abb060b1ea5451a5f09abc2634cd"的余額。假定幣基賬戶的余額大于1ether,記錄"0xcc130bc18dd3abb060b1ea5451a5f09abc2634cd"的余額以便后面核對轉(zhuǎn)賬的結(jié)果。(3)如果幣基賬戶或"0xcc130bc18dd3abb060b1ea5451a5f09abc2634cd"被鎖定,則執(zhí)行web3.personal.unlockAccount()命令將其解鎖。(4)創(chuàng)建一個名為sample6-13.html的網(wǎng)頁文件<html><head><metacharset="UTF-8"><title>web3.eth.getTransaction</title><scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script><script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);}else{//settheprovideryouwantfromWvidersvarweb3=newWeb3(newWviders.HttpProvider('01:8545'));}web3.eth.sendTransaction({from:'0xd169d4387c2dcd1ec4e6952bddf0a22c1f8c2b2f',to:'0xcc130bc18dd3abb060b1ea5451a5f09abc2634cd',value:'1000000000000000'}).then(function(receipt){console.log(receipt);});
});</script></head></html>
在console窗口中查看例6-13的日志輸出6.3.9估算交易的gas用量web3.eth.estimateGas(<交易對象>[,<回調(diào)函數(shù)>])【例6-14】<html><head><metacharset="UTF-8"><title>web3.eth.getTransaction</title><scriptsrc="/ajax/jquery/jquery-3.5.1.min.js"></script><scriptsrc="web3.min.js"></script><script>$(function(){if(typeofweb3!=='undefined'){web3=newWeb3(web3.currentProvider);
}else{//settheprovideryouwantfromWvidersvarweb3=newWeb3(newWviders.HttpProvider('01:8545'));}addr=web3.eth.coinbase;web3.eth.estimateGas({from:'0xd169d4387c2dcd1ec4e6952bddf0a22c1f8c2b2f',to:'0xcc130bc18dd3abb060b1ea5451a5f09abc2634cd',value:'1000000000000000'}).then(console.log);
});</script></head></html>在console窗口中查看例6-14的日志輸出6.4智能合約編程基礎(chǔ)6.4.1以太坊智能合約ABI6.4.2以太坊智能合約的字節(jié)碼6.4.3在VisualStudioCode中生成智能合約的ABI和字節(jié)碼6.4.4JSON-RPC6.4.1以太坊智能合約ABIABI(ApplicationBinaryInterface,應(yīng)用程序二進制接口)與API類似,也是程序與程序之間交互的接口,只不過ABI是與被編譯成二進制代碼后的程序進行交互。ABI傳遞的是二進制格式的信息。在Web3.js中,可以通過ABI調(diào)用智能合約。以太坊智能合約ABI是一個數(shù)組,其中包含若干個JSON字符串,這些JSON字符串用來表示智能合約的函數(shù)或事件。JSON一種輕量級的、以格式字符串來表示對象的方法。將對象轉(zhuǎn)換為字符串的過程被稱為序列化,這么做的原因是因為作為一種結(jié)構(gòu)化的數(shù)據(jù)、對象無法直接在網(wǎng)絡(luò)中傳輸,通??梢允褂脤?yīng)的JSON字符串完成對象的傳輸。接收到數(shù)據(jù)時再從JSON字符串轉(zhuǎn)換為對象,這個過程叫做反序列化。JSON字符串中包含如下特殊字符?左方括號([):標識一個數(shù)組的開始。?左大括號({):標識一個對象的開始。?右方括號(]):標識一個數(shù)組的結(jié)束。?右大括號(}):標識一個對象的結(jié)束。?冒號(:):標識屬性名與值之間的分隔。?逗號(,):標識屬性名-值對之間的分隔。JSON字符串的例子
{"username":"zhangsan","name":"張三","age":18}在以太坊智能合約ABI中,函數(shù)共包含如下7個參數(shù)
name:函數(shù)名。
type:函數(shù)類型,可以是"function"、"constructor"或"fallback"。
inputs:函數(shù)的輸入?yún)?shù)。inputs是一個數(shù)組。
outputs:函數(shù)的返回值。與inputs一樣,outputs也是一個數(shù)組。
payable:如果為true,則表示函數(shù)可以接受以太幣。默認為false。
constant:一個布爾值,如果為true,則表示方法不會修改合約字段的狀態(tài)變量。
stateMutability:一個字符串,指定函數(shù)的狀態(tài)類型。inputs參數(shù)中數(shù)組元素的屬性
可取值類型具體說明name字符串參數(shù)名type字符串參數(shù)的數(shù)據(jù)類型components數(shù)組輸入?yún)?shù)為struct時才會由此參數(shù),用于描述struct中包含的參數(shù)類型stateMutability參數(shù)的可取值
屬性名具體說明pure指定函數(shù)不讀取區(qū)塊鏈狀態(tài)view指定函數(shù)只查看狀態(tài)變量,不會修改合約字段nonpayable指定函數(shù)不可以接受以太幣payable指定函數(shù)可以接受以太幣一個簡單的智能合約demo.solpragmasolidity^0.5.1;//聲明合約contractDemo{functionsum_for(uint_max)publicpurereturns(uint){uint_i;uint_sum=0;for(_i=0;_i<=_max;_i++){_sum+=_i;}return_sum;}}l對應(yīng)的ABI代碼[ { "constant":true, "inputs":[ { "name":"_max", "type":"uint256" } ],
"name":"sum_for", "outputs":[ { "name":"", "type":"uint256" } ], "payable":false, "stateMutability":"pure", "type":"function" }]
inputs參數(shù)中數(shù)組元素的屬性屬性屬性值具體說明namesum_for指定函數(shù)名typefunction指定sum_for的類型為stateMutabilitypure指定sum_for()函數(shù)不訪問區(qū)塊鏈inputs "inputs":[ { "name":"_max", "type":"uint256" } ],
指定sum_for()函數(shù)的輸入?yún)?shù),本例中只有一個參數(shù)_max,類型為uint256outputs[{"internalType":"uint256","name":"","type":"uint256"}]指定sum_for()函數(shù)的返回值,本例中只有一個uint256類型借助Solidity語言的編譯器solc自動生成智能合約ABI代碼執(zhí)行下面命令可以通過npm安裝solc。npminstall-gsolc安裝成功后可以執(zhí)行下面的命令查看solc的版本。solcjs-V生成demo.sol的ABI代碼cd/usr/local/web3/solcjsdemo.sol–abisolc版本不匹配時會報下面的錯誤ParserError:Sourcefilerequiresdifferentcompilerversion(currentcompileris0.7.5+commit.eb77ed08.Emscripten.clang)-notethatnightlybuildsareconsideredtobestrictlylessthanthereleasedversionpragmasolidity^0.5.1;^---------------------^在Remix中生成ABI代碼6.4.2以太坊智能合約的字節(jié)碼1.函數(shù)選擇器2.參數(shù)編碼1.函數(shù)選擇器函數(shù)選擇器是對函數(shù)的編碼。編碼的規(guī)則是對“函數(shù)名稱+參數(shù)類型”得到的字符串進行SHA3(keccak256)哈希運算之后取前4個字節(jié)。通過如下代碼生成函數(shù)選擇器constWeb3=require('web3')constweb3=newWeb3()console.log(web3.sha3(<函數(shù)名(參數(shù)類型)>))查看sum_for()函數(shù)的ABI代碼[ { "constant":true, "inputs":[ { "name":"_max", "type":"uint256" } ], "name":"sum_for",
"outputs":[ { "name":"", "type":"uint256" } ], "payable":false, "stateMutability":"pure", "type":"function" }]啟動以太坊私有鏈并啟用RPC,在8545端口上監(jiān)聽RPC連接。cd/usr/local/go-ethereum/build/bin./geth--datadirethchain--nodiscoverconsole2-dev--dev.period1--password'./password.txt'--rpcport8545--rpccorsdomain"*"–rpcaddr01-rpc--rpcapi="db,eth,net,web3,personal"在/usr/local/web3目錄下創(chuàng)建funcselector.jsconstWeb3=require('web3')constweb3=newWeb3()console.log(web3.sha3('sum_for(uint256)'))運行funcselector.jscd/usr/local/web3/nodefuncselector.js得到的結(jié)果如下:0x82412b00582deaf25e31ac0b9f73cbd06fc532b2e629f411c462efd8f0d8ee0c一個字節(jié)相當(dāng)于2個16進制字符,因此前4個字符為0x82412b00。也就是sum_for(uint)的函數(shù)選擇器。2.參數(shù)編碼參數(shù)類型編碼不足32位字節(jié)的處理方式整型如果是正數(shù),則高位補0;如果是負數(shù),則高位補1bool高位補0字節(jié)/字符串類型低位補0【例6-15】pragmasolidity^0.5.1;//聲明合約contractFoo{functionbaz(uint32x,booly)publicpurereturns(boolr){r=x>32||y;}}假設(shè)調(diào)用函數(shù)的參數(shù)如下baz(69,true)生成調(diào)用智能合約Foo字節(jié)碼的過程(1)計算baz(uint32,bool)的函數(shù)選擇器,得到0xcdcd77c0。(2)計算69的參數(shù)編碼。將10進制數(shù)69轉(zhuǎn)換為16進制數(shù)為45。因為45是正數(shù),所以前面使用0補齊至32個字節(jié),得到:0x0000000000000000000000000000000000000000000000000000000000000045第2個參數(shù)的參數(shù)編碼第2個參數(shù)bool類型true對應(yīng)16進制的1,高位補0,得到:0x0000000000000000000000000000000000000000000000000000000000000001對智能合約Foo中baz()函數(shù)的調(diào)用baz(69,true)對應(yīng)的字節(jié)碼如下0xcdcd77c00x00000000000000000000000000000000000000000000000000000000000000450x0000000000000000000000000000000000000000000000000000000000000001【例6-16】functionbar(bytes3[2]memoryarr)publicpurereturns(bytes3){returnarr[0];}假設(shè)調(diào)用函數(shù)的參數(shù)如下bar(["abc","def"])生成調(diào)用bar()函數(shù)字節(jié)碼的過程(1)參照前面的方法計算bar(bytes3[2])的函數(shù)選擇器,得到0xfce353f6。對于固定長度的動態(tài)類型數(shù)據(jù)對于固定長度的動態(tài)類型數(shù)據(jù),計算參數(shù)編碼的過程很簡單,只需要依次計算每個元素的編碼即可。本例中,只需要分別計算"abc"和"def"的編碼即可。"abc"的編碼"abc"對應(yīng)的16進制數(shù)為616263,不足32位的低位用0補齊,得到:0x6162630000000000000000000000000000000000000000000000000000000000"def"的編碼"def"對應(yīng)的16進制數(shù)為646566,不足32位的低位用0補齊,得到:0x6465660000000000000000000000000000000000000000000000000000000000以["abc","def"]為參數(shù)調(diào)用bar()函數(shù)對應(yīng)的字節(jié)碼0xfce353f60x61626300000000000000000000000000000000000000000000000000000000000x6465660000000000000000000000000000000000000000000000000000000000【例6-17】functionf(uint_a,uint32[]_arr,bytes10_b1,bytes_b2)publicpure{}假設(shè)調(diào)用函數(shù)的參數(shù)如下f(0x123,[0x456,0x789],"1234567890","Hello,world!")對應(yīng)的字節(jié)碼格式如下函數(shù)選擇器(4個字節(jié))第1個參數(shù)(0x123)的值(32個字節(jié))第2個參數(shù)([0x456,0x789])的偏移量(32個字節(jié))第3個參數(shù)("1234567890")的值(32個字節(jié))第4個參數(shù)("Hello,world!")的偏移量(32個字節(jié))第2個參數(shù)([0x456,0x789])的值第4個參數(shù)("Hello,world!")的值生成調(diào)用f()函數(shù)字節(jié)碼的過程(1)參照前面的方法計算f(uint256,uint32[],bytes10,bytes)的函數(shù)選擇器,得到0x8be65246。(2)第1個參數(shù)(0x123)的值如下:0x0000000000000000000000000000000000000000000000000000000000000123(3)計算第2個參數(shù)([0x456,0x789])偏移量(32個字節(jié))的計算公式第2個參數(shù)的偏移量=第1個參數(shù)(0x123)的值占用的空間(32個字節(jié))+第2個參數(shù)([0x456,0x789])的偏移量本身所占用的空間(32個字節(jié))+第3個參數(shù)的值占用的空間(32個字節(jié))+第4個參數(shù)的值占用的空間(32個字節(jié))因此第2個參數(shù)的偏移量為4×32字節(jié)=128字節(jié),轉(zhuǎn)換為32字節(jié)的16進制數(shù)如下:0x0000000000000000000000000000000000000000000000000000000000000080(4)第3個參數(shù)("1234567890")的值0x31323334353637383930000000000000000000000000000000000000000000000x3132333435363738393是字符串"1234567890"對應(yīng)的16進制數(shù)據(jù),低位用0補足32位。(5)計算第4個參數(shù)("Hello,world!")的偏移量第4個參數(shù)參數(shù)的偏移量=第2個參數(shù)的偏移量+第2個參數(shù)所占的空間=4*32+3*32=7*32=224字節(jié)224轉(zhuǎn)換為16進制數(shù)等于e0,高位補0,得到:0x00000000000000000000000000000000000000000000000000000000000000e0(6)接下來計算第2個參數(shù)([0x456,0x789])的編碼。首先是元素的個數(shù),然后是每個元素的編碼,具體如下:0x00000000000000000000000000000000000000000000000000000000000000020x00000000000000000000000000000000000000000000000000000000000004560x0000000000000000000000000000000000000000000000000000000000000789(5)最后計算第4個動態(tài)參數(shù)的編碼首先是元素的個數(shù),然后是每個元素的編碼,具體如下:0x000000000000000000000000000000000000000000000000000000000000000d0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000第2個動態(tài)參數(shù)第2個動態(tài)參數(shù)"Hello,world!"的長度為13,轉(zhuǎn)換為16進制為0d。0x48656c6c6f2c20776f726c6421是參數(shù)"Hello,world!"對應(yīng)的16進制數(shù)。匯總起來,例6-17中調(diào)用函數(shù)的參數(shù)編碼如下0x8be6524600000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c6421000000000000000000000000000000000000006.4.3在VisualStudioCode中生成智能合約的ABI和字節(jié)碼首先參照3.1.3節(jié)在VisualStudioCode中安裝和配置solidity插件。然后運行VisualStudioCode,在菜單中依次選擇File/Folder,打開準備好的項目目錄,在src子目錄下創(chuàng)建demo.sol,代碼與6.4.1節(jié)中介紹的一致。按下F5鍵,在OUTPUT窗格中提示Compilationcompletedsuccessfully!,即表示編譯成功。VisualStudioCode生成的ABI文件和字節(jié)碼文件6.4.4JSON-RPCJSON-RPC是一種無狀態(tài)的、輕量級的遠程過程訪問協(xié)議,使用JSON作為其數(shù)據(jù)格式。JSON-RPC可以通過curl方式來調(diào)用,具體方法如下:curl--data<JSON字符串><以太坊區(qū)塊鏈服務(wù)器地址>1.獲取幣基賬戶地址首先啟動CentOS虛擬機,執(zhí)行如下命令,啟動之前搭建好的以太坊私有鏈。cd/usr/local/go-ethereum/build/bin./geth--datadirethchain--nodiscoverconsole2-dev>>1.log--dev.period1--password'./password.txt'--rpcport8545--rpccorsdomain"*"-rpc--rpcapi="db,eth,net,web3,personal"獲取以太坊節(jié)點的幣基賬戶地址curl-H'Content-Type:application/json'--data'{"jsonrpc":"2.0","method":"eth_coinbase","id":1}'localhost:8545使用curl命令調(diào)用以太坊中的JSON-RPC接口的參數(shù)說明參
數(shù)具體說明-H'Content-Type:application/json'指定請求中的媒體類型為JSON。如果不指定此參數(shù),會報錯--data指定JSON字符串格式的命令"jsonrpc":"2.0"指定JSON-RPC的版本號為2.0"method":"eth_coinbase"指定在以太網(wǎng)私有鏈上執(zhí)行的方法。eth_coinbase方法用于獲取幣基賬戶的地址"id":1標識當(dāng)前命令的編號。在返回結(jié)果中也有一個同樣的id與命令匹配localhost:8545以太網(wǎng)私有鏈的地址執(zhí)行結(jié)果如下{"jsonrpc":"2.0","id":1,"result":"0xd169d4387c2dcd1ec4e6952bddf0a22c1f8c2b2f"}2.獲取當(dāng)前賬戶的余額curl-H'Content-Type:application/json'--data'{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xd169d4387c2dcd1ec4e6952bddf0a22c1f8c2b2f","latest"],"id":2}'localhost:85453.部署合約curl-H'Content-Type:application/json'--data'{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":<發(fā)起該筆
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- KTV清潔合同范本
- 出租電纜鋼板合同范本
- 個人賺取傭金合同范例
- 中介公租房合同范本
- 住家阿姨雇傭合同范本
- 農(nóng)村改造房出售合同范本
- 兄妹房屋出賣合同范例
- 產(chǎn)權(quán)代辦合同范本
- 專利權(quán)轉(zhuǎn)讓合同范本
- 創(chuàng)業(yè)辦公司合同范本
- 高效液相含量測定計算公式
- 六宮格數(shù)獨解題技巧
- 公安機關(guān)通用告知書模板
- 工程款支付審批流程圖
- 人教版七年級歷史下冊第一單元填空題
- 封頭重量和容積計算
- 《小學(xué)數(shù)學(xué)課程與教學(xué)》教學(xué)大綱
- 《手機攝影》全套課件(完整版)
- 彩色學(xué)生電子小報手抄報模板春節(jié)41
- 筒形件拉深成形工藝分析及模具設(shè)計
- JGJ_T231-2021建筑施工承插型盤扣式鋼管腳手架安全技術(shù)標準(高清-最新版)
評論
0/150
提交評論