From 4e1fc9bc25891311e6351456d147001b079a9936 Mon Sep 17 00:00:00 2001 From: lightclient Date: Mon, 5 Jan 2026 06:55:18 -0700 Subject: [PATCH] core: extend TxContext to have all data related to TXPARAM* family --- core/evm.go | 44 ++++++++++++++++++++ core/state_transition.go | 2 + core/vm/eips.go | 90 ++++++++++++++++++++++++++++++---------- core/vm/evm.go | 24 ++++++++++- core/vm/runtime/env.go | 6 +++ 5 files changed, 143 insertions(+), 23 deletions(-) diff --git a/core/evm.go b/core/evm.go index 18d940fdd2..594d8d26d2 100644 --- a/core/evm.go +++ b/core/evm.go @@ -25,6 +25,8 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/uint256" ) @@ -82,13 +84,55 @@ func NewEVMTxContext(msg *Message) vm.TxContext { Origin: msg.From, GasPrice: new(big.Int).Set(msg.GasPrice), BlobHashes: msg.BlobHashes, + + // EIP-7701 fields + TxType: msg.TxType, + Nonce: msg.Nonce, + Sender: msg.Sender, + Deployer: msg.Deployer, + Paymaster: msg.Paymaster, + SenderExecutionData: msg.Data, + SenderExecutionGas: msg.GasLimit, } if msg.BlobGasFeeCap != nil { ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap) } + if msg.GasTipCap != nil { + ctx.MaxPriorityFeePerGas = new(big.Int).Set(msg.GasTipCap) + } + if msg.GasFeeCap != nil { + ctx.MaxFeePerGas = new(big.Int).Set(msg.GasFeeCap) + } + + // Compute AccessListHash + if len(msg.AccessList) > 0 { + ctx.AccessListHash = rlpHash(msg.AccessList) + } + + // Compute AuthorizationListHash + if len(msg.SetCodeAuthorizations) > 0 { + ctx.AuthorizationListHash = rlpHash(msg.SetCodeAuthorizations) + } + + // TxHashForSignature is nil for AA transactions + // For non-AA transactions, we leave it nil as we don't have access to the + // original transaction here. If needed, this should be set by the caller. + if !msg.Abstract { + // TODO: compute TxHashForSignature for non-AA transactions if needed + } + return ctx } +// rlpHash computes the keccak256 hash of the RLP encoding of x. +func rlpHash(x interface{}) common.Hash { + encoded, err := rlp.EncodeToBytes(x) + if err != nil { + return common.Hash{} + } + return crypto.Keccak256Hash(encoded) +} + // GetHashFn returns a GetHashFunc which retrieves header hashes by number func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { // Cache will initially contain [refHash.parent], diff --git a/core/state_transition.go b/core/state_transition.go index f38afdf404..12eec70a8b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -146,6 +146,7 @@ func toWordSize(size uint64) uint64 { // A Message contains the data derived from a single transaction that is relevant to state // processing. type Message struct { + TxType uint8 To *common.Address From common.Address Nonce uint64 @@ -187,6 +188,7 @@ type Message struct { // TransactionToMessage converts a transaction into a Message. func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { msg := &Message{ + TxType: tx.Type(), Nonce: tx.Nonce(), GasLimit: tx.Gas(), GasPrice: new(big.Int).Set(tx.GasPrice()), diff --git a/core/vm/eips.go b/core/vm/eips.go index 03d7f642c2..d9870df5dc 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -599,82 +599,128 @@ func opAcceptRole(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { type paramLoader func(*EVM, uint64) []byte +// uint64ToBytes32 converts a uint64 to a 32-byte big-endian slice. +func uint64ToBytes32(v uint64) []byte { + return common.LeftPadBytes(new(uint256.Int).SetUint64(v).Bytes(), 32) +} + func loadType(evm *EVM, id uint64) []byte { - // TODO: plumb in type to TxContext - return nil + return uint64ToBytes32(uint64(evm.TxContext.TxType)) } func loadNonce(evm *EVM, id uint64) []byte { - // TODO: plumb in nonce to TxContext - return nil + return uint64ToBytes32(evm.TxContext.Nonce) } func loadSender(evm *EVM, id uint64) []byte { - return nil + return common.LeftPadBytes(evm.TxContext.Origin.Bytes(), 32) } func loadSenderValidationData(evm *EVM, id uint64) []byte { - return nil + if evm.TxContext.Sender == nil { + return nil + } + return evm.TxContext.Sender.Data } func loadDeployer(evm *EVM, id uint64) []byte { - return nil + // 0 or 32 bytes: return nil when not set + if evm.TxContext.Deployer == nil { + return nil + } + return common.LeftPadBytes(evm.TxContext.Deployer.Target.Bytes(), 32) } func loadDeployerData(evm *EVM, id uint64) []byte { - return nil + // dynamic, default empty array + if evm.TxContext.Deployer == nil { + return nil + } + return evm.TxContext.Deployer.Data } func loadPaymaster(evm *EVM, id uint64) []byte { - return nil + // 0 or 32 bytes: return nil when not set + if evm.TxContext.Paymaster == nil { + return nil + } + return common.LeftPadBytes(evm.TxContext.Paymaster.Target.Bytes(), 32) } func loadPaymasterData(evm *EVM, id uint64) []byte { - return nil + // dynamic, default empty array + if evm.TxContext.Paymaster == nil { + return nil + } + return evm.TxContext.Paymaster.Data } func loadSenderExecutionData(evm *EVM, id uint64) []byte { - return nil + return evm.TxContext.SenderExecutionData } func loadMaxPriorityFeePerGas(evm *EVM, id uint64) []byte { - return nil + if evm.TxContext.MaxPriorityFeePerGas == nil { + return make([]byte, 32) + } + return common.LeftPadBytes(evm.TxContext.MaxPriorityFeePerGas.Bytes(), 32) } func loadMaxFeePerGas(evm *EVM, id uint64) []byte { - return nil + if evm.TxContext.MaxFeePerGas == nil { + return make([]byte, 32) + } + return common.LeftPadBytes(evm.TxContext.MaxFeePerGas.Bytes(), 32) +} + +func loadSenderValidationGas(evm *EVM, id uint64) []byte { + if evm.TxContext.Sender == nil { + return uint64ToBytes32(0) + } + return uint64ToBytes32(evm.TxContext.Sender.Gas) } func loadPaymasterValidationGas(evm *EVM, id uint64) []byte { - return nil + // 32 bytes, default 0 + if evm.TxContext.Paymaster == nil { + return uint64ToBytes32(0) + } + return uint64ToBytes32(evm.TxContext.Paymaster.Gas) } func loadSenderExecutionGas(evm *EVM, id uint64) []byte { - return nil + return uint64ToBytes32(evm.TxContext.SenderExecutionGas) } func loadPaymasterPostOpGas(evm *EVM, id uint64) []byte { - return nil + // 32 bytes, default 0 + if evm.TxContext.Paymaster == nil { + return uint64ToBytes32(0) + } + return uint64ToBytes32(evm.TxContext.Paymaster.PostOpGas) } func loadAccessListHash(evm *EVM, id uint64) []byte { - return nil + return evm.TxContext.AccessListHash[:] } func loadAuthorizationListHash(evm *EVM, id uint64) []byte { - return nil + return evm.TxContext.AuthorizationListHash[:] } func loadExecutionStatus(evm *EVM, id uint64) []byte { - return nil + return uint64ToBytes32(evm.TxContext.ExecutionStatus) } func loadExecutionGasUsed(evm *EVM, id uint64) []byte { - return nil + return uint64ToBytes32(evm.TxContext.ExecutionGasUsed) } func loadTxHashForSignature(evm *EVM, id uint64) []byte { - return nil + if evm.TxContext.TxHashForSignature == nil { + return nil + } + return evm.TxContext.TxHashForSignature[:] } // idToParam is a map of identifier to transaction parameter loader. @@ -690,7 +736,7 @@ var idToParam = map[uint64]paramLoader{ 0x08: loadSenderExecutionData, 0x0B: loadMaxPriorityFeePerGas, 0x0C: loadMaxFeePerGas, - 0x0D: loadSenderValidationData, + 0x0D: loadSenderValidationGas, 0x0E: loadPaymasterValidationGas, 0x0F: loadSenderExecutionGas, 0x10: loadPaymasterPostOpGas, diff --git a/core/vm/evm.go b/core/vm/evm.go index 913ceb9fb1..7f6809c2d7 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -80,7 +80,29 @@ type TxContext struct { BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set AccessEvents *state.AccessEvents // Capture all state accesses for this tx - // TODO: add EIP-7701 fields + // EIP-7701: Transaction parameters for TXPARAM* opcodes + TxType uint8 // 0x00: transaction type + Nonce uint64 // 0x01: transaction nonce + // Origin is sender (0x02) + + // AA authorization structs (nil for non-AA txs) + Sender *types.AbstractAuthorization // 0x03: Data, 0x0D: Gas + Deployer *types.AbstractAuthorization // 0x04: Target, 0x05: Data + Paymaster *types.PaymasterAuthorization // 0x06: Target, 0x07: Data, 0x0E: Gas, 0x10: PostOpGas + + SenderExecutionData []byte // 0x08: execution calldata + SenderExecutionGas uint64 // 0x0F: execution gas limit + MaxPriorityFeePerGas *big.Int // 0x0B: max priority fee per gas + MaxFeePerGas *big.Int // 0x0C: max fee per gas + + // Hashes (computed when creating TxContext) + AccessListHash common.Hash // 0x11: hash of access list + AuthorizationListHash common.Hash // 0x12: hash of authorization list + TxHashForSignature *common.Hash // 0xff: hash for signature (nil for AA txs) + + // Mutable execution state (set during AA tx processing) + ExecutionStatus uint64 // 0xf1: execution result status + ExecutionGasUsed uint64 // 0xf2: gas used during execution } // EVM is the Ethereum Virtual Machine base object and provides diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index e54041f7e2..3fb32d9f2e 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -27,6 +27,12 @@ func NewEnv(cfg *Config) *vm.EVM { GasPrice: cfg.GasPrice, BlobHashes: cfg.BlobHashes, BlobFeeCap: cfg.BlobFeeCap, + + // EIP-7701: Set basic fields for non-AA transactions + // AA-specific fields (Sender, Deployer, Paymaster) remain nil + MaxPriorityFeePerGas: cfg.GasPrice, + MaxFeePerGas: cfg.GasPrice, + SenderExecutionGas: cfg.GasLimit, } blockContext := vm.BlockContext{ CanTransfer: core.CanTransfer,