以太坊交易生命周期
這篇文章總結(jié)以太坊上的交易如何被構(gòu)建和全網(wǎng)廣播。
交易是區(qū)塊鏈的核心。當(dāng)你和以太坊交易交互時,本質(zhì)上就是執(zhí)行一個交易并更改它的狀態(tài)。是否好奇在以太坊上交易被執(zhí)行的過程發(fā)生了什么嗎?讓我們通過一個例子來探討說明以太坊交易的本質(zhì),在這篇文章中我們會覆蓋以下知識點:
默認(rèn)讀者具備以太坊的基礎(chǔ)知識,譬如賬戶系統(tǒng)、gas 和合約等。如果你是一名開發(fā)者,推薦看文章 Ethereum for web developers ,簡單的投票 Dapp 開發(fā)教程
讀這篇文章的同時如果一邊執(zhí)行交易的話你應(yīng)該理解更通透 (有能力的程序員可以搭建私有鏈節(jié)點進行轉(zhuǎn)賬),比如把 eth 發(fā)給其他普通賬戶或者合約賬戶、投票 dapp 交互等操作都是一筆交易。
交易過程
通過調(diào)用合約解釋交易生命周期的全部流程。投票合約的源碼在 這里,整體而言,這是一個初始化一些參與競選的候選者,任何人都可以對候選者投票,最終投票結(jié)果永久不可篡改記錄在區(qū)塊鏈上。
Voting.deployed().then(function(instance) { instance.voteForCandidate('Nick', {gas: 140000, from: web3.eth.accounts[0]}).then(function(r) {console.log("Voted successfully!") }) })假設(shè)在你的電腦上已經(jīng)部署好以太坊客戶端 (geth 或 parity) 并連接到以太坊區(qū)塊鏈網(wǎng)絡(luò) (Testnet 或 Mainnet),通過合約地址和合約 ABI 就能調(diào)用合約中的函數(shù)。拿到合約對象之后調(diào)用 voteForCandidate 函數(shù)。
構(gòu)建原始交易對象
voteForCandidate 函數(shù)被調(diào)用之后首次生成原始交易:
txnCount = web3.eth.getTransactionCount(web3.eth.accounts[0]) var rawTxn = {nonce: web3.toHex(txnCount),gasPrice: web3.toHex(100000000000),gasLimit: web3.toHex(140000),to: '0x633296baebc20f33ac2e1c1b105d7cd1f6a0718b',value: web3.toHex(0),data: '0xc7ed014952616d6100000000000000000000000000000000000000000000000000000000' };逐個解釋原始交易中每個字段的意義
- nonce: 以太坊上每個賬戶都有一個 nonce 字段,用以標(biāo)記該賬戶發(fā)生交易的次數(shù)。賬戶中每發(fā)生一筆新交易,則 nonce 增加 1 ,與此同時區(qū)塊鏈網(wǎng)絡(luò)也能處理交易的執(zhí)行順序。nonce 還被用來重放保護。
What is a replay attack? Without a replay protection, when you, say send out 1 Bitcoin from the legacy chain, the transaction is also valid on the forked chain with the same amount of new coins and same recipient. Someone else can make use of this and send out your new coins without your agreement. This is the same case for the opposite direction: when you send out the new coins, you are potentially also sending out your Bitcoin!
- gasPrice:支付該筆交易每個單元 gas 的價格。
- gasLimit: 支付該筆交易最大數(shù)量的 gas 。該字段防止執(zhí)行交易時出現(xiàn)特殊情況(譬如合約出現(xiàn)無限循環(huán))導(dǎo)致賬戶余額被耗完,一旦交易完成,剩余的 gas 將會返回到你的賬戶。
- to:調(diào)用合約時這個字段是合約地址,普通交易時為目標(biāo)用戶地址。在這里是投票合約地址
- value:轉(zhuǎn)賬數(shù)量。在這里我們的目的是調(diào)用投票合約,故賦值為 0
- data:交易攜帶的信息,在這里有個 tip ,普通交易得到交易結(jié)果的 input data 通常為 0x, 合約交易結(jié)果該字段保護合約交易信息。通過這個字段能夠區(qū)別普通交易和合約交易。
進一步解釋 data 字段值的生成規(guī)則。
首先是被調(diào)用合約函數(shù)的散列之后獲取前面四個字節(jié),得到 0xcc9ab267
> web3.sha3('voteForCandidate(bytes32 candidate)') '0xc7ed014922ff9493a686391b70ca0e8bb7e80f91c98a5cd3d285778ab2e245b3'然后是被調(diào)用合約函數(shù)的參數(shù)值轉(zhuǎn)為 32 字節(jié),得到 52616d6100000000000000000000000000000000000000000000000000000000*
上述兩次得到的組合一起就是 data 字段的值。
簽名交易
web3.eth.accounts[0] 執(zhí)行交易,以太坊網(wǎng)絡(luò)需校驗交易發(fā)起者是否有效,通過私鑰簽名就能證明你有該賬戶余額的使用權(quán)。
const privateKey = Buffer.from('e331b6d69882b4ab4ea581s88e0b6s4039a3de5967d88dfdcffdd2270c0fd109', 'hex') const txn = new EthereumTx(rawTxn) txn.sign(privateKey) const serializedTxn = txn.serialize()本地節(jié)點校驗交易合法性
簽名后的交易被提交到你搭建的節(jié)點,節(jié)點會校驗被簽名的交易是否真的被對應(yīng)的私鑰簽名。
交易被全網(wǎng)廣播
一旦構(gòu)建的交易被你搭建的節(jié)點廣播到區(qū)塊鏈網(wǎng)絡(luò),本地節(jié)點就會返回交易 ID, 通過該散列可追蹤交易狀態(tài)
transactionId = sha3(serializedTxn)Mainnet 上的交易可在 http://etherscan.io 上查看詳情,如果你的交易被其他節(jié)點收到,在區(qū)塊瀏覽器上可以看到交易狀態(tài)為 pending 。本地廣播出去的交易并不會被所有的節(jié)點接收,這種情況發(fā)生在交易的 gas price 低于節(jié)點設(shè)置的最低 gas price 。
挖礦節(jié)點打包交易
礦工節(jié)點維護一個交易池,把收集到未被打包的交易按照 gas price 從高到低排列 (當(dāng)然排列規(guī)則是可配置的),然后打包生成一個區(qū)塊。交易池能夠保存的交易有上限,如果網(wǎng)絡(luò)區(qū)塊擁堵會導(dǎo)致手續(xù)費提高和低手續(xù)費的交易遲遲不能得到打包甚至?xí)坏V工丟棄,此時我們就要重新廣播交易。還有一個技巧讓被礦工從交易池丟棄的交易重新被打包:保持 nonce 不變同時提高 gas price 再重新廣播,礦工收到增加手續(xù)費的交易后,新交易會覆蓋被剔出交易池的交易,舊的交易將會失效。
出塊并全網(wǎng)廣播
礦工最終把我們構(gòu)建的交易和其他交易一并打包生成塊。以太坊協(xié)議通過設(shè)置塊的 gas limit 限制塊中的交易數(shù)量,塊中所有交易 gas limit 的總和加起來不能超過塊設(shè)置的 gas limit 。通過 ethstats.net 可查看當(dāng)前塊 gas limit 。
一旦礦工選擇把交易打包生成區(qū)塊,意味著這些交易成功被校驗,此時塊的狀態(tài)是 pending block ,接著礦工節(jié)點開始工作量證明計算。最終只有一個挖礦節(jié)點獲得區(qū)塊權(quán),并把 pending 塊追加到鏈上。廣播區(qū)塊就像我們節(jié)點廣播交易一樣,出塊的礦工把塊廣播到全網(wǎng)。
本地節(jié)點接收/同步最新區(qū)塊
本地節(jié)點接收到出塊礦工廣播的最新塊并同步,接收到新塊時,本地節(jié)點執(zhí)行塊中的所有事務(wù)。如果你使用 truffle 執(zhí)行交易,這個工具會不斷地輪訓(xùn)鏈上的數(shù)據(jù)判斷交易是否被確認(rèn),一旦收到確認(rèn)這段代碼就會被執(zhí)行:
.then(function(r) { console.log("Voted successfully!") })推薦閱讀
- What is transaction replay and replay protection?
- Life Cycle of an Ethereum Transaction
總結(jié)
- 上一篇: Java-gt;Android并发编程筑
- 下一篇: gRPC amp; Protocol B