【以太坊源碼】交易(一)

交易是區塊鏈中最基本也是最核心的一個概念,在以太坊中,交易更是重中之重,因爲以太坊是一個智能合約平臺,以太坊上的應用都是通過智能合約與區塊鏈進行交互,而智能合約的執行是由交易觸發的,沒有交易,智能合約就是一段死的代碼,可以說在以太坊中,一切都源於交易。下面就來看看在以太坊中交易是什麼樣的,交易裏面都有什麼。

交易的數據結構

core/types/transaction.go中定義了交易的數據結構:

type Transaction struct {
    data txdata
    // caches
    hash atomic.Value
    size atomic.Value
    from atomic.Value
}

在這個結構體裏面只有一個data字段,它是txdata類型的,其他的三個字段hash size from是緩存字段,txdata也是一個結構體,它裏面定義了交易的具體的字段:

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
}

各字段的含義如下:

  • AccountNonce:此交易的發送者已發送過的交易數
  • Price:此交易的gas price
  • GasLimit:本交易允許消耗的最大gas數量
  • Recipient:交易的接收者,是一個地址
  • Amount:交易轉移的以太幣數量,單位是wei
  • Payload:交易可以攜帶的數據,在不同類型的交易中有不同的含義
  • V R S:交易的簽名數據

注意:這裏並沒有一個字段來指明交易的發送者,因爲交易的發送者地址可以從簽名中得到。

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字段。

交易的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算法是Keccak256

那麼到底是對交易中的哪些字段計算的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)來調用合約。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章