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

與比特幣相比,以太坊中的交易結構有至關明顯的不一樣。下面是以太坊中Transaction數據結構的UML圖:
 
以太坊交易類圖

右邊的txdata纔是實際的交易數據,它在core/types/transaction.go裏是這樣聲明的:javascript

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就是它發出的交易數,若是是合約帳戶就是從它的建立合約數。java

爲何要使用這個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也就攻擊成功了。因此說,就算交易被處理了也還要再等待必定時間,確保生成足夠深度的區塊,才能保證交易的不可逆。json

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

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

Recipient是交易接收者,它是common.Address指針類型,表明一個地址。這個值也能夠是空的,這時在交易執行時,會經過智能合約建立一個地址來完成交易。數據結構

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

Payload比較重要,它是一個字節數組,能夠用來做爲建立合約的指令數組,這時每一個字節都是一個單獨的指令;也能夠做爲數據數組,由合約指令來進行操做。合約由以太坊虛擬機(Ethereum Virtual Machine,EVM)建立並執行。spa

V、R、S是交易的簽名數據。以太坊當中,交易通過數字簽名以後,生成的signature是一個長度65的字節數組,它被截成三段,前32字節被放進R,再32字節放進S,最後1個字節放進V。那麼爲何要被截成3段呢?以太坊用的是ECDSA算法,R和S就是ECSDA簽名輸出,V則是Recovery ID。看下面的javascript代碼:3d

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 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
相關文章
相關標籤/搜索