交易是區塊鏈中最基本也是最核心的一個概念,在以太坊中,交易更是重中之重,由於以太坊是一個智能合約平臺,以太坊上的應用都是經過智能合約與區塊鏈進行交互,而智能合約的執行是由交易觸發的,沒有交易,智能合約就是一段死的代碼,能夠說在以太坊中,一切都源於交易。下面就來看看在以太坊中交易是什麼樣的,交易裏面都有什麼。javascript
在core/types/transaction.go
中定義了交易的數據結構:java
type Transaction struct { data txdata // caches hash atomic.Value size atomic.Value from atomic.Value }
在這個結構體裏面只有一個data
字段,它是txdata
類型的,其餘的三個字段hash
size
from
是緩存字段,txdata
也是一個結構體,它裏面定義了交易的具體的字段:git
type txdata struct { AccountNonce uint64 Price, GasLimit *big.Int Recipient *common.Address `rlp:"nil"` // nil means contract creation Amount *big.Int Payload []byte V *big.Int // signature R, S *big.Int // signature }
各字段的含義以下:github
AccountNonce
:此交易的發送者已發送過的交易數Price
:此交易的gas priceGasLimit
:本交易容許消耗的最大gas數量Recipient
:交易的接收者,是一個地址Amount
:交易轉移的以太幣數量,單位是wei
Payload
:交易能夠攜帶的數據,在不一樣類型的交易中有不一樣的含義V R S
:交易的簽名數據注意:這裏並無一個字段來指明交易的發送者,由於交易的發送者地址能夠從簽名中獲得。web
在transaction.go
中還定義了一個jsonTransaction
結構體,這個結構體用於將交易進行json序列化和反序列化,具體的序列化和反序列化能夠參照MarshalJSON
和UnmarshalJSON
函數。以太坊節點會向外部提供JSON RPC服務,供外部調用,RPC服務經過json格式傳輸數據,節點收到json數據後,會轉換成內部的數據結構來使用。jsonTransaction
結構體使用go語言的struct tag
特性指定了內部數據結構與json數據各字段的對應關係,例如內部的AccountNonce
對應json的nonce
,Amount
對應json的value
。web3.js的eth.getTransaction()
和eth.sendTransaction()
使用的數據就是json格式的,根據這個結構體就能夠知道在web3.js中交易的各個字段與程序內部的各個字段的對應關係。算法
type jsonTransaction struct { Hash *common.Hash `json:"hash"` AccountNonce *hexutil.Uint64 `json:"nonce"` Price *hexutil.Big `json:"gasPrice"` GasLimit *hexutil.Big `json:"gas"` Recipient *common.Address `json:"to"` Amount *hexutil.Big `json:"value"` Payload *hexutil.Bytes `json:"input"` V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` }
注:Payload
這個字段在eth.sendTransaction()
中對應的是data
字段,在eth.getTransaction()
中對應的是input
字段。json
下面是計算交易Hash的函數,它是先從緩存tx.hash
中取,若是取到,就直接返回,若是緩存中沒有,就調用rlpHash
計算hash,而後把hash值加入到緩存中。緩存
// Hash hashes the RLP encoding of tx. // It uniquely identifies the transaction. func (tx *Transaction) Hash() common.Hash { if hash := tx.hash.Load(); hash != nil { return hash.(common.Hash) } v := rlpHash(tx) tx.hash.Store(v) return v }
rlpHash
的代碼在core/types/block.go
中:數據結構
func rlpHash(x interface{}) (h common.Hash) { hw := sha3.NewKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h }
從rlpHash
函數能夠看出,計算hash的方法是先對交易進行RLP編碼,而後計算RLP編碼數據的hash,具體的hash算法是Keccak256
。ide
那麼究竟是對交易中的哪些字段計算的hash呢?這就要看rlp.Encode
對哪些字段進行了編碼。rlp.Encode
代碼在rlp/encode.go
中,不用看具體的實現,在註釋中有這麼一段:
// If the type implements the Encoder interface, Encode calls // EncodeRLP. This is true even for nil pointers, please see the // documentation for Encoder.
就是說若是一個類型實現了Encoder
接口,那麼Encode
函數就會調用那個類型所實現的EncodeRLP
函數。因此咱們就要看Transaction
這個結構體是否實現了EncodeRLP
函數。回到core/types/transaction.go
中,能夠看到Transaction
確實實現了EncodeRLP
函數:
// DecodeRLP implements rlp.Encoder func (tx *Transaction) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &tx.data) }
從這能夠看出交易的hash其實是對tx.data
進行hash計算獲得的:txhash=Keccak256(rlpEncode(tx.data))
。
在源碼中交易只有一種數據結構,若是非要給交易分個類的話,我認爲交易能夠分爲三種:轉帳的交易、建立合約的交易、執行合約的交易。web3.js提供了發送交易的接口:
web3.eth.sendTransaction(transactionObject [, callback])
參數是一個對象,在發送交易的時候指定不一樣的字段,區塊鏈節點就能夠識別出對應類型的交易。
轉帳是最簡單的一種交易,這裏轉帳是指從一個帳戶向另外一個帳戶發送以太幣。發送轉帳交易的時候只須要指定交易的發送者、接收者、轉幣的數量。使用web3.js發送轉帳交易應該像這樣:
web3.eth.sendTransaction({ from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567", value: 10000000000000000 });
value
是轉移的以太幣數量,單位是wei
,對應的是源碼中的Amount
字段。to
對應的是源碼中的Recipient
。
建立合約指的是將合約部署到區塊鏈上,這也是經過發送交易來實現。在建立合約的交易中,to
字段要留空不填,在data
字段中指定合約的二進制代碼,from
字段是交易的發送者也是合約的建立者。
web3.eth.sendTransaction({ from: "contract creator's address", data: "contract binary code" });
data
字段對應的是源碼中的Payload
字段。
調用合約中的方法,須要將交易的to
字段指定爲要調用的合約的地址,經過data
字段指定要調用的方法以及向該方法傳遞的參數。
web3.eth.sendTransaction({ from: "sender's address", to: "contract address", data: "hash of the invoked method signature and encoded parameters" });
data
字段須要特殊的編碼規則,具體細節能夠參考Ethereum Contract ABI。本身拼接字段既不方便又容易出錯,因此通常都使用封裝好的SDK(好比web3.js)來調用合約。
代碼版本:1.5.9