go-ethereum源码剖析:交易

交易是区块链中最基本也是最核心的一个概念,在以太坊中,交易更是重中之重,因为以太坊是一个智能合约平台,以太坊上的应用都是通过智能合约与区块链进行交互,而智能合约的执行是由交易触发的,没有交易,智能合约就是一段死的代码,可以说在以太坊中,一切都源于交易。下面就来看看在以太坊中交易是什么样的,交易里面都有什么。

交易的数据结构

在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序列化和反序列化,具体的序列化和反序列化可以参照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字段。

交易的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)来调用合约。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值