原文:Transactions
交易是由外部擁有的賬戶發(fā)起的簽名消息,由以太坊網(wǎng)絡(luò)傳輸,并記錄(挖掘)在以太坊區(qū)塊鏈上。在這個(gè)基本定義的背后,有許多令人驚訝和迷人的細(xì)節(jié)。查看交易的另一種方法是,它們是唯一可以觸發(fā)狀態(tài)更改或?qū)е潞霞s在EVM中執(zhí)行的事物。以太坊是一個(gè)全局單例狀態(tài)機(jī),交易是唯一可以使?fàn)顟B(tài)機(jī)“tick”,改變其狀態(tài)的東西。合約不是自己運(yùn)行的。以太坊不會(huì)“在后臺(tái)”運(yùn)行。一切都始于交易。
在本節(jié)中,我們將剖析交易,展示它們的工作方式,并了解細(xì)節(jié)。
交易結(jié)構(gòu)
首先讓我們看一下交易的基本結(jié)構(gòu),因?yàn)樗窃谝蕴痪W(wǎng)絡(luò)上序列化和傳輸?shù)?。接收序列化交易的每個(gè)客戶端和應(yīng)用程序?qū)⑹褂闷渥约旱膬?nèi)部數(shù)據(jù)結(jié)構(gòu)將其存儲(chǔ)在內(nèi)存中,可能使用在網(wǎng)絡(luò)序列化交易本身中不存在的元數(shù)據(jù)進(jìn)行修飾。因此,交易的網(wǎng)絡(luò)序列化是交易結(jié)構(gòu)的唯一通用標(biāo)準(zhǔn)。
交易是包含以下數(shù)據(jù)的序列化二進(jìn)制消息:
- nonce:由發(fā)起人EOA發(fā)出的序列號(hào),用于防止重播消息。
- gas price:發(fā)起人愿意支付的gas價(jià)格(wei)。
- start gas:發(fā)起人愿意支付的最大gas量。
- to:目的地以太坊地址。
- value:要發(fā)送到目的地的以太數(shù)量。
- data:可變長(zhǎng)度二進(jìn)制數(shù)據(jù)負(fù)載。
- v,r,s:發(fā)起人EOA的ECDSA簽名的三個(gè)組成部分。
交易消息的結(jié)構(gòu)使用遞歸長(zhǎng)度前綴(RLP)編碼方案(參見[rlp])進(jìn)行序列化,該方案專為在以太坊中準(zhǔn)確和字節(jié)完美的數(shù)據(jù)序列化而創(chuàng)建。以太坊中的所有數(shù)字都被編碼為big-endian,長(zhǎng)度為8位的倍數(shù)。
注意,為了清楚起見,這里示出了字段標(biāo)簽(“to”,“start gas”等),但不是交易序列化數(shù)據(jù)的一部分,其包含RLP編碼的字段值。通常,RLP不包含任何字段分隔符或標(biāo)簽。RLP的長(zhǎng)度前綴用于標(biāo)識(shí)每個(gè)字段的長(zhǎng)度。因此,超出定義長(zhǎng)度的任何內(nèi)容都屬于結(jié)構(gòu)中的下一個(gè)字段。
雖然這是傳輸?shù)膶?shí)際交易結(jié)構(gòu),但大多數(shù)內(nèi)部表示和用戶界面可視化都通過從交易或區(qū)塊鏈派生的附加信息來修飾。
例如,您可能會(huì)注意到標(biāo)識(shí)發(fā)起者EOA的地址中沒有“from”數(shù)據(jù)。EOA的公鑰可以很容易地從ECDSA簽名的v,r,s組件中獲得。反過來,地址可以很容易地從公鑰中導(dǎo)出。當(dāng)您看到顯示“from”字段的交易時(shí),該交易由用于可視化交易的軟件添加??蛻舳塑浖?jīng)常添加到交易中的其他元數(shù)據(jù)包括區(qū)塊號(hào)(一旦被挖掘)和交易ID(計(jì)算的哈希)。同樣,此數(shù)據(jù)源自交易,而不是交易消息本身的一部分。
交易中的nonce
nonce是交易中最重要和最不理解的組件之一。黃色紙的定義是:
nonce:一個(gè)標(biāo)量值,等于從這個(gè)地址發(fā)送的交易數(shù),或者,對(duì)于關(guān)聯(lián)code的帳戶,這個(gè)帳戶創(chuàng)建合約的數(shù)量。
嚴(yán)格的說,nonce是發(fā)送地址的屬性(它只在發(fā)送地址的上下文中有意義)。但是,nonce不會(huì)明確存儲(chǔ)為區(qū)塊鏈中帳戶狀態(tài)的一部分。相反,它是通過計(jì)算發(fā)送地址的已確認(rèn)交易的數(shù)量來動(dòng)態(tài)計(jì)算的。
nonce值還用于防止錯(cuò)誤計(jì)算賬戶余額。例如,假設(shè)一個(gè)賬戶的余額為10以太,并簽署兩個(gè)交易,每個(gè)交易花費(fèi)6個(gè)以太,分別為nonce 1和nonce 2。這兩項(xiàng)交易中哪一項(xiàng)有效?在像以太坊這樣的分布式系統(tǒng)中,節(jié)點(diǎn)可能不按順序接收交易。nonce強(qiáng)制來自任何地址的交易按順序處理,沒有間隔,無論節(jié)點(diǎn)接收它們的順序如何。這樣,所有節(jié)點(diǎn)都計(jì)算出相同的余額。使用nonce 1支付6 ether的交易將成功處理,將賬戶余額減少到4以太。用nonce 2支付6 ether的交易將被所有節(jié)點(diǎn)看作無效,無論它何時(shí)被接收。
使用nonce確保所有節(jié)點(diǎn)計(jì)算相同的余額和正確的序列交易,等同于用于防止比特幣“雙重支付”的機(jī)制。但是,由于以太坊跟蹤賬戶余額并且不單獨(dú)跟蹤貨幣(在比特幣中稱為UTXO),因此只有在錯(cuò)誤地計(jì)算賬戶余額時(shí)才會(huì)發(fā)生“雙重支付”。nonce機(jī)制可以防止這種情況發(fā)生。
追蹤nonces
實(shí)際上,nonce是源自帳戶的已確認(rèn)(已開采)交易數(shù)量的最新計(jì)數(shù)。要找出nonce是什么,你可以查詢區(qū)塊鏈,例如通過web3接口:
檢索示例地址的交易計(jì)數(shù)
web3.eth.getTransactionCount( “0x9e713963a92c02317a681b9bb3065a8249de124f”)
40
| Tip | nonce是一個(gè)從零開始的計(jì)數(shù)器,這意味著第一個(gè)交易具有nonce 0.在檢索我們的示例地址的交易計(jì)數(shù)時(shí),我們的交易計(jì)數(shù)為40,這意味著已經(jīng)看到了nonce 0到39。下一筆交易的nonce將是40。 |
|---|
您的錢包將跟蹤其管理的每個(gè)地址的nonce。這樣做相當(dāng)簡(jiǎn)單,只要您只從一個(gè)點(diǎn)發(fā)起交易即可。假設(shè)您正在編寫自己的錢包軟件或其他一些發(fā)起交易的應(yīng)用程序。你如何跟蹤nonces?
創(chuàng)建新交易時(shí),將在序列中分配下一個(gè)nonce。但在確認(rèn)之前,它不會(huì)計(jì)入getTransactionCount總計(jì)。
不幸的是,如果我們連續(xù)發(fā)送一些交易,getTransactionCount函數(shù)將遇到一些問題。有一個(gè)已知的錯(cuò)誤,getTransactionCount沒有正確計(jì)算待處理的交易。我們來看一個(gè)例子:
web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")
40
web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")});
web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")
41
web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")});
web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")
41
web3.eth.sendTransaction({from: web3.eth.accounts[0], to: "0xB0920c523d582040f2BCB1bD7FB1c7C1ECEbdB34", value: web3.toWei(0.01, "ether")});
web3.eth.getTransactionCount("0x9e713963a92c02317a681b9bb3065a8249de124f", "pending")
41
如您所見,我們發(fā)送的第一筆交易將交易計(jì)數(shù)增加到41,顯示待處理的交易。但是當(dāng)我們快速連續(xù)發(fā)送3個(gè)以上的交易時(shí),getTransactionCount調(diào)用沒有正確計(jì)算它們。它只計(jì)算了一個(gè),即使在mempool中有3個(gè)待處理的交易。如果我們等待幾秒鐘,一旦區(qū)塊被挖掘,getTransactionCount調(diào)用將返回正確的數(shù)字。但在此期間,雖然有多個(gè)待處理的交易,但對(duì)我們沒有幫助。
實(shí)施構(gòu)建交易的應(yīng)用程序時(shí),它不能依賴getTransactionCount來處理待處理的交易。只有當(dāng)待處理和確認(rèn)相等(所有未完成的交易都已確認(rèn))時(shí),您才能信任getTransactionCount的輸出以啟動(dòng)您的nonce計(jì)數(shù)器。此后,在每個(gè)交易確認(rèn)之前跟蹤應(yīng)用程序中的nonce。
Parity的JSON RPC接口提供了parity_nextNonce函數(shù),該函數(shù)返回應(yīng)在交易中使用的下一個(gè)nonce。parity_nextNonce函數(shù)正確計(jì)算nonce,即使您快速連續(xù)構(gòu)造多個(gè)交易,也不確認(rèn)它們。
Parity有一個(gè)用于訪問JSON RPC接口的Web控制臺(tái),但在這里我們使用命令行HTTP客戶端來訪問它:
curl --data '{"method":"parity_nextNonce","params":["0x9e713963a92c02317a681b9bb3065a8249de124f"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545
{"jsonrpc":"2.0","result":"0x32","id":1}
nonces,重復(fù)nonce和確認(rèn)間隔
如果以編程方式創(chuàng)建交易,則跟蹤nonce非常重要,尤其是如果您同時(shí)從多個(gè)獨(dú)立進(jìn)程執(zhí)行此操作。
以太坊網(wǎng)絡(luò)基于nonce順序處理交易。這意味著如果您使用nonce 0傳輸交易,然后使用nonce 2傳輸交易,則不會(huì)挖掘第二個(gè)交易。它將存儲(chǔ)在mempool中,而以太坊網(wǎng)絡(luò)則等待丟失的nonce出現(xiàn)。所有節(jié)點(diǎn)都將假設(shè)缺少的nonce已被簡(jiǎn)單地延遲,并且具有nonce 2的交易是無序接收的。
如果您隨后使用缺失的nonce 1傳輸交易,則將挖掘兩個(gè)交易(nonce 1和2)。一旦填補(bǔ)了空白,網(wǎng)絡(luò)就可以挖掘它在mempool中保存的無序交易。
這意味著如果您按順序創(chuàng)建多個(gè)交易并且其中一個(gè)交易沒有被挖掘,則所有后續(xù)交易都將“卡住”,等待缺少的nonce。交易可以在nonce序列中產(chǎn)生無意的“間隔”,因?yàn)樗鼰o效或者氣體不足。為了讓事情再次運(yùn)轉(zhuǎn)起來,您必須使用缺少的nonce傳輸有效的交易。
另一方面,如果您意外復(fù)制了一個(gè)nonce,例如通過發(fā)送具有相同nonce但具有不同收件人或值的兩個(gè)交易,則其中一個(gè)將被確認(rèn),一個(gè)將被拒絕。確認(rèn)哪一個(gè)將由它們到達(dá)接收它們的第一個(gè)驗(yàn)證節(jié)點(diǎn)的順序確定。
正如您所看到的,跟蹤nonce是必要的,如果您的應(yīng)用程序無法正確管理該過程,您將遇到問題。不幸的是,如果您嘗試同時(shí)執(zhí)行此操作會(huì)變得更加困難,我們將在下一節(jié)中看到。
并發(fā),交易發(fā)起和nonce
并發(fā)性是計(jì)算機(jī)科學(xué)的一個(gè)復(fù)雜方面,有時(shí)會(huì)出乎意料地出現(xiàn),尤其是在像以太坊這樣的分散/分布式實(shí)時(shí)系統(tǒng)中。
簡(jiǎn)單來說,并發(fā)性是指您可以通過多個(gè)獨(dú)立系統(tǒng)同時(shí)進(jìn)行計(jì)算。這些可以在相同的程序(例如線程)中,在相同的CPU上(例如,多處理),或在不同的計(jì)算機(jī)上(即,分布式系統(tǒng))。根據(jù)定義,以太坊是一個(gè)允許操作(節(jié)點(diǎn),客戶端,DApps)并發(fā)的系統(tǒng),但強(qiáng)制執(zhí)行單例狀態(tài)(例如,每個(gè)挖掘的區(qū)塊只有一個(gè)系統(tǒng)的公共/共享狀態(tài))。
現(xiàn)在,假設(shè)我們有多個(gè)獨(dú)立的錢包應(yīng)用程序,它們使用相同的地址生成交易。這種情況的一個(gè)例子是熱錢包的交易所處理提款。理想情況下,您希望有多個(gè)計(jì)算機(jī)處理提款,因此它不會(huì)成為堵塞或單點(diǎn)故障。然而,這很快就會(huì)成為問題,因?yàn)橛卸嗯_(tái)計(jì)算機(jī)產(chǎn)生提款將導(dǎo)致一些棘手的并發(fā)問題,其中最重要的是選擇nonce。多臺(tái)計(jì)算機(jī)如何協(xié)調(diào)從同一個(gè)熱錢包帳戶生成,簽名和廣播交易?
您可以使用一臺(tái)計(jì)算機(jī)以先到先得的方式將nonce分配給計(jì)算機(jī)簽名交易。但是,這臺(tái)計(jì)算機(jī)現(xiàn)在是一個(gè)單點(diǎn)故障。更糟糕的是,如果分配了幾個(gè)nonce并且其中一個(gè)永遠(yuǎn)不會(huì)被使用(由于計(jì)算機(jī)處理與該nonce的交易的失?。?,所有后續(xù)的都會(huì)被卡住。
您可以生成交易,但不要對(duì)它們進(jìn)行簽名或?yàn)樗鼈兎峙鋘once。然后將它們排隊(duì)到一個(gè)標(biāo)記它們的節(jié)點(diǎn),并跟蹤nonce。同樣,你有一個(gè)單點(diǎn)的失敗。對(duì)nonce的簽名和跟蹤是您的操作的一部分,可能會(huì)在負(fù)載下變得擁擠,而無符號(hào)交易的生成是您不需要并行化的部分。您有并發(fā)性,但在流程的任何有用部分都沒有它。
最后,除了在獨(dú)立進(jìn)程中跟蹤帳戶余額和交易確認(rèn)的難度之外,這些并發(fā)問題迫使大多數(shù)實(shí)現(xiàn)避免并發(fā)和創(chuàng)建瓶頸,例如處理交易所中的所有提款交易的單個(gè)流程。
交易中的gas
我們?cè)赱gas]中詳細(xì)討論了gas。但是,讓我們介紹一下有關(guān)交易的gasPrice和startGas組件的作用的一些基礎(chǔ)知識(shí)。
gas是以太坊的燃料。gas不是以太 - 它是一種單獨(dú)的虛擬貨幣,匯率與以太成比例。以太坊使用gas來控制交易可以花費(fèi)的資源量,因?yàn)樗鼘⒃谌驍?shù)千臺(tái)計(jì)算機(jī)上處??理。開放式(圖靈完成)計(jì)算模型需要某種形式的計(jì)量,以避免拒絕服務(wù)攻擊或無意的資源吞噬交易。
gas和ether分離,以保護(hù)系統(tǒng)免受隨著ether值的快速變化而可能出現(xiàn)的波動(dòng)。
交易中的gasPrice字段允許交易發(fā)起人設(shè)置每個(gè)單位gas的匯率。gas價(jià)格以每單位gas計(jì)量。例如,在我們最近為本書中的一個(gè)例子創(chuàng)建的交易中,我們的錢包將gasPrice設(shè)置為3 Gwei(3 Giga-wei,30億wei)。
熱門網(wǎng)站ethgasstation.info提供有關(guān)gas當(dāng)前價(jià)格的信息,以及以太坊主網(wǎng)絡(luò)的其他相關(guān)gas指標(biāo):
錢包可以在它們發(fā)起的交易中調(diào)整gasPrice,以實(shí)現(xiàn)更快的交易確認(rèn)(挖掘)。gasPrice越高,交易可能越快確認(rèn)。相反,優(yōu)先級(jí)較低的交易可以降低他們?cè)敢鉃間as支付的價(jià)格,從而導(dǎo)致確認(rèn)速度變慢??稍O(shè)置的最低gasPrice為零,這意味著免費(fèi)交易。在區(qū)塊中對(duì)空間的低需求期間,此類交易將被挖掘。
| Tip | 最低可接受的gasPrice為零。這意味著錢包可以生成完全免費(fèi)的交易。根據(jù)容量,這些可能永遠(yuǎn)不會(huì)被挖掘,但協(xié)議中沒有禁止自由交易的內(nèi)容。您可以在以太坊區(qū)塊鏈中找到成功挖掘此類交易的幾個(gè)示例。 |
|---|
web3界面提供了gasPrice建議,通過計(jì)算幾個(gè)區(qū)塊的中間價(jià)格:
truffle(mainnet)> web3.eth.getGasPrice(console.log)
truffle(mainnet)> null BigNumber {s:1,e:10,c:[10000000000]}
與gas有關(guān)的第二個(gè)重要領(lǐng)域是startGas。這在[gas]中有更詳細(xì)的解釋。簡(jiǎn)單來說,startGas定義了交易發(fā)起人愿意花費(fèi)多少單位的gas來完成交易。對(duì)于簡(jiǎn)單付款,意味著將ether從一個(gè)EOA轉(zhuǎn)移到另一個(gè)EOA的交易,所需的燃?xì)饬抗潭?1,000個(gè)燃?xì)鈫挝?。要?jì)算將花費(fèi)多少以太,您需要將21,000乘以您愿意支付的gasPrice:
truffle(mainnet)> web3.eth.getGasPrice(function(err, res) {console.log(res*21000)} )
truffle(mainnet)> 210000000000000
如果您的交易的接收地址是合約,那么可以估算所需的gas量,但無法準(zhǔn)確確定。這是因?yàn)楹霞s可以評(píng)估不同的條件,導(dǎo)致不同的執(zhí)行路徑,不同的gas成本。這意味著合約可能只執(zhí)行一個(gè)簡(jiǎn)單的計(jì)算或更復(fù)雜的計(jì)算,這取決于你無法控制和無法預(yù)測(cè)的條件。為了證明這一點(diǎn),讓我們使用一個(gè)精心設(shè)計(jì)的例子:每次調(diào)用一個(gè)合約時(shí),它會(huì)遞增一個(gè)計(jì)數(shù)器,并且在第100次(僅)計(jì)算一些復(fù)雜的東西。如果你調(diào)用合約99次就會(huì)發(fā)生一件事,但是在第100次調(diào)用時(shí)會(huì)發(fā)生一些完全不同的事情。您要支付的gas量取決于在開采交易之前有多少其他交易已經(jīng)調(diào)用該函數(shù)。也許您的估算是基于第99次交易,就在您的交易被開采之前,其他人第99次調(diào)用了合約?,F(xiàn)在你是第100個(gè)要調(diào)用的交易,計(jì)算工作量(和gas成本)要高得多。
借用以太坊中使用的常用類比,您可以將startGas視為汽車中的油箱(您的汽車就是交易)。您可以使用您認(rèn)為旅程所需的氣體(為驗(yàn)證交易所需的計(jì)算)填充油箱。您可以在一定程度上估算金額,但您的旅程可能會(huì)出現(xiàn)意外變化,例如轉(zhuǎn)移(更復(fù)雜的執(zhí)行路徑),這會(huì)增加燃油消耗。
然而,與油箱的類比有些誤導(dǎo)。它更像是一家加油站公司的信用賬戶,根據(jù)您實(shí)際使用的燃?xì)饬浚梢栽诼眯薪Y(jié)束后付款。當(dāng)您發(fā)起交易時(shí),第一個(gè)驗(yàn)證步驟之一是檢查它發(fā)送的帳戶是否有足夠以支付gasPrice * startGas的費(fèi)用。但是,在交易執(zhí)行結(jié)束之前,實(shí)際上并未從您的帳戶中扣除金額。您只需支付最終交易實(shí)際消耗的gas費(fèi)用,但在發(fā)送交易之前,您必須有足夠的余額支付您愿意支付的最高金額。
交易的接收者
交易接收者在to字段中指定。這包含一個(gè)20字節(jié)的以太坊地址。地址可以是EOA或合約地址。
以太坊沒有進(jìn)一步驗(yàn)證該領(lǐng)域。任何20字節(jié)的值都被認(rèn)為是有效的。如果20字節(jié)值對(duì)應(yīng)于沒有相應(yīng)私鑰的地址,或沒有相應(yīng)的合約,則該交易仍然有效。以太坊無法知道地址是否是從公鑰(因此來自私鑰)正確派生的。
| Warning | 以太坊不能也不會(huì)驗(yàn)證交易中的接收者地址。您可以發(fā)送到?jīng)]有相應(yīng)私鑰或合約的地址,從而“銷毀”以太,使其永遠(yuǎn)丟失。驗(yàn)證應(yīng)在用戶界面級(jí)別完成。 |
|---|
將交易發(fā)送到無效地址將銷毀發(fā)送的以太,使其永遠(yuǎn)無法訪問(不可連接),因?yàn)闊o法生成簽名來使用它。假設(shè)地址驗(yàn)證發(fā)生在用戶界面級(jí)別(參見[eip-55]或[icap])。事實(shí)上,銷毀以太有很多正當(dāng)理由,包括作為一種博弈論,用來抑制在支付渠道和其他智能合同中作弊。
交易的Value和Data
交易的主要“有效負(fù)載”包含在兩個(gè)字段中:value和data。交易可以同時(shí)具有value和data,僅具有value,僅具有data,或者既不具有value也不具有data。所有四種組合都有效。
僅具有value的交易是付款。僅包含data的交易是調(diào)用。既沒有value也沒有data的交易,這可能只是浪費(fèi)gas!但它仍然有可能。
讓我們嘗試以上所有組合:
首先,我們?cè)O(shè)置錢包中的發(fā)送地址和接收地址,只是為了讓演示更容易閱讀:
設(shè)置發(fā)送和接收地址
src = web3.eth.accounts[0];
dst = web3.eth.accounts[1];
僅具有value的交易
web3.eth.sendTransaction({from: src, to: dst, value: web3.toWei(0.01, "ether"), data: ""});

圖1,Parity錢包顯示一筆有value沒有data的交易
同時(shí)具有value和data的交易
web3.eth.sendTransaction({from: src, to: dst, value: web3.toWei(0.01, "ether"), data: "0x1234"});

圖2,Parity錢包顯示一筆有value也有data的交易
僅包含data的交易
web3.eth.sendTransaction({from: src, to: dst, value: 0, data: "0x1234"});

圖3,Parity錢包顯示一筆沒有value有data的交易
既沒有value也沒有data的交易
web3.eth.sendTransaction({from: src, to: dst, value: 0, data: ""}));

圖4,Parity錢包顯示一筆沒有value也沒有data的交易
向EOA和合約傳遞value
當(dāng)您構(gòu)建包含value的以太坊交易時(shí),它相當(dāng)于付款。根據(jù)接收地址是否為合約,這些交易的行為會(huì)有所不同。
對(duì)于EOA地址,或者更確切地說,對(duì)于未在區(qū)塊鏈中注冊(cè)為合約的任何地址,以太坊將記錄狀態(tài)更改,并將您發(fā)送的value添加到地址的余額中。如果以前沒有看到該地址,則會(huì)創(chuàng)建該地址并將其余額初始化為您的付款金額。
如果接收地址(to)是合約,則EVM將執(zhí)行合約并嘗試調(diào)用交易的數(shù)據(jù)有效負(fù)載中指定的函數(shù)(請(qǐng)參閱[invocation])。如果您的交易中沒有數(shù)據(jù)有效負(fù)載,則EVM將調(diào)用目標(biāo)合約的fallback功能,如果該功能需要支付,則將執(zhí)行該功能以確定下一步操作。
合約可以通過在調(diào)用payable函數(shù)時(shí)立即拋出異常,或者由payable函數(shù)中編碼的條件確定,來拒絕收款。如果payable函數(shù)成功終止(沒有例外),則更新合約的狀態(tài)以反映合約的以太余額的增加。
向EOA和合約傳遞數(shù)據(jù)有效負(fù)載
當(dāng)您的交易包含數(shù)據(jù)有效負(fù)載時(shí),它很可能發(fā)送到合同地址。這并不意味著您無法將數(shù)據(jù)有效負(fù)載發(fā)送到EOA。事實(shí)上,你可以做到這一點(diǎn)。但是,在這種情況下,數(shù)據(jù)有效負(fù)載的解釋取決于您用于訪問EOA的錢包。大多數(shù)錢包會(huì)忽略交易中收到的任``何數(shù)據(jù)有效負(fù)載,而不是他們控制的EOA。將來,有可能出現(xiàn)允許錢包以合約的方式解釋數(shù)據(jù)有效負(fù)載編碼的標(biāo)準(zhǔn),從而允許交易調(diào)用在用戶錢包內(nèi)運(yùn)行的功能。關(guān)鍵的區(qū)別在于,與合約執(zhí)行不同,EOA對(duì)數(shù)據(jù)有效負(fù)載的任何解釋都不受以太坊的共識(shí)規(guī)則約束。
現(xiàn)在,我們假設(shè)您的交易正在向合約地址提供數(shù)據(jù)有效負(fù)載。在這種情況下,數(shù)據(jù)有效負(fù)載將由EVM解釋為函數(shù)調(diào)用,調(diào)用命名函數(shù)并將任何編碼參數(shù)傳遞給函數(shù)。
發(fā)送給合約的數(shù)據(jù)有效負(fù)載是十六進(jìn)制序列化編碼:
- 函數(shù)選擇器:函數(shù)原型的Keccak256哈希的前4個(gè)字節(jié)。這允許EVM明確地識(shí)別您要調(diào)用的函數(shù)。
- 函數(shù)參數(shù):根據(jù)EVM定義的各種基本類型的規(guī)則進(jìn)行編碼。
讓我們看一個(gè)簡(jiǎn)單的例子,它來自我們的[solidity_faucet_example]。在Faucet.sol中,我們?yōu)樘峥疃x了一個(gè)函數(shù):
function withdraw(uint withdraw_amount) public {
withdraw函數(shù)的原型定義為包含函數(shù)名稱的字符串,后跟括號(hào)中括起的每個(gè)參數(shù)的數(shù)據(jù)類型,并用單個(gè)逗號(hào)分隔。函數(shù)名是withdraw,它接受一個(gè)uint的參數(shù)(這是uint256的別名)。所以withdraw的原型將是:
withdraw(unit256)
讓我們計(jì)算一下這個(gè)字符串的Keccak256哈希值(我們可以使用tuffle控制臺(tái)或任何JavaScript web3控制臺(tái)來做到這一點(diǎn)):
web3.sha3("withdraw(uint256)");
'0x2e1a7d4d13322e7b96f9a57413e1525c250fb7a9021cf91d1540d5b69f16a49f'
哈希的前4個(gè)字節(jié)是0x2e1a7d4d。這是我們的“函數(shù)選擇器”值,它將告訴EVM我們想要調(diào)用哪個(gè)函數(shù)。
接下來,讓我們計(jì)算一個(gè)值作為withdraw_amount參數(shù)。我們想要提取0.01以太。讓我們將其編碼為十六進(jìn)制序列的大端無符號(hào)256位整數(shù),以wei命名:
withdraw_amount = web3.toWei(0.01, "ether");
'10000000000000000'
withdraw_amount_hex = web3.toHex(withdraw_amount);
'0x2386f26fc10000'
現(xiàn)在,我們將函數(shù)選擇器添加到金額(填充到32個(gè)字節(jié)):
2e1a7d4d000000000000000000000000000000000000000000000000002386f26fc10000
這是我們交易的數(shù)據(jù)有效負(fù)載,調(diào)用withdraw函數(shù)并請(qǐng)求0.01 ether作為withdraw_amount。
特殊交易:合約注冊(cè)
有一個(gè)具有數(shù)據(jù)有效負(fù)載且沒有value的交易的特例。那就是一個(gè)注冊(cè)新合約的交易。合約注冊(cè)交易被發(fā)送到特殊目的地地址,即零地址。簡(jiǎn)單來說,合約注冊(cè)交易中的to字段包含地址0x0。該地址既不代表EOA(沒有相應(yīng)的私鑰/公鑰對(duì))也不代表合約。它永遠(yuǎn)不會(huì)花費(fèi)以太或發(fā)起交易。它僅用作目的地,具有特殊含義“注冊(cè)此合約”。
雖然零地址僅用于合同注冊(cè),但它有時(shí)會(huì)收到來自各種地址的付款。對(duì)此有兩種解釋:要么是偶然的,導(dǎo)致失去以太;要么是故意銷毀以太。如果您想進(jìn)行有意的以太銷毀,您應(yīng)該明確網(wǎng)絡(luò)意圖并使用專門指定的地址:
0x000000000000000000000000000000000000dEaD
| Warning | 發(fā)送到合同合約地址0x0或上面指定的地址0x0 ... dEaD的任何以太將變得不可靠并永遠(yuǎn)丟失。 |
|---|
合約注冊(cè)交易不應(yīng)包含以太值,只包含合約的已編譯字節(jié)碼的數(shù)據(jù)有效負(fù)載。此交易的唯一效果是注冊(cè)合約。
例如,我們可以發(fā)布[intro]中使用的Faucet.sol。合約需要編譯成二進(jìn)制十六進(jìn)制表示。這可以使用Solidity編譯器完成。
> solc --bin Faucet.sol
======= Faucet.sol:Faucet =======
Binary:
6060604052341561000f57600080fd5b60e58061001d6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632e1a7d4d146041575b005b3415604b57600080fd5b605f60048080359060200190919050506061565b005b67016345785d8a00008111151515607757600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151560b657600080fd5b505600a165627a7a72305820d276ddd56041f7dc2d2eab69f01dd0a0146446562e25236cf4ba5095d2ee802f0029
也可以從Remix在線編譯器獲得相同的信息?,F(xiàn)在我們可以創(chuàng)建交易了。
> src = web3.eth.accounts[0];
> faucet_code = "0x6060604052341561000f57600080fd5b60e58061001d6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632e1a7d4d146041575b005b3415604b57600080fd5b605f60048080359060200190919050506061565b005b67016345785d8a00008111151515607757600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151560b657600080fd5b505600a165627a7a72305820d276ddd56041f7dc2d2eab69f01dd0a0146446562e25236cf4ba5095d2ee802f0029"
> web3.eth.sendTransaction({from: src, data: faucet_code, gas: 113558, gasPrice: 200000000000})
"0x7bcc327ae5d369f75b98c0d59037eec41d44dfae75447fd753d9f2db9439124b"
無需指定to參數(shù),將使用默認(rèn)的零地址。您可以指定gasPrice和gas limit。合約被注冊(cè)我們可以在etherscan block explorer上看到它

圖5. Etherscan顯示合約成功
您可以查看交易收據(jù)以獲取有關(guān)合約的信息。
> eth.getTransactionReceipt("0x7bcc327ae5d369f75b98c0d59037eec41d44dfae75447fd753d9f2db9439124b");
{
blockHash: "0x6fa7d8bf982490de6246875deb2c21e5f3665b4422089c060138fc3907a95bb2",
blockNumber: 3105256,
contractAddress: "0xb226270965b43373e98ffc6e2c7693c17e2cf40b",
cumulativeGasUsed: 113558,
from: "0x2a966a87db5913c1b22a59b0d8a11cc51c167a89",
gasUsed: 113558,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: null,
transactionHash: "0x7bcc327ae5d369f75b98c0d59037eec41d44dfae75447fd753d9f2db9439124b",
transactionIndex: 0
}
在這里我們可以看到合約的地址。我們可以從合約中發(fā)送和接收資金,如[將數(shù)據(jù)有效負(fù)載傳輸?shù)紼OA或合約中所示]。
> contract_address = "0xb226270965b43373e98ffc6e2c7693c17e2cf40b"
> web3.eth.sendTransaction({from: src, to: contract_address, value: web3.toWei(0.1, "ether"), data: ""});
"0x6ebf2e1fe95cc9c1fe2e1a0dc45678ccd127d374fdf145c5c8e6cd4ea2e6ca9f"
> web3.eth.sendTransaction({from: src, to: contract_address, value: 0, data: "0x2e1a7d4d000000000000000000000000000000000000000000000000002386f26fc10000"});
"0x59836029e7ce43e92daf84313816ca31420a76a9a571b69e31ec4bf4b37cd16e"
過了一會(huì)兒,兩個(gè)交易都在ethescan上可見。

圖6. Etherscan顯示發(fā)送和接收資金的交易
數(shù)字簽名
到目前為止,我們還沒有深入探討有關(guān)“數(shù)字簽名”的任何細(xì)節(jié)。在本節(jié)中,我們將了解數(shù)字簽名的工作原理以及如何在不泄露私鑰的情況下提供私鑰的所有權(quán)證明。
橢圓曲線數(shù)字簽名算法(ECDSA)
以太坊中使用的數(shù)字簽名算法是橢圓曲線數(shù)字簽名算法,或ECDSA。ECDSA是用于基于橢圓曲線私鑰/公鑰對(duì)的數(shù)字簽名的算法,如[elliptic_curve]中所述。
數(shù)字簽名在以太坊中有三個(gè)用途(參見下面的側(cè)欄)。首先,簽名證明私鑰的所有者(其暗示是以太坊賬戶的所有者)已授權(quán)以太的支出或合約的執(zhí)行。其次,授權(quán)證明是不可否認(rèn)的(不可否認(rèn)性)。第三,簽名證明交易數(shù)據(jù)在交易簽署后沒有也不能被任何人修改。
維基百科對(duì)“數(shù)字簽名”的定義
數(shù)字簽名是用于證明數(shù)字消息或文檔的真實(shí)性的數(shù)學(xué)方案。有效的數(shù)字簽名使收件人有理由相信該郵件是由已知發(fā)件人(身份驗(yàn)證)創(chuàng)建的,發(fā)件人不能拒絕發(fā)送郵件(不可否認(rèn)),并且郵件在傳輸過程中未被更改(完整性) 。
資料來源:https://en.wikipedia.org/wiki/Digital_signature
數(shù)字簽名的工作原理
數(shù)字簽名是一種由兩部分組成的數(shù)學(xué)方案。第一部分是使用來自消息(交易)的私鑰(簽名密鑰)創(chuàng)建簽名的算法。第二部分是一種算法,允許任何人僅使用消息和公鑰來驗(yàn)證簽名。
創(chuàng)建數(shù)字簽名
在以太坊的ECDSA實(shí)現(xiàn)中,被簽名的“消息”是交易,或者更準(zhǔn)確地說,是來自交易的RLP編碼數(shù)據(jù)的Keccak256散列。簽名密鑰是EOA的私鑰。結(jié)果是簽名:
描述:
- k是簽名的私鑰
- m是RLP編碼的交易
-
是Keccak256哈希函數(shù)
-
是簽名算法
- Sig是由此產(chǎn)生的簽名
有關(guān)ECDSA數(shù)學(xué)的更多細(xì)節(jié)可以在[ECDSA數(shù)學(xué)中]找到。
函數(shù)產(chǎn)生一個(gè)由兩個(gè)值組成的簽名Sig,通常稱為R和S:
Sig =(R,S)
驗(yàn)證簽名
要驗(yàn)證簽名,必須具有簽名(R和S)、序列化交易和公鑰(對(duì)應(yīng)于用于創(chuàng)建簽名的私鑰)。實(shí)質(zhì)上,驗(yàn)證簽名意味著“只有生成此公鑰的私鑰的所有者才能在此交易中生成此簽名”。
簽名驗(yàn)證算法接收消息(交易的哈?;蚱渌糠郑?,簽名者的公鑰和簽名(R和S值),如果簽名對(duì)此消息和公鑰有效,則返回TRUE。
ECDSA數(shù)學(xué)
如前所述,簽名由數(shù)學(xué)函數(shù)創(chuàng)建,該函數(shù)產(chǎn)生由兩個(gè)值R和S組成的簽名。在本節(jié)中,我們將更詳細(xì)地介紹函數(shù)
。
簽名算法首先生成短暫(臨時(shí))私鑰/公鑰對(duì)。在涉及簽名私鑰和交易哈希的轉(zhuǎn)換之后,該臨時(shí)密鑰對(duì)用于計(jì)算R和S值。
臨時(shí)密鑰對(duì)由兩個(gè)輸入值生成:
- 隨機(jī)數(shù)q,用作臨時(shí)私鑰
- 和橢圓曲線發(fā)生器點(diǎn)G.
從q和G,我們生成相應(yīng)的臨時(shí)公鑰Q(計(jì)算為Q = q * G,與導(dǎo)出以太坊公鑰的方式相同;參見[pubkey])。然后,數(shù)字簽名的R值是短暫公鑰Q的x坐標(biāo)。
從那里,算法計(jì)算簽名的S值,使得:
描述:
- q是臨時(shí)的私鑰
- R是臨時(shí)公鑰的x坐標(biāo)
- k是簽名(EOA所有者)的私鑰
- m是交易數(shù)據(jù)
- p是橢圓曲線的素?cái)?shù)階
驗(yàn)證是簽名生成函數(shù)的反轉(zhuǎn),使用R,S值和公鑰來計(jì)算值Q,它是橢圓曲線上的一個(gè)點(diǎn)(簽名創(chuàng)建中使用的臨時(shí)公鑰):
描述:
- R和S是簽名值
- K是簽名(EOA所有者)的公鑰
- m是已簽名的交易數(shù)據(jù)
- G是橢圓曲線發(fā)生器點(diǎn)
- p是橢圓曲線的素?cái)?shù)階
如果計(jì)算的點(diǎn)Q的x坐標(biāo)等于R,則驗(yàn)證者可以斷定簽名是有效的。
請(qǐng)注意,在驗(yàn)證簽名時(shí),私鑰既不知道也不透露。
| Tip | ECDSA必然是一個(gè)相當(dāng)復(fù)雜的數(shù)學(xué); 完整的解釋超出了本書的范圍。許多優(yōu)秀的在線指南將逐步引導(dǎo)您完成:搜索“ECDSA解釋”或嘗試以下內(nèi)容:http://bit.ly/2r0HhGB。 |
|---|
在實(shí)踐中簽署交易
為了產(chǎn)生有效的交易,發(fā)起者必須使用橢圓曲線數(shù)字簽名算法對(duì)消息應(yīng)用數(shù)字簽名。當(dāng)我們說“簽署交易”時(shí),我們實(shí)際上是指“簽署RLP序列化交易數(shù)據(jù)的Keccak256哈希”。簽名應(yīng)用于交易數(shù)據(jù)的哈希,而不是交易本身。
| Tip | 在#2,675,000塊,以太坊實(shí)施了“Spurious Dragon”硬分叉,除了其他變化之外,還引入了一個(gè)包含交易重放保護(hù)的新簽名方案。這種新的簽名方案在EIP-155中規(guī)定(見[eip155])。此更改會(huì)影響簽名過程的第一步,在簽名之前向交易添加三個(gè)字段(v,r,s)。 |
|---|
要在以太坊簽署交易,發(fā)起人必須:
- 創(chuàng)建一個(gè)包含九個(gè)字段的交易數(shù)據(jù)結(jié)構(gòu):nonce,gasPrice,startGas,to,value,data,v,r,s
- 生成RLP編碼的交易序列化消息
- 計(jì)算此序列化消息的Keccak256哈希值
- 計(jì)算ECDSA簽名,使用發(fā)件人EOA的私鑰對(duì)哈希進(jìn)行簽名
- 在交易中插入ECDSA簽名的計(jì)算r和s值
原始交易創(chuàng)建和簽署
讓我們使用ethereumjs-tx庫創(chuàng)建一個(gè)原始交易并對(duì)其進(jìn)行簽名。此示例的源代碼位于GitHub存儲(chǔ)庫中的raw_tx_demo.js中:
raw_tx_demo.js:在JavaScript中創(chuàng)建和簽署原始交易
link:code/web3js/raw_tx/raw_tx_demo.js[]
在這里下載:https://github.com/ethereumbook/ethereumbook/blob/develop/code/web3js/raw_tx/raw_tx_demo.js
運(yùn)行示例代碼:
$ node raw_tx_demo.js
RLP-Encoded Tx: 0xe6808609184e72a0008303000094b0920c523d582040f2bcb1bd7fb1c7c1ecebdb348080
Tx Hash: 0xaa7f03f9f4e52fcf69f836a6d2bbc7706580adce0a068ff6525ba337218e6992
Signed Raw Transaction: 0xf866808609184e72a0008303000094b0920c523d582040f2bcb1bd7fb1c7c1ecebdb3480801ca0ae236e42bd8de1be3e62fea2fafac7ec6a0ac3d699c6156ac4f28356a4c034fda0422e3e6466347ef6e9796df8a3b6b05bed913476dc84bbfca90043e3f65d5224
使用EIP-155創(chuàng)建原始交易
EIP-155“簡(jiǎn)單重放攻擊保護(hù)”標(biāo)準(zhǔn)規(guī)定了重放攻擊保護(hù)的交易編碼,其在簽名之前包括交易 數(shù)據(jù)內(nèi)的鏈標(biāo)識(shí)符。這確保了為一個(gè)區(qū)塊鏈(例如以太坊主網(wǎng)絡(luò))創(chuàng)建的交易在另一個(gè)區(qū)塊鏈(例如以太坊Classic或Ropsten測(cè)試網(wǎng)絡(luò))上無效。因此,在一個(gè)網(wǎng)絡(luò)上廣播的交易不能在另一個(gè)網(wǎng)絡(luò)上重播,因此標(biāo)準(zhǔn)的“重放攻擊保護(hù)”名稱。
EIP-155在交易數(shù)據(jù)結(jié)構(gòu)中添加了v,r和s三個(gè)字段。r和s字段初始化為零。在對(duì)交易數(shù)據(jù)進(jìn)行編碼和哈希之前,會(huì)將這三個(gè)字段添加到交易數(shù)據(jù)中。因此,這三個(gè)附加字段會(huì)更改交易的哈希值,稍后將應(yīng)用簽名。通過在被簽名的數(shù)據(jù)中包含鏈標(biāo)識(shí)符,交易簽名可以防止任何更改,因?yàn)槿绻湗?biāo)識(shí)符被修改,則簽名無效。因此,EIP-155使得交易不可能在另一條鏈上重放,因?yàn)楹灻挠行匀Q于鏈標(biāo)識(shí)符。
v簽名前綴字段初始化為鏈標(biāo)識(shí)符,其值為:
| 鏈 | Chain ID |
|---|---|
| 以太坊主網(wǎng) | 1 |
| Morden (obsolete), Expanse | 2 |
| Ropsten | 3 |
| Rinkeby | 4 |
| Rootstock主網(wǎng) | 30 |
| Rootstock測(cè)試網(wǎng)絡(luò) | 31 |
| Kovan | 42 |
| 以太坊經(jīng)典主網(wǎng) | 61 |
| 以太坊經(jīng)典測(cè)試網(wǎng)絡(luò) | 62 |
| Geth私有網(wǎng)絡(luò) | 1337 |
生成的交易結(jié)構(gòu)經(jīng)過RLP編碼,散列和簽名。稍微修改簽名算法以對(duì)v前綴中的chainID進(jìn)行編碼。
有關(guān)更多詳細(xì)信息,請(qǐng)參閱EIP-155規(guī)范:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
簽名前綴值(v)和公鑰恢復(fù)
如[交易結(jié)構(gòu)中]所述,交易消息不包括任何“from”字段。這是因?yàn)榘l(fā)起人的公鑰可以直接從ECDSA簽名中計(jì)算出來。獲得公鑰后,您可以輕松計(jì)算地址?;謴?fù)簽名者公鑰的過程稱為公鑰恢復(fù)。
給定在[ECDSA Math]中計(jì)算的值r和s,我們可以計(jì)算兩個(gè)可能的公鑰。
首先,我們從簽名中的x坐標(biāo)r值計(jì)算兩個(gè)橢圓曲線點(diǎn)R和R'。有兩個(gè)點(diǎn),因?yàn)闄E圓曲線在x軸上是對(duì)稱的,因此對(duì)于任何值x,在x軸的任一側(cè)有兩個(gè)可能的值匹配曲線。
從r中,我們還要計(jì)算,它是r的乘法倒數(shù)。
最后,我們計(jì)算z,它是消息哈希的n個(gè)最低位,其中n是橢圓曲線的階數(shù)。
那么兩個(gè)可能的公鑰是:
和
描述:
-
和
是簽名者公鑰的兩種可能性
-
是簽名r值的乘法倒數(shù)
- s是簽名的值
- R和R'是臨時(shí)公鑰Q的兩種可能性
- z是消息哈希的n個(gè)最低位
- G是橢圓曲線發(fā)生器點(diǎn)
為了提高效率,交易簽名包括前綴值v,它告訴我們兩個(gè)可能的R值中的哪一個(gè)是臨時(shí)的公鑰。如果v是偶數(shù),則R是正確的值。如果v是奇數(shù),那么R'是正確的值。這樣,我們只需要計(jì)算R的一個(gè)值和K的一個(gè)值。
分離簽名和傳輸(離線簽名)
交易簽署后,即可傳輸?shù)揭蕴痪W(wǎng)絡(luò)。創(chuàng)建、簽名和廣播交易的三個(gè)步驟通常發(fā)生在單個(gè)函數(shù)中,例如使用web3.eth.sendTransaction。但是,正如我們?cè)赱Raw交易創(chuàng)建和簽名]中看到的那樣,您可以通過兩個(gè)單獨(dú)的步驟創(chuàng)建和簽署交易。獲得簽名交易后,您可以使用web3.eth.sendSignedTransaction傳輸它,該事件采用十六進(jìn)制編碼和簽名的交易消息并在以太坊網(wǎng)絡(luò)上傳輸。
您為什么要分開交易的簽名和傳輸?最常見的原因是安全性:簽署交易的計(jì)算機(jī)必須具有加載在內(nèi)存中的未鎖定私鑰。傳輸?shù)挠?jì)算機(jī)必須連接到互聯(lián)網(wǎng)并運(yùn)行以太坊客戶端。如果這兩個(gè)功能在一臺(tái)計(jì)算機(jī)上,那么您在在線系統(tǒng)上有私鑰,這非常危險(xiǎn)。分離簽名和傳輸?shù)墓δ芊Q為離線簽名,是一種常見的安全措施。
根據(jù)您所需的安全級(jí)別,您的“離線簽名”計(jì)算機(jī)可能與在線計(jì)算機(jī)有不同程度的分離,從孤立和防火墻子網(wǎng)(在線但隔離)到稱為氣隙系統(tǒng)的完全脫機(jī)系統(tǒng)。在氣隙系統(tǒng)中根本沒有網(wǎng)絡(luò)連接 - 計(jì)算機(jī)與在線環(huán)境之間存在“空中”差距。要對(duì)交易進(jìn)行簽名,您可以使用數(shù)據(jù)存儲(chǔ)介質(zhì)或(更好)網(wǎng)絡(luò)攝像頭和QR碼將其傳輸?shù)綒庀队?jì)算機(jī)或從氣隙計(jì)算機(jī)傳輸。當(dāng)然,這意味著您必須手動(dòng)傳輸要簽名的每個(gè)交易,但這不會(huì)擴(kuò)展。
雖然沒有多少環(huán)境可以使用完全氣隙系統(tǒng),但即使是很小程度的隔離也具有顯著的安全性優(yōu)勢(shì)。例如,具有僅允許消息隊(duì)列協(xié)議通過的防火墻的隔離子網(wǎng)可以提供比在線系統(tǒng)上簽名大大減少的攻擊面和更高的安全性。許多公司使用ZeroMQ(0MQ)等協(xié)議,因?yàn)樗鼮楹灻?jì)算機(jī)提供了很小的攻擊面。通過這樣的設(shè)置,交易被序列化并排隊(duì)等待簽名。排隊(duì)協(xié)議以類似于TCP套接字的方式將序列化消息發(fā)送到簽名計(jì)算機(jī)。簽名計(jì)算機(jī)從隊(duì)列中讀取序列化交易(小心),使用適當(dāng)?shù)拿荑€應(yīng)用簽名,并將它們放在傳出隊(duì)列中。
交易廣播
以太坊網(wǎng)絡(luò)使用“flood”路由協(xié)議。每個(gè)以太坊客戶端,充當(dāng)對(duì)等網(wǎng)絡(luò)(P2P)的節(jié)點(diǎn) ,其(理想地)形成網(wǎng)狀網(wǎng)絡(luò)。沒有網(wǎng)絡(luò)節(jié)點(diǎn)是“特殊的”,它們都作為平等的對(duì)等體。我們將使用術(shù)語“節(jié)點(diǎn)”來指代連接并參與P2P網(wǎng)絡(luò)的以太坊客戶端。
交易傳播開始于創(chuàng)建以太坊節(jié)點(diǎn)(或從離線接收)簽署的交易。交易被驗(yàn)證,然后傳輸?shù)街苯舆B接到始發(fā)節(jié)點(diǎn)的所有其他以太坊節(jié)點(diǎn)。平均而言,每個(gè)以太坊節(jié)點(diǎn)保持與至少13個(gè)稱為其鄰居的其他節(jié)點(diǎn)的連接。每個(gè)鄰居節(jié)點(diǎn)在收到交易后立即驗(yàn)證交易。如果他們同意這是有效的,他們會(huì)保存一份副本并將其傳播給所有的鄰居(除了它的鄰居)。結(jié)果,交易從源節(jié)點(diǎn)向外傳播擴(kuò)散,直到網(wǎng)絡(luò)中的所有節(jié)點(diǎn)都擁有該交易的副本。
幾秒鐘內(nèi),以太坊交易就會(huì)傳播到全球所有以太坊節(jié)點(diǎn)。從每個(gè)節(jié)點(diǎn)的角度來看,不可能辨別交易的起源。發(fā)送給我們節(jié)點(diǎn)的鄰居可能是交易的發(fā)起者,或者可能從其鄰居那里收到它。為了能夠跟蹤交易的起源或干擾傳播,攻擊者必須控制所有節(jié)點(diǎn)的相當(dāng)大的百分比。這是P2P網(wǎng)絡(luò)安全和隱私設(shè)計(jì)的一部分,尤其適用于區(qū)塊鏈。
記錄在鏈上
盡管以太坊中的所有節(jié)點(diǎn)都是同等的對(duì)等節(jié)點(diǎn),但其中一些節(jié)點(diǎn)由礦工負(fù)責(zé)運(yùn)營(yíng),并向礦場(chǎng)提供交易和區(qū)塊,這些礦場(chǎng)是具有高性能圖形處理單元(GPU)的計(jì)算機(jī)。挖掘計(jì)算機(jī)將交易添加到候選區(qū)塊,并嘗試找到使得候選區(qū)塊有效的工作證明。我們將在[共識(shí)]中更詳細(xì)地討論這一點(diǎn)。
沒有太多細(xì)節(jié),有效的交易最終將被包含在一個(gè)交易區(qū)塊中,并因此記錄在以太坊區(qū)塊鏈中。一旦開采成區(qū)塊,交易還通過修改賬戶余額(在簡(jiǎn)單付款的情況下)或通過調(diào)用改變其內(nèi)部狀態(tài)的合約來修改以太坊singleton的狀態(tài)。這些變更以交易收據(jù)的形式記錄在交易旁邊,交易收據(jù)也可能包括事件。我們將在[evm]中更詳細(xì)地檢查所有這些。
我們的交易已經(jīng)完成了從創(chuàng)建到簽署EOA、傳播以及最終采礦的旅程。它改變了singleton的狀態(tài),并在區(qū)塊鏈上留下了不可磨滅的印記。
多重簽名(multisig)交易
如果您熟悉比特幣的腳本功能,那么您就知道有可能創(chuàng)建一個(gè)比特幣多幣種賬戶,該賬戶只能在多方簽署交易時(shí)花費(fèi)資金(例如2of2 或 3of4簽名)。以太坊的價(jià)值交易沒有多重簽名的規(guī)定,盡管可以部署任意條件的任意合約來處理ether和token的交易。
為了在多重情況下保護(hù)你的ether,將它們轉(zhuǎn)移到多簽合約中。無論何時(shí)您想將資金轉(zhuǎn)入其他賬戶,所有必需的用戶都需要使用常規(guī)錢包軟件將交易發(fā)送至合約,從而有效授權(quán)合約執(zhí)行最終交易。
這些合約還可以設(shè)計(jì)為在執(zhí)行本地代碼之前需要多個(gè)簽名或觸發(fā)其他合約。該方案的安全性最終由multisig合約代碼決定。
Discussion 和 Grid +參考實(shí)現(xiàn):https://blog.gridplus.io/toward-an-ethereum-multisig-standard-c566c7b7a3f6