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