【一步步一起學DApp開發】(四)web3.js 基本使用 | 連線geth | 建立web客戶端
概述
web3.js內部使用JSONRPC與geth通訊。它把所有JSON-RPC API當作JavaScript API,也就是說,它不僅支援所有與以太坊相關的API,還支援與Whisper和Swarm相關的API。
相關連結 web3.js託管地址 web3.js文件
匯入web3.js
為了在node.js中使用web3.js,可以在專案目錄中執行npm install web3
,且在原始碼中可以使用require("web3")
匯入它。
與節點連線
web3.js可以與使用HTTP或者IPC的節點通訊。我們將使用HTTP與節點建立通訊。web3.js允許與多個節點建立連線。一個web3例項代表與節點的一個連線。
當在Mist中執行一個App時,它自動使一個連線到mist節點的web3例項可用。例項變數名是web3。
連線到節點所使用的基礎程式碼:
if(typeof web3!=='undefined'){
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
解釋:
首先,通過檢查web3是否是undefined,來確定程式碼是否在Mist中執行。如果web3被定義了,則使用已經可用的例項;否則,通過連線至自定義節點建立一個例項。 Web3.providers物件使用多種協議顯示建構函式(在此稱為providers),以建立連線和傳輸資訊。Web3.providers.HttpProvider允許建立HTTP連線,Web3.providers.IpcProvider允許建立IPC連線。 web3.currentProvider屬性被自動分配給當前的provider例項。在建立web3例項之後,可使用web3.setProvider()方法改變provider。它有一個實參,即新provider的例項。
注意
geth預設禁用HTTP-RPC。所以在執行geth時通過–rpc選項以使用HTTP-RPC。HTTP-RPC預設在8545埠執行。
web3顯示isConnected()方法,可用於查詢是否已經與節點連線。 根據連線狀態的不同,返回true或者false。
API 結構
- web3包含一個eth物件(web3.eth),專門用於以太坊區塊鏈互動;
- 一個shh物件(web3.shh),用於whisper互動;
- 所有API都是默認同步的。如果想發出非同步請求,可以把一個可選回撥函式作為最後的引數傳送給大多數函式。所有回撥函式都採用錯誤優先(error-first)回撥方式。
BigNumber.js
由於JS本身對於處理大數字不在行,所以,web3.js依賴BigNumber.js進行大數字的處理與計算。
如:
web3.eth.getBalance("0x27E829fB34d14f338464F938165dfcD30FfB7c").toString();//使用web3.eth.getBalance()方法獲取地址餘額,該方法返回一個BigNumber物件
需要在BigNumber物件上呼叫toString(),把它轉換成數字字串。
注意: BigNumber.js不能正確處理有超過20個浮點數位的大數字,因此推薦以wei為單位儲存餘額,在顯示時再轉換成其他單位。web3.js自身總是以wei為單位返回和調取餘額。
單位換算
web3.js提供了把wei餘額轉換成任何其他單位和把任何其他單位餘額轉換成wei的API。
- web3.fromWei()方法用於將wei轉換成其他單位
- web3.toWei()方法用於將以其他單位表示的數字轉化成以wei為單位的數字
如:
web3.fromWei("1000000000000000","ether");//將wei轉換為ether
web3.toWei("0.0000000000000001","ether");//將ether轉換為wei
第二個實參可以是以下字串之一:
- kwei/ada
- mwei/babbage
- gwei/shannon
- szabo
- finney
- ether
- kether/grand/einstein
- mether
- gether
- tether
檢索gas價格、餘額和交易細節
- web3.eth.gasPrice():由x個最新區塊的gas價格中位數決定gas價格。
- web3.ethgetBalance():返回任何給定地址的餘額,給定的地址應當是十六進位制的字串。
- web3.eth.getTransactionReceipt():獲取交易使用其雜湊的細節。如果在區塊鏈中發現交易,則返回交易資料物件;否則,返回null。
交易資料物件包含下列屬性:
- blockHash:該交易所在區塊的雜湊地址。
- blockNumber:該交易所在區塊的序號。
- transactionHash:交易雜湊。
- transactionIndex:區塊中交易索引位置的整數部分
- from:發起人地址。
- to:接收者地址,如果是合約建立交易,則為null。
- cumulativeGasUsed:在區塊中執行該交易時使用的gas總量。
- gasUsed:這個特定交易獨自使用的gas量。
- contractAddress:如果交易是合約建立,表示被建立的合約地址,否則,為null。
- logs:該交易生成的日誌物件陣列。
傳送以太幣
為了傳送以太幣,需要使用web3.eth.sendTransaction()
方法。
該方法可用於傳送任意種類的交易,但主要用於傳送以太幣,原因是使用這種方法部署合約或者呼叫合約方法比較麻煩——它要求生成交易資料而不是自動生成交易資料。
該方法的交易物件包含下列屬性:
- from 傳送賬戶地址
- to 可選項。資訊目的地的地址,對於合約建立交易。
- value 可選項。通常在轉賬中單位為wei
- gas 可選,交易使用的gas量
- gasPrice 可選,交易中以wei為單位的gas價格,預設為網路平均gas價格
- data 可選,包含資訊相關資料的位元組字串
- nonce 可選,是個整數,每個交易都有一個相關計數nonce。該數字表示交易發起人傳送的交易數量。如果未提供nonce,則自動確定。它的作用是防止重播攻擊。如果使用的nonce大於交易應當有的nonce,則交易被放入一個佇列直到其他交易數量到達。例如,如果下一個交易的nonce應該是4,而nonce被設為10,則geth在廣播這個交易之前將等待之間的6個交易。nonce為10的交易稱為排隊交易,而不是待定交易。
示例-向一個地址傳送以太幣
var txnHash = web3.eth.sendTransaction({
from: web3.eth.accounts[0],
to : web3.eth.accounts[1],
value: web3.toWei("1","ether")
});
解釋: 這裡從賬戶0向賬戶1傳送一個以太幣。
處理合約
若要部署一個新合約或者獲取一個已部署合約的引用,首先需要使用web3.eth.contract()方法建立一個合約物件。該方法以合約ABI作為一個實參,並返回合約物件。
例如:建立合約物件
var proofContract = web3.eth.contract([{
//
}])
部署合約的示例:
var proof = proofContract.new({
from: web3.eth.accounts[0],
data: "0x606060405261068",
gas:"4700000"
},
function(e,contract){
if(e){
console.log("Error"+e);
}else if(contract.address!=undefined){
console.log("Contract Address:"+contract.address);
}else {
console.log("Txn Hash:"+Contract.transactionHash)
}
})
其中,new方法的呼叫是非同步的,所以如果成功建立和廣播交易,回撥函式將被呼叫兩次。第一次,廣播交易之後呼叫它;第二次,挖出交易之後呼叫它。如果不提供回撥函式,則proof變數的address屬性被設成undefined。挖出交易之後,address屬性將被設定。
在proof合約中,沒有建構函式,但是如果有建構函式,則建構函式實參應當放在new方法的開頭。傳送的物件包含from地址、合約位元組碼和使用的gas上限。這三個屬性必須存在,否則無法建立交易。該物件可以有被傳送給sendTransaction方法的物件所展示的屬性,但是這裡,data是合約位元組碼,且to屬性被忽略。
可以用at方法引用一個已經部署的合約:
var proof = proofContract.at("0xd45e43df3234sdfsdfa32423423432dfsf232");
如何傳送交易以呼叫合約方法:
proof.set.sendTransaction("Owner Name",
"e3jksdfk234j32k4j23l4234jkdsjfkdsjfkj989898989234mkdf",{
from: web3.eth.account[0],
},function(error,transactionHash){
if(!err)
console.log(transactionHash);
})
解釋:
這裡呼叫方法同名物件的sendTransaction
方法。被傳送給這個sendTransaction
方法的物件屬性與web3.eth.sendTransaction()
相同,只是data和to屬性被忽略了
如果想呼叫節點本地的方法,而非建立交易並廣播,則可使用call
而非sendTransaction。
var returnValue = proof.get.call("e3jksdfk234j32k4j23l4234jkdsjfkdsjfkj989898989234mkdf");
找到呼叫方法所需的gas,以便決定是否呼叫:estimateGas()方法
var returnValue = proof.get.estimateGas("e3jksdfk234j32k4j23l4234jkdsjfkdsjfkj989898989234mkdf");
檢索和監聽合約事件
在瞭解如何檢索和監聽事件之前,我們需要學習事件的索引引數。一個事件最多有三個引數可以有被索引(indexed)屬性。該屬性用於提示節點對它進行索引,這樣應用客戶端可以用匹配返回值來檢索事件。如果不使用indexed屬性,則必須檢索所有事件,並篩選出需要的那些事件。
監聽合約事件示例:
var event = proof.logFileAddedStatus(null,{
fromBlock:0,
toBlock:"latest"
});
event.get(function(error,result){
if(!error){
console.log(result)
}
else {
console.log(error)
}
})
event.watch(function(error,result){
if(!error){
console.log(result.args.status)
}else {
console.log(error)
}
})
setTimeout(function(){
event.stopWatching();
},60000)
var events = proof.allEvents({
fromBlock:0,
toBlock:"latest"
});
events.get(function(error,result){
if(!error){
console.log(result)
}else {
console.log(error)
}
})
events.watch(function(error,result){
if(!error){
console.log(result.args.status);
}else {
console.log(error)
}
})
setTimeout(function(){
events.stopWatching();
},60000);
解析:
- 呼叫一個合約例項的事件同名的方法獲取事件物件。該方法用兩個物件作為實參,用於篩選事件:
第一個物件用索引返回數值篩選事件。例如,{‘valueA’:1,’valueB’:[myFirstAddress,mySecondAddress]}。所有篩選數值都預設設定為null。這意味著它們將匹配該合約發出的任意型別事件。
第二個物件可以包含三個屬性,即fromBlock(搜尋起始區塊,預設為”latest”)、toBlock(搜尋截至區塊,預設為”latest”)和address(僅獲取日誌的地址列表;預設為合約地址)。
- 事件物件顯示三種方法:get、watch和stopWatching。get用於獲取區塊範圍內的所有事件。watch與get類似,但是它在獲取事件後還監聽變化。stopWatching可以用於停止監聽變化。
- 合約例項的allEvents方法用於檢索合約的所有事件。
- 每一個事件由一個包含下列屬性的物件代表。
args:一個帶有來自事件的實參的物件。 event:用一個字串表示事件名。 logIndex:用一個整數表示區塊中的日誌索引位置。 transactionIndex:用一個整數表示日誌最初的交易索引位置。 transactionHash:用一個字串表示日誌最初的交易雜湊。 address:用一個字串表示日誌最初的地址。 blockHash:用一個字串表示日誌所在區塊的雜湊。如待定,則為null。 blockNumber:日誌所在區塊的序號。如待定,則為null
為所有權合約建立客戶端
首要我們要知道這是什麼樣個客戶端?
在客戶端中,使用者從中選擇一個檔案,輸入所有者細節,然後按下Submit按鈕廣播交易,用檔案雜湊和所有者的細節呼叫合約的set方法。一旦交易被成功廣播,將顯示交易雜湊。使用者還能夠選擇一個檔案,並從智慧合約中得到所有者的細節。客戶端還將實時顯示最新挖出的set交易。
思路:
在前端使用sha1.js獲取檔案雜湊,使用jQuery進行DOM操縱,並使用Bootstrap 4建立一個反應層(responsive layout)。在後端使用express.js和web3.js。我們將使用socket.io,這樣不需要前端間隔相等的時間請求資料,後端就把最近挖出的交易推到前端。