go-ethereum源碼剖析:交易

交易是區塊鏈中最基本也是最核心的一個概念,在以太坊中,交易更是重中之重,由於以太坊是一個智能合約平臺,以太坊上的應用都是經過智能合約與區塊鏈進行交互,而智能合約的執行是由交易觸發的,沒有交易,智能合約就是一段死的代碼,能夠說在以太坊中,一切都源於交易。下面就來看看在以太坊中交易是什麼樣的,交易裏面都有什麼。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 price
  • GasLimit:本交易容許消耗的最大gas數量
  • Recipient:交易的接收者,是一個地址
  • Amount:交易轉移的以太幣數量,單位是wei
  • Payload:交易能夠攜帶的數據,在不一樣類型的交易中有不一樣的含義
  • V R S:交易的簽名數據

注意:這裏並無一個字段來指明交易的發送者,由於交易的發送者地址能夠從簽名中獲得。web

transaction.go中還定義了一個jsonTransaction結構體,這個結構體用於將交易進行json序列化和反序列化,具體的序列化和反序列化能夠參照MarshalJSONUnmarshalJSON函數。以太坊節點會向外部提供JSON RPC服務,供外部調用,RPC服務經過json格式傳輸數據,節點收到json數據後,會轉換成內部的數據結構來使用。jsonTransaction結構體使用go語言的struct tag特性指定了內部數據結構與json數據各字段的對應關係,例如內部的AccountNonce對應json的nonceAmount對應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

下面是計算交易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算法是Keccak256ide

那麼究竟是對交易中的哪些字段計算的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

相關文章
相關標籤/搜索