理解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结构体和手续费TxByPriceAndTime:TxWithMinerFee数组,先根据矿工手续费排序,再根据交易时间排序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"`
}