Go-ethereum 源码解析之 core/types/transaction.go

Go-ethereum 源码解析之 core/types/transaction.go

package types

import (
    "container/heap"
    "errors"
    "io"
    "math/big"
    "sync/atomic"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/rlp"
)

//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go

var (
    ErrInvalidSig = errors.New("invalid transaction v, r, s values")
)

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

type txdata struct {
    AccountNonce uint64          `json:"nonce"    gencodec:"required"`
    Price        *big.Int        `json:"gasPrice" gencodec:"required"`
    GasLimit     uint64          `json:"gas"      gencodec:"required"`
    Recipient    *common.Address `json:"to"       rlp:"nil"` // nil means contract creation
    Amount       *big.Int        `json:"value"    gencodec:"required"`
    Payload      []byte          `json:"input"    gencodec:"required"`

    // Signature values
    V *big.Int `json:"v" gencodec:"required"`
    R *big.Int `json:"r" gencodec:"required"`
    S *big.Int `json:"s" gencodec:"required"`

    // This is only used when marshaling to JSON.
    Hash *common.Hash `json:"hash" rlp:"-"`
}

type txdataMarshaling struct {
    AccountNonce hexutil.Uint64
    Price        *hexutil.Big
    GasLimit     hexutil.Uint64
    Amount       *hexutil.Big
    Payload      hexutil.Bytes
    V            *hexutil.Big
    R            *hexutil.Big
    S            *hexutil.Big
}

func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
    return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
}

func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
    return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}

func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
    if len(data) > 0 {
        data = common.CopyBytes(data)
    }
    d := txdata{
        AccountNonce: nonce,
        Recipient:    to,
        Payload:      data,
        Amount:       new(big.Int),
        GasLimit:     gasLimit,
        Price:        new(big.Int),
        V:            new(big.Int),
        R:            new(big.Int),
        S:            new(big.Int),
    }
    if amount != nil {
        d.Amount.Set(amount)
    }
    if gasPrice != nil {
        d.Price.Set(gasPrice)
    }

    return &Transaction{data: d}
}

// ChainId returns which chain id this transaction was signed for (if at all)
func (tx *Transaction) ChainId() *big.Int {
    return deriveChainId(tx.data.V)
}

// Protected returns whether the transaction is protected from replay protection.
func (tx *Transaction) Protected() bool {
    return isProtectedV(tx.data.V)
}

func isProtectedV(V *big.Int) bool {
    if V.BitLen() <= 8 {
        v := V.Uint64()
        return v != 27 && v != 28
    }
    // anything not 27 or 28 is considered protected
    return true
}

// EncodeRLP implements rlp.Encoder
func (tx *Transaction) EncodeRLP(w io.Writer) error {
    return rlp.Encode(w, &tx.data)
}

// DecodeRLP implements rlp.Decoder
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
    _, size, _ := s.Kind()
    err := s.Decode(&tx.data)
    if err == nil {
        tx.size.Store(common.StorageSize(rlp.ListSize(size)))
    }

    return err
}

// MarshalJSON encodes the web3 RPC transaction format.
func (tx *Transaction) MarshalJSON() ([]byte, error) {
    hash := tx.Hash()
    data := tx.data
    data.Hash = &hash
    return data.MarshalJSON()
}

// UnmarshalJSON decodes the web3 RPC transaction format.
func (tx *Transaction) UnmarshalJSON(input []byte) error {
    var dec txdata
    if err := dec.UnmarshalJSON(input); err != nil {
        return err
    }
    var V byte
    if isProtectedV(dec.V) {
        chainID := deriveChainId(dec.V).Uint64()
        V = byte(dec.V.Uint64() - 35 - 2*chainID)
    } else {
        V = byte(dec.V.Uint64() - 27)
    }
    if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
        return ErrInvalidSig
    }
    *tx = Transaction{data: dec}
    return nil
}

func (tx *Transaction) Data() []byte       { return common.CopyBytes(tx.data.Payload) }
func (tx *Transaction) Gas() uint64        { return tx.data.GasLimit }
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
func (tx *Transaction) Value() *big.Int    { return new(big.Int).Set(tx.data.Amount) }
func (tx *Transaction) Nonce() uint64      { return tx.data.AccountNonce }
func (tx *Transaction) CheckNonce() bool   { return true }

// To returns the recipient address of the transaction.
// It returns nil if the transaction is a contract creation.
func (tx *Transaction) To() *common.Address {
    if tx.data.Recipient == nil {
        return nil
    }
    to := *tx.data.Recipient
    return &to
}

// 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
}

// Size returns the true RLP encoded storage size of the transaction, either by
// encoding and returning it, or returning a previsouly cached value.
func (tx *Transaction) Size() common.StorageSize {
    if size := tx.size.Load(); size != nil {
        return size.(common.StorageSize)
    }
    c := writeCounter(0)
    rlp.Encode(&c, &tx.data)
    tx.size.Store(common.StorageSize(c))
    return common.StorageSize(c)
}

// AsMessage returns the transaction as a core.Message.
//
// AsMessage requires a signer to derive the sender.
//
// XXX Rename message to something less arbitrary?
func (tx *Transaction) AsMessage(s Signer) (Message, error) {
    msg := Message{
        nonce:      tx.data.AccountNonce,
        gasLimit:   tx.data.GasLimit,
        gasPrice:   new(big.Int).Set(tx.data.Price),
        to:         tx.data.Recipient,
        amount:     tx.data.Amount,
        data:       tx.data.Payload,
        checkNonce: true,
    }

    var err error
    msg.from, err = Sender(s, tx)
    return msg, err
}

// WithSignature returns a new transaction with the given signature.
// This signature needs to be formatted as described in the yellow paper (v+27).
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
    r, s, v, err := signer.SignatureValues(tx, sig)
    if err != nil {
        return nil, err
    }
    cpy := &Transaction{data: tx.data}
    cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
    return cpy, nil
}

// Cost returns amount + gasprice * gaslimit.
func (tx *Transaction) Cost() *big.Int {
    total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
    total.Add(total, tx.data.Amount)
    return total
}

func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) {
    return tx.data.V, tx.data.R, tx.data.S
}

// Transactions is a Transaction slice type for basic sorting.
type Transactions []*Transaction

// Len returns the length of s.
func (s Transactions) Len() int { return len(s) }

// Swap swaps the i'th and the j'th element in s.
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// GetRlp implements Rlpable and returns the i'th element of s in rlp.
func (s Transactions) GetRlp(i int) []byte {
    enc, _ := rlp.EncodeToBytes(s[i])
    return enc
}

// TxDifference returns a new set which is the difference between a and b.
func TxDifference(a, b Transactions) Transactions {
    keep := make(Transactions, 0, len(a))

    remove := make(map[common.Hash]struct{})
    for _, tx := range b {
        remove[tx.Hash()] = struct{}{}
    }

    for _, tx := range a {
        if _, ok := remove[tx.Hash()]; !ok {
            keep = append(keep, tx)
        }
    }

    return keep
}

// TxByNonce implements the sort interface to allow sorting a list of transactions
// by their nonces. This is usually only useful for sorting transactions from a
// single account, otherwise a nonce comparison doesn't make much sense.
type TxByNonce Transactions

func (s TxByNonce) Len() int           { return len(s) }
func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
func (s TxByNonce) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// TxByPrice implements both the sort and the heap interface, making it useful
// for all at once sorting as well as individually adding and removing elements.
type TxByPrice Transactions

func (s TxByPrice) Len() int           { return len(s) }
func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 }
func (s TxByPrice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func (s *TxByPrice) Push(x interface{}) {
    *s = append(*s, x.(*Transaction))
}

func (s *TxByPrice) Pop() interface{} {
    old := *s
    n := len(old)
    x := old[n-1]
    *s = old[0 : n-1]
    return x
}

// TransactionsByPriceAndNonce represents a set of transactions that can return
// transactions in a profit-maximizing sorted order, while supporting removing
// entire batches of transactions for non-executable accounts.
type TransactionsByPriceAndNonce struct {
    txs    map[common.Address]Transactions // Per account nonce-sorted list of transactions
    heads  TxByPrice                       // Next transaction for each unique account (price heap)
    signer Signer                          // Signer for the set of transactions
}

// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
// price sorted transactions in a nonce-honouring way.
//
// Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor.
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
    // Initialize a price based heap with the head transactions
    heads := make(TxByPrice, 0, len(txs))
    for from, accTxs := range txs {
        heads = append(heads, accTxs[0])
        // Ensure the sender address is from the signer
        acc, _ := Sender(signer, accTxs[0])
        txs[acc] = accTxs[1:]
        if from != acc {
            delete(txs, from)
        }
    }
    heap.Init(&heads)

    // Assemble and return the transaction set
    return &TransactionsByPriceAndNonce{
        txs:    txs,
        heads:  heads,
        signer: signer,
    }
}

// Peek returns the next transaction by price.
func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
    if len(t.heads) == 0 {
        return nil
    }
    return t.heads[0]
}

// Shift replaces the current best head with the next one from the same account.
func (t *TransactionsByPriceAndNonce) Shift() {
    acc, _ := Sender(t.signer, t.heads[0])
    if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
        t.heads[0], t.txs[acc] = txs[0], txs[1:]
        heap.Fix(&t.heads, 0)
    } else {
        heap.Pop(&t.heads)
    }
}

// Pop removes the best transaction, *not* replacing it with the next one from
// the same account. This should be used when a transaction cannot be executed
// and hence all subsequent ones should be discarded from the same account.
func (t *TransactionsByPriceAndNonce) Pop() {
    heap.Pop(&t.heads)
}

// Message is a fully derived transaction and implements core.Message
//
// NOTE: In a future PR this will be removed.
type Message struct {
    to         *common.Address
    from       common.Address
    nonce      uint64
    amount     *big.Int
    gasLimit   uint64
    gasPrice   *big.Int
    data       []byte
    checkNonce bool
}

func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
    return Message{
        from:       from,
        to:         to,
        nonce:      nonce,
        amount:     amount,
        gasLimit:   gasLimit,
        gasPrice:   gasPrice,
        data:       data,
        checkNonce: checkNonce,
    }
}

func (m Message) From() common.Address { return m.from }
func (m Message) To() *common.Address  { return m.to }
func (m Message) GasPrice() *big.Int   { return m.gasPrice }
func (m Message) Value() *big.Int      { return m.amount }
func (m Message) Gas() uint64          { return m.gasLimit }
func (m Message) Nonce() uint64        { return m.nonce }
func (m Message) Data() []byte         { return m.data }
func (m Message) CheckNonce() bool     { return m.checkNonce }

Appendix A. 总体批注

文件 core/types/transaction.go 中主要定义了数据结构 Transaction,用于存储事务的详细信息。

Transaction 的具体信息由数据结构 txdata 描述,并通过 atomic.Value 的方式对事务的哈希值、内存大小、发送者地址进行了缓存。

数据结构 txdata 用于描述事务的具体信息。
数据结构 txdataMarshaling 用于表示 txdata 的 JSON 流。
通过方法 ChainId() 返回 chain id。
通过方法 Protected() 返回是否受重话保护。
通过方法 EncodeRLP() 实现 rlp.Encoder 接口,并生成事务的 RLP 编码流。
通过方法 DecodeRLP() 实现 rlp.Decoder 接口,并从 RLP 编码流中恢复事务。
通过方法 MarshalJSON() 将事务编码成 JSON 流。
通过方法 UnmarshalJSON() 从 JSON 流中恢复事务。
通过方法 Data() 返回事务中智能合约的 ABI 字节码。
通过方法 Gas() 返回事务的 gas limit。
通过方法 GasPrice() 返回事务的 gas price。
通过方法 Value() 返回事务的 ether amount。
通过方法 Nonce() 返回事务的 AmountNonce。
通过方法 CheckNonce() 返回对 AmountNonce 的校验,这里永远返回 true。
通过方法 To() 返回事务的接收者,如果事务表示创建智能合约,则返回 nil。
通过方法 Hash() 返回事务的哈希值。
通过方法 Size() 返回事务的 RLP 编码字节数。
通过方法 AsMessage() 将事务作为 core.Message 形式返回。
通过方法 WithSignature() 生成原事务的签名信息,将以新事务的方式返回。
通过方法 Cost() 返回事务的价值,公式为 amount + gasprice * gaslimit。
通过方法 RawSignatureValues() 返回事务的签名信息 R, S, V。
通过函数 NewTransaction() 创建新的事务对象。
通过函数 NewContractCreation() 以事务的方式创建智能合约。

数据结构 Transactions 用于表示 Transaction 列表的封装器。

通过方法 Len() 返回列表的元素个数。
通过方法 Swap() 交换列表中的两个元素。
通过方法 GetRlp() 返回列表中给定元素的 RLP 编码。
通过函数 TxDifference() 返回两个 Transactions 的差集。

数据结构 TxByNonce 用于表示 Transactions 的封装器,且根据 nonce 排序列表中的各事务。TxByNonce 实现了 sort 接口。

通过方法 Len() 返回列表的元素个数。
通过方法 Less() 实现 sort 接口,用于支持列表的排序操作。排序标准是事务的 nonce 值。
通过方法 Swap() 交换列表中的两个元素。

数据结构 TxByPrice 用于表示 Transactions 的封装器,且根据 gas price 排序列表中的各事务。TxByPrice 同时实现了 sort 和 heap 接口。通过 heap 数据结构来优化事务的新增和删除操作。

通过方法 Len() 返回列表的元素个数。
通过方法 Less() 实现 sort 接口,用于支持列表的排序操作。排序标准是事务的 gas price 值。
通过方法 Swap() 交换列表中的两个元素。
通过方法 Push() 新增一个 Transaction。
通过方法 Pop() 删除并返回最后一个 Transaction。

数据结构 TransactionsByPriceAndNonce 包含了:签名者、各地址对应的事务列表、各地址事务列表中第一个事务构成的根据 gas price 进行排序的列表。用于矿工打包新区块时选择事务的标准,根据收益最大化来选择新区块能够包含的下一个事务。

字段 txs 表示每个账户对应的事务列表(且事务根据 nonce 排序)。
字段 heads 是由 txs 中每个账户的第一个事务构成的,并根据 price 排序的事务列表。
字段 signer 表示签名者。
通过方法 Peek() 返回下一个价值最大的事务。
通过方法 Shift() 替换字段 heads 中第一个事务,并重新排序 heap 结构。替换规则如下:首先通过组合签名者计算出该事务的地址,然后从字段 txs 中获取该地址的下一个事务(并将新事务从该地址对应的事务列表中删除)。
通过方法 Pop() 移除价值最大的事务,而不需要像 Shift() 那样用同一个地址的下一个事务来替换。
通过函数 NewTransactionsByPriceAndNonce() 来创建对象 TransactionsByPriceAndNonce。需要注意的是参数 txs 是值传递形式。

数据结构 Message 是一个完全派生的 Transaction,并且实现了 core.Message。
Message 各字段的含义与 Transaction 的同名字段或对应的 json 注解一致。

通过方法 From() 返回发送者地址。
通过方法 To() 返回接收者地址。
通过方法 GasPrice() 返回事务的 gas price。
通过方法 Gas() 返回事务的 gas limit。
通过方法 Nonce() 返回事务的随机数值。
通过方法 Data() 返回事务的智能合约 ABI 字节码。
通过方法 CheckNonce() 返回事务的 nonce 值。
通过方法 Value() 返回事务的以太币数量。
通过函数 NewMessage() 创建一个新的 Message, 并以传入的参数填充各字段。

Appendix B. 详细批注

var

  • ErrInvalidSig: 表示事务无效的 error 值

type Transaction struct

对象 Transaction 用于表示事务。

    data txdata:           事务的具体信息
    
    // caches
    hash atomic.Value:     事务的缓存值 - 哈希
    size atomic.Value:     事务的缓存值 - 内存大小
    from atomic.Value:     事务的缓存值 - 发送者地址
  1. func (tx *Transaction) ChainId() *big.Int

方法 ChainId() 返回此事务签署的 chain id(如果有的话)。

具体实现是将事务中的 tx.data.V 传递给函数 deriveChainId() 来计算。
  1. func (tx *Transaction) Protected() bool

方法 Protected() 返回此事务是否受重放保护。

具体实现是将事务中的 tx.data.V 传递给函数 isProtectedV() 来计算。
  1. func (tx *Transaction) EncodeRLP(w io.Writer) error

方法 EncodeRLP() 实现了 rlp.Encoder 接口,并将 Transaction 的 data 字段扁平化成 RLP 流。

将 txdata 的 RLP 编码写入到参数 w 中。

- 具体的实现是调用函数 rlp.Encode() 完成的。
  1. func (tx *Transaction) DecodeRLP(s *rlp.Stream) error

方法 DecodeRLP() 实现了 rlp.Decoder 接口,并从 RLP 流中加载 Transaction 的 data 字段。

从参数 s 中加载 data 字段。

- 具体的实现是调用方法 rlp.Stream.Decode() 完成的。
- 计算缓存值 size 字段。
  1. func (tx *Transaction) MarshalJSON() ([]byte, error)

方法 MarshalJSON 将 txdata 编码为 web3 RPC 的 JSON 流。

具体实现是调用对象 txdata 的方法 MarshalJSON()。
  1. func (tx *Transaction) UnmarshalJSON(input []byte) error

方法 UnmarshalJSON() 从 web3 RPC 的 JSON 流解码出 txdata。

具体实现是调用对象 txdata 的方法 UnmarshalJSON() 解码出字段 data。并验证签名是否有效,有效才从输入 input 恢复 Transaction。
  1. func (tx *Transaction) Data() []byte

方法 Data() 返回智能合约的 ABI 字节码。

  1. func (tx *Transaction) Gas() uint64

方法 Gas() 返回事务的 gas limit。

  1. func (tx *Transaction) GasPrice() *big.Int

方法 GasPrice() 返回事务的 gas price。

  1. func (tx *Transaction) Value() *big.Int

方法 Value() 返回事务的以太币。

  1. func (tx *Transaction) Nonce() uint64

方法 Nonce() 返回事务的账号随机数。

  1. func (tx *Transaction) CheckNonce() bool

方法 CheckNonce() 永远返回 true。

  1. func (tx *Transaction) To() *common.Address

方法 To() 返回事务的接收者地址。如果事务是创建智能合约则返回 nil。

  1. func (tx *Transaction) Hash() common.Hash

方法 Hash() 返回事务 RLP 编码的哈希值。事务哈希能够唯一标识事务。

具体实现是直接返回 Transaction 中的缓存字段 hash 的值,如果无缓存则先计算哈希值,再更新缓存。
哈希值的计算是调用函数 rlpHash() 实现的。
  1. func (tx *Transaction) Size() common.StorageSize

方法 Size() 返回事务 RLP 编码的内存大小。

具体实现是直接返回 Transaction 中的缓存字段 size 的值。
  1. func (tx *Transaction) AsMessage(s Signer) (Message, error)

方法 AsMessage() 将 Transaction 作为 core.Message 返回。

方法 AsMessage() 需要签名信息来返回发送者。

此方法表明了一个重要的信息,即事务中不需要存储发送者,但是可以通过事务和签名信息的组合恢复出发送者。
  1. func (tx Transaction) WithSignature(signer Signer, sig []byte) (Transaction, error)

方法 WithSignature() 将此事务与给定签名,返回新的带签名信息的事务。

事务新创建的时候还不知道签名信息,随着挖矿的进行再补充签名信息,要以通过此方法来完成。

具体实现是由方法 signer.SignatureValues(tx, sig) 来计算出签名信息 r, s, v。
  1. func (tx *Transaction) Cost() *big.Int

方法 Cost() 返回 amount + gasprice * gaslimit 的值。

  1. func (tx *Transaction) RawSignatureValues()

方法 RawSignatureValues() 返回事务的原始签名信息 r, s, v。

type txdata struct

数据结构 txdata 用于表示 Transaction 中的具体数据。

    AccountNonce uint64:           账户的随机数据
    Price        *big.Int:         gas 的价格
    GasLimit     uint64:           gas 的上限
    Recipient    *common.Address:  事务的接收方。nil 表示创建智能合约。
    Amount       *big.Int:         以太币的数量
    Payload      []byte:           ??? 智能合约的 ABI 字节码

    // Signature values
    V *big.Int:                    事务签名的 V 值
    R *big.Int:                    事务签名的 R 值
    S *big.Int:                    事务签名的 S 值

    // This is only used when marshaling to JSON.
    Hash *common.Hash:             事务的哈希值

type txdataMarshaling struct

数据结构 txdataMarshaling 用于表示 txdata 的 JSON 编码。

各字段的意义同 txdata 中的同名字段。

AccountNonce hexutil.Uint64
Price        *hexutil.Big
GasLimit     hexutil.Uint64
Amount       *hexutil.Big
Payload      hexutil.Bytes
V            *hexutil.Big
R            *hexutil.Big
S            *hexutil.Big

func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction

函数 NewTransaction() 创建新的事务对象。

将具体的创建过程转发给函数 newTransaction()

func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction

函数 NewContractCreation() 创建新的事务对象。且事务对象的字段 Recipient 为 nil。

将具体的创建过程转发给函数 newTransaction()

func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction

函数 NewTransaction() 负责真正的创建新的事务对象。

  • 对非 nil 的 data 进行尝试拷贝,避免同步问题。
  • 通过函数填充字段 txdata 的各字段。

func isProtectedV(V *big.Int) bool

函数 isProtectedV() 用于判定参数 V 是否是受重放保护的。

type Transactions []*Transaction

Transactions 表示 Transaction 列表的封装器。

  1. func (s Transactions) Len() int

方法 Len() 返回 Transactions 中 Transaction 的个数。

  1. func (s Transactions) Swap(i, j int)

方法 Swap() 交换 Transactions 中的第 i 个和第 j 个元素。

  1. func (s Transactions) GetRlp(i int) []byte

方法 GetRlp() 返回列表中某个 Transaction 的 RLP 编码。

func TxDifference(a, b Transactions) Transactions

函数 TxDifference() 返回一个新的 Transactions,它是 a 和 b 两个 Transactions 的差集。

type TxByNonce Transactions

TxByNonce 表示 Transactions 的封装器,并实现了 sort 接口以允许 Transactions 根据事务的 nonce 排序。这只在排序由单个账户维护的 Transactions 时才有意义,否则 nonce 的比较没有任何意义。

  1. func (s TxByNonce) Len() int

方法 Len() 返回 TxByNonce 中的 Transaction 的个数。

  1. func (s TxByNonce) Less(i, j int) bool

方法 Less() 比较 TxByNonce 中的第 i 个和第 j 个 Transaction 的字段 data.AccountNonce 的大小。

  1. func (s TxByNonce) Swap(i, j int)

方法 Swap() 交换 TxByNonce 中的第 i 个和第 j 个元素。

type TxByPrice Transactions

TxByPrice 表示 Transactions 的封装器,并实现了 sort 和 heap 接口,使其对所有一次排序以及单独添加和删除元素都很有用。

  1. func (s TxByPrice) Len() int

方法 Len() 返回 TxByPrice 中的 Transaction 的个数。

  1. func (s TxByPrice) Less(i, j int) bool

方法 Less() 比较 TxByPrice 中的第 i 个和第 j 个 Transaction 的字段 data.AccountNonce 的大小。

  1. func (s TxByPrice) Swap(i, j int)

方法 Swap() 交换 TxByPrice 中的第 i 个和第 j 个元素。

  1. func (s *TxByPrice) Push(x interface{})

方法 Push() 新增一个 Transaction。

  1. func (s *TxByPrice) Pop() interface{}

方法 Pop() 删除并返回最后一个 Transaction。

type TransactionsByPriceAndNonce struct

TransactionsByPriceAndNonce 表示一个可以根据 Transaction 最大化收益排序的 Transaction 集合,同时支持移除不可执行账户的全部事务。

注意字段 txs 和 heads 之间的关系。字段 txs 表示每个账户对应的事务列表(且事务根据 nonce 排序),而字段 heads 是由 txs 中每个账户的第一个事务构成的,并根据 price 排序的事务列表。

txs    map[common.Address]Transactions:    每个账户根据 nonce 排序的 Transaction 列表
heads  TxByPrice:                          每个账户的下一个事务(price heap)
signer Signer:                             Transaction 集合的签名者

需要说明的是,每个账户地址可以发送多个交易,通过映射 txs 描述。其中,对于每个账户,其发起的交易列表是根据 Nonce 升序排序的。从每个账户中选择一个优化级最高的事务,组成交易列表 heads,根据每个交易的 Price 进行降序排列。heads[0] 代表的交易是下一个需要被处理的优先级最高的交易,通过方法 Peek() 获得,通过方法 Pop() 删除。调用 Pop() 之后,也就不会再处理 heads[0] 交易的发起者的所有交易了。这个实现也太 6 了吧。

方法 Shift() 的作用是,对于已经被处理了的交易 heads[0],对该交易发起者 acc 维护的交易列表 txs = t.txs[acc] 做下列操作:1. 将交易列表 txs 中的第 1 个元素 txs[0] 赋值给 heads[0],并通过 heap.Fix() 方法重新排序 heads。2. 删除交易列表 txs 中的第 1 个元素。

  1. func (t *TransactionsByPriceAndNonce) Peek() *Transaction

方法 Peek() 返回下一个价格最高的 Transaction。

  1. func (t *TransactionsByPriceAndNonce) Shift()

方法 Shift() 采用如下规则替换字段 heads 中第一个事务,首先通过组合签名者计算出该事务的地址,然后从字段 txs 中获取该地址的下一个事务(并将新事务从该地址对应的事务列表中删除)。

具体实现需要注意两点:

  • 同一地址原有的事务列表需要删除被取出的事务。
  • 字段 heads 对应的 heap 需要重新排序。

方法 Shift() 与方法 Pop() 的差别在于:

  • 方法 Shift() 获取当前已被处理的交易(即优化级最高的交易 heads[0])的发起者账户的下一个交易,并将该账户的列表中剔除这个被处理的交易,并将该账户优化级最高的交易放入 heads 中,并排序 heads。
  • 方法 Pop() 直接删除当前已被处理的交易((即优化级最高的交易 heads[0])。
  1. func (t *TransactionsByPriceAndNonce) Pop()

方法 Pop() 移除最好的事务,而不是像 Shift() 那样用同一个地址的下一个事务来替换。

当一个事务不能被执行,因此该事务同一地址的所有后续事务都应该被丢弃时,使用此方法。

func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce

函数 NewTransactionsByPriceAndNonce 创建对象 TransactionsByPriceAndNonce。

具体实现需要注意的地方有三点:

  • 参数 txs 传递的是值,在函数内部会被修改。
  • 参数 txs 每个 common.Address 对应的第一个事务会从原列表中拿出来构成 heads,而原有的列表则少了一个 Transaction。
  • 对于每个 common.Address 的事务列表,都会通过参数 signer 和事务恢复出地址来进行验证,验证失败则从 txs 中删除该地址对应的事务列表。

type Message struct

Message 是一个完全派生的 Transaction,并且实现了 core.Message。

Message 各字段的含义与 Transaction 的同名字段或对应的 json 注解一致。

to         *common.Address
from       common.Address
nonce      uint64
amount     *big.Int
gasLimit   uint64
gasPrice   *big.Int
data       []byte
checkNonce bool

func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message

函数 NewMessage() 创建一个新的 Message, 并以传入的参数填充各字段。

func (m Message) From() common.Address

方法 From() 返回发送者地址。

func (m Message) To() *common.Address

方法 To() 返回接收者地址。

func (m Message) GasPrice() *big.Int

方法 GasPrice() 返回事务的 gas price。

func (m Message) Value() *big.Int

方法 Value() 返回事务的以太币数量。

func (m Message) Gas() uint64

方法 Gas() 返回事务的 gas limit。

func (m Message) Nonce() uint64

方法 Nonce() 返回事务的随机数值。

func (m Message) Data() []byte

方法 Data() 返回事务的智能合约 ABI 字节码。

func (m Message) CheckNonce() bool

方法 CheckNonce() 返回事务的 nonce 值。

Reference

  1. https://github.com/ethereum/go-ethereum/blob/master/core/types/derive_sha.go

Contributor

  1. Windstamp, https://github.com/windstamp
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 看一部电影等于看十部 看书加速要用手指 年轻人必须做的六/十三/二十件事 想成为女神/男神/学霸/作家,现在还不晚...
    修冰箱的小王阅读 199评论 2 1
  • 第一章 我生于1995年3月,农历为乙亥猪年二月初九,大概是因为出生在辰时,再取朝气蓬勃之意,故取名为李朝阳。 我...
    雅痞不是痞阅读 360评论 0 2
  • 有这样一个人 我们从小一起长大 我们是最好的朋友 小时候无话不说 我们约定 我们之间分享一切 没有秘密 我那个时候...
    几许清渠阅读 135评论 0 0
  • 如果时光可以倒流, 我一定好好学习; 而不是像现在这样懊悔。 如果时光可以倒流, 就应该听妈妈的话; 踏入社会前应...
    尹伊静阅读 131评论 0 0