1. 程式人生 > >以太坊原始碼探究之交易與簽名

以太坊原始碼探究之交易與簽名

與比特幣相比,以太坊中的交易結構有相當明顯的不同。下面是以太坊中Transaction資料結構的UML圖:   以太坊交易類圖

右邊的txdata才是實際的交易資料,它在core/types/transaction.go裡是這樣宣告的:

type txdata struct {
    AccountNonce uint64          `json:"nonce"    gencodec:"required"`
    Price        *big.Int        `json:"gasPrice" gencodec:"required"` GasLimit uint64 `json:"gas" gencodec:"required"` Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation Amount *big.Int `json:"value" gencodec:"required"` Payload []byte `json:"input" gencodec:"required"` // Signature values V *big.Int `json:"v" gencodec:"required"` R *big.Int `json:"r" gencodec:"required"` S *big.Int `json:"s" gencodec:"required"` // This is only used when marshaling to JSON. Hash *common.Hash `json:"hash" rlp:"-"` } 

第一個欄位AccountNonce,直譯就是賬戶隨機數。它是以太坊中很小但也很重要的一個細節。以太坊為每個賬戶和交易都建立了一個Nonce,當從賬戶發起交易的時候,當前賬戶的Nonce值就被作為交易的Nonce。這裡,如果是普通賬戶那麼Nonce就是它發出的交易數,如果是合約賬戶就是從它的建立合約數。

為什麼要使用這個Nonce呢?其主要目的就是為了防止重複攻擊(Replay Attack)。因為交易都是需要簽名的,假定沒有Nonce,那麼只要交易資料和發起人是確定的,簽名就一定是相同的,這樣攻擊者就能在收到一個交易資料後,重新生成一個完全相同的交易並再次提交,比如A給B發了個交易,因為交易是有簽名的,B雖然不能改動這個交易資料,但只要反覆提交一模一樣的交易資料,就能把A賬戶的所有資金都轉到B手裡。

當使用賬戶Nonce之後,每次發起一個交易,A賬戶的Nonce值就會增加,當B重新提交時,因為Nonce對不上了,交易就會被拒絕。這樣就可以防止重複攻擊。當然,事情還沒有完,因為還能跨鏈實施攻擊,直到EIP-155引入了chainID,才實現了不同鏈之間的交易資料不相容。事實上,Nonce並不能真正防止重複攻擊,比如A向B買東西,發起交易T1給B,緊接著又提交另一個交易T2,T2的Gas價格更高、優先順序更高將被優先處理,如果恰好T2處理完成後剩餘資金已經不足以支付T1,那麼T1就會被拒絕。這時如果B已經把東西給了A,那A也就攻擊成功了。所以說,就算交易被處理了也還要再等待一定時間,確保生成足夠深度的區塊,才能保證交易的不可逆。

Price指的是單位Gas的價格,所謂Gas就是交易的消耗,Price就是單位Gas要消耗多少以太幣(Ether),Gas * Price就是處理交易需要消耗多少以太幣,它就相當於比特幣中的交易手續費。

GasLimit限定了本次交易允許消耗資源的最高上限,換句話說,以太坊中的交易不可能無限制地消耗資源,這也是以太坊的安全策略之一,防止攻擊者惡意佔用資源。

Recipient是交易接收者,它是common.Address指標型別,代表一個地址。這個值也可以是空的,這時在交易執行時,會通過智慧合約建立一個地址來完成交易。

Amount是交易額。這個簡單,不用解釋。

Payload比較重要,它是一個位元組陣列,可以用來作為建立合約的指令陣列,這時每個位元組都是一個單獨的指令;也可以作為資料陣列,由合約指令來進行操作。合約由以太坊虛擬機器(Ethereum Virtual Machine,EVM)建立並執行。

V、R、S是交易的簽名資料。以太坊當中,交易經過數字簽名之後,生成的signature是一個長度65的位元組陣列,它被截成三段,前32位元組被放進R,再32位元組放進S,最後1個位元組放進V。那麼為什麼要被截成3段呢?以太坊用的是ECDSA演算法,R和S就是ECSDA簽名輸出,V則是Recovery ID。看下面的javascript程式碼:

var sig = secp256k1.sign(msgHash, privateKey)
  var ret = {}
  ret.r = sig.signature.slice(0, 32)
  ret.s = sig.signature.slice(32, 64) ret.v = sig.recovery + 27 

 



作者:魏兆華
連結:https://www.jianshu.com/p/30833db88330
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。