理解Geth--交易

交易定义

以太坊中的交易包括三种类型:

const (
	LegacyTxType = iota
	AccessListTxType
	DynamicFeeTxType
)

交易的结构定义如下:

type Transaction struct {
	inner TxData    // Consensus contents of a transaction
	time  time.Time // Time first seen locally (spam avoidance)

	// caches
	hash atomic.Value // 使用原子(store+load)类型,保证一致性
	size atomic.Value
	from atomic.Value
}

TxData是核心结构,在代码中定义为interface,上面提到的三种类型分别实现了TxData接口:

此接口的功能就是把tx内的数据做了读返回

type TxData interface {
	txType() byte // returns the type ID
	copy() TxData // creates a deep copy and initializes all fields

	chainID() *big.Int
	accessList() AccessList
	data() []byte
	gas() uint64
	gasPrice() *big.Int
	gasTipCap() *big.Int
	gasFeeCap() *big.Int
	value() *big.Int
	nonce() uint64
	to() *common.Address

	rawSignatureValues() (v, r, s *big.Int)
	setSignatureValues(chainID, v, r, s *big.Int)
}

交易编解码

transaction对外提供了多种编解码的方法:

func (t *Transaction) MarshalJSON() ([]byte, error)
func (t *Transaction) UnmarshalJSON(input []byte) error
func (tx *Transaction) EncodeRLP(w io.Writer) error
func (tx *Transaction) MarshalBinary() ([]byte, error)
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error
func (tx *Transaction) UnmarshalBinary(b []byte) error

矿工视角的交易

transaction的多种封装结构:

  • TxByNonce:tx数组,实现了sort接口,根据nonce排序
  • TxWithMinerFee:封装了tx结构体和手续费
  • TxByPriceAndTimeTxWithMinerFee数组,先根据矿工手续费排序,再根据交易时间排序
  • TransactionsByPriceAndNonce:记录了同一区块内的交易,并构建好TxByPriceAndTime 结构
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *big.Int) *TransactionsByPriceAndNonce {
	// 每个区块的baseFee是相同的
	// Initialize a price and received time based heap with the head transactions
	heads := make(TxByPriceAndTime, 0, len(txs))
	for from, accTxs := range txs {
		acc, _ := Sender(signer, accTxs[0]) // 解析出地址
		wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee)
		// Remove transaction if sender doesn't match from, or if wrapping fails.
		if acc != from || err != nil {
			delete(txs, from)
			continue
		}
		heads = append(heads, wrapped)
		txs[from] = accTxs[1:] // 每次只处理一个交易
	}
	heap.Init(&heads)

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

EffectiveGasTip 用来计算矿工盈利的数目

func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
	if baseFee == nil {
		return tx.GasTipCap(), nil
	}
	var err error
	gasFeeCap := tx.GasFeeCap()
	if gasFeeCap.Cmp(baseFee) == -1 {
		// gasFeeCap是上限,若baseFee比cap还大,报错
		err = ErrGasFeeCapTooLow
	}
	// 对比 tip小费和(cap-base),选较小值
	return math.BigMin(tx.GasTipCap(), gasFeeCap.Sub(gasFeeCap, baseFee)), err
}

Message结构是对transaction的转换,用于合约内:

// 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
	gasFeeCap  *big.Int
	gasTipCap  *big.Int
	data       []byte
	accessList AccessList
	isFake     bool
}

这里是从transaction转换到message的过程:

// AsMessage returns the transaction as a core.Message.
func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
	msg := Message{
		nonce:      tx.Nonce(),
		gasLimit:   tx.Gas(),
		gasPrice:   new(big.Int).Set(tx.GasPrice()),
		gasFeeCap:  new(big.Int).Set(tx.GasFeeCap()),
		gasTipCap:  new(big.Int).Set(tx.GasTipCap()),
		to:         tx.To(),
		amount:     tx.Value(),
		data:       tx.Data(),
		accessList: tx.AccessList(),
		isFake:     false,
	}
	// If baseFee provided, set gasPrice to effectiveGasPrice.
	if baseFee != nil {
	  // 如果给了baseFee,那么按min(tip+base, cap)来设置gasPrice
		msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
	}
	var err error
	msg.from, err = Sender(s, tx)
	return msg, err
}

交易的演变

在看完transaction的所有相关结构后再回到开头定义的三个类型,是随着EIP的提出而新增的:

  • LegacyTxType:最基础的结构,定义如下:
type LegacyTx struct {
	Nonce    uint64          // nonce of sender account
	GasPrice *big.Int        // wei per gas
	Gas      uint64          // gas limit
	To       *common.Address `rlp:"nil"` // nil means contract creation
	Value    *big.Int        // wei amount
	Data     []byte          // contract invocation input data
	V, R, S  *big.Int        // signature values
}
  • AccessListTxType **:在交易结构内新增accesslist,对应的结构是AccessListTx,新增了AccessList,即记录了访问地址和存储slot
// AccessListTx is the data of EIP-2930 access list transactions.
type AccessListTx struct {
	ChainID    *big.Int        // destination chain ID
	Nonce      uint64          // nonce of sender account
	GasPrice   *big.Int        // wei per gas
	Gas        uint64          // gas limit
	To         *common.Address `rlp:"nil"` // nil means contract creation
	Value      *big.Int        // wei amount
	Data       []byte          // contract invocation input data
	AccessList AccessList      // EIP-2930 access list 新增的access-list
	V, R, S    *big.Int        // signature values
}

// AccessList is an EIP-2930 access list.
type AccessList []AccessTuple

// AccessTuple is the element type of an access list.
type AccessTuple struct {
	Address     common.Address `json:"address"        gencodec:"required"`
	StorageKeys []common.Hash  `json:"storageKeys"    gencodec:"required"`
}
  • DynamicFeeTxType :在上面的基础之上又新增了GasTipCap和GasFeeCap,这些是EIP-1559引入的手续费机制,将gas费用分为base(基础)+tip(小费)两部分,每个区块内的交易的base是相同的,会直接销毁,只有tip会发给矿工,这两个变量的存在与Gas是互斥的,这也是上面许多地方针对这里做了if-else逻辑的原因
type DynamicFeeTx struct {
	ChainID    *big.Int
	Nonce      uint64
	GasTipCap  *big.Int // a.k.a. maxPriorityFeePerGas 给矿工的小费
	GasFeeCap  *big.Int // a.k.a. maxFeePerGas 每个区块的基础费用,会被销毁
	Gas        uint64
	To         *common.Address `rlp:"nil"` // nil means contract creation
	Value      *big.Int
	Data       []byte
	AccessList AccessList

	// Signature values
	V *big.Int `json:"v" gencodec:"required"`
	R *big.Int `json:"r" gencodec:"required"`
	S *big.Int `json:"s" gencodec:"required"`
}
comments powered by Disqus