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