all: initial sketch of 7701

This commit is contained in:
lightclient 2026-01-03 11:01:25 -07:00
parent 57f84866bc
commit 2859da852d
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
18 changed files with 703 additions and 27 deletions

View file

@ -134,7 +134,7 @@ func Transaction(ctx *cli.Context) error {
} }
// Check intrinsic gas // Check intrinsic gas
rules := chainConfig.Rules(common.Big0, true, 0) rules := chainConfig.Rules(common.Big0, true, 0)
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, tx.SenderAuthorization() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
if err != nil { if err != nil {
r.Error = err r.Error = err
results = append(results, r) results = append(results, r)

View file

@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
data := make([]byte, nbytes) data := make([]byte, nbytes)
return func(i int, gen *BlockGen) { return func(i int, gen *BlockGen) {
toaddr := common.Address{} toaddr := common.Address{}
gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false) gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false, false)
signer := gen.Signer() signer := gen.Signer()
gasPrice := big.NewInt(0) gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil { if gen.header.BaseFee != nil {

View file

@ -63,12 +63,12 @@ var (
func TestProcessVerkle(t *testing.T) { func TestProcessVerkle(t *testing.T) {
var ( var (
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true) intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true, false)
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness // A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
// will not contain that copied data. // will not contain that copied data.
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985 // Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`) codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true) intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true, false)
signer = types.LatestSigner(testVerkleChainConfig) signer = types.LatestSigner(testVerkleChainConfig)
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm/roles"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
@ -68,11 +69,13 @@ func (result *ExecutionResult) Revert() []byte {
} }
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. // IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool, isAbstract bool) (uint64, error) {
// Set the starting gas for the raw transaction // Set the starting gas for the raw transaction
var gas uint64 var gas uint64
if isContractCreation && isHomestead { if isContractCreation && isHomestead {
gas = params.TxGasContractCreation gas = params.TxGasContractCreation
} else if isAbstract {
gas = params.TxGasAbstract
} else { } else {
gas = params.TxGas gas = params.TxGas
} }
@ -157,6 +160,15 @@ type Message struct {
BlobHashes []common.Hash BlobHashes []common.Hash
SetCodeAuthorizations []types.SetCodeAuthorization SetCodeAuthorizations []types.SetCodeAuthorization
Abstract bool
Sender *types.AbstractAuthorization
Deployer *types.AbstractAuthorization
Paymaster *types.PaymasterAuthorization
Payer common.Address
AllData []byte
TotalGasLimit uint64
// When SkipNonceChecks is true, the message nonce is not checked against the // When SkipNonceChecks is true, the message nonce is not checked against the
// account nonce in state. // account nonce in state.
// //
@ -189,6 +201,14 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
SkipTransactionChecks: false, SkipTransactionChecks: false,
BlobHashes: tx.BlobHashes(), BlobHashes: tx.BlobHashes(),
BlobGasFeeCap: tx.BlobGasFeeCap(), BlobGasFeeCap: tx.BlobGasFeeCap(),
Abstract: tx.SenderAuthorization() != nil,
Sender: tx.SenderAuthorization(),
Deployer: tx.DeployerAuthorization(),
Paymaster: tx.PaymasterAuthorization(),
AllData: common.CopyBytes(tx.Data()),
TotalGasLimit: tx.Gas(),
} }
// If baseFee provided, set gasPrice to effectiveGasPrice. // If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil { if baseFee != nil {
@ -199,6 +219,27 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
} }
var err error var err error
msg.From, err = types.Sender(s, tx) msg.From, err = types.Sender(s, tx)
msg.Payer = msg.From
if msg.Abstract {
msg.Payer = msg.Paymaster.Target
msg.SkipNonceChecks = true
}
// Sum all gas limits if AA.
if msg.Abstract {
msg.AllData = append(msg.AllData, msg.Sender.Data...)
msg.TotalGasLimit += msg.Sender.Gas
if msg.Deployer != nil {
msg.AllData = append(msg.AllData, msg.Deployer.Data...)
msg.TotalGasLimit += msg.Deployer.Gas
}
if msg.Paymaster != nil {
msg.AllData = append(msg.AllData, msg.Paymaster.Data...)
msg.TotalGasLimit += msg.Paymaster.Gas
}
}
return msg, err return msg, err
} }
@ -264,11 +305,11 @@ func (st *stateTransition) to() common.Address {
} }
func (st *stateTransition) buyGas() error { func (st *stateTransition) buyGas() error {
mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval := new(big.Int).SetUint64(st.msg.TotalGasLimit)
mgval.Mul(mgval, st.msg.GasPrice) mgval.Mul(mgval, st.msg.GasPrice)
balanceCheck := new(big.Int).Set(mgval) balanceCheck := new(big.Int).Set(mgval)
if st.msg.GasFeeCap != nil { if st.msg.GasFeeCap != nil {
balanceCheck.SetUint64(st.msg.GasLimit) balanceCheck.SetUint64(st.msg.TotalGasLimit)
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap) balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
} }
balanceCheck.Add(balanceCheck, st.msg.Value) balanceCheck.Add(balanceCheck, st.msg.Value)
@ -289,21 +330,21 @@ func (st *stateTransition) buyGas() error {
if overflow { if overflow {
return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex()) return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
} }
if have, want := st.state.GetBalance(st.msg.From), balanceCheckU256; have.Cmp(want) < 0 { if have, want := st.state.GetBalance(st.msg.Payer), balanceCheckU256; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want) return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.Payer.Hex(), have, want)
} }
if err := st.gp.SubGas(st.msg.GasLimit); err != nil { if err := st.gp.SubGas(st.msg.TotalGasLimit); err != nil {
return err return err
} }
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance) st.evm.Config.Tracer.OnGasChange(0, st.msg.TotalGasLimit, tracing.GasChangeTxInitialBalance)
} }
st.gasRemaining = st.msg.GasLimit st.gasRemaining = st.msg.TotalGasLimit
st.initialGas = st.msg.GasLimit st.initialGas = st.msg.TotalGasLimit
mgvalU256, _ := uint256.FromBig(mgval) mgvalU256, _ := uint256.FromBig(mgval)
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy) st.state.SubBalance(st.msg.Payer, mgvalU256, tracing.BalanceDecreaseGasBuy)
return nil return nil
} }
@ -327,8 +368,8 @@ func (st *stateTransition) preCheck() error {
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
if !msg.SkipTransactionChecks { if !msg.SkipTransactionChecks {
// Verify tx gas limit does not exceed EIP-7825 cap. // Verify tx gas limit does not exceed EIP-7825 cap.
if isOsaka && msg.GasLimit > params.MaxTxGas { if isOsaka && msg.TotalGasLimit > params.MaxTxGas {
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit) return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.TotalGasLimit)
} }
// Make sure the sender is an EOA // Make sure the sender is an EOA
code := st.state.GetCode(msg.From) code := st.state.GetCode(msg.From)
@ -443,7 +484,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
) )
// Check clauses 4-5, subtract intrinsic gas if everything is correct // Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) gas, err := IntrinsicGas(msg.AllData, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai, msg.Abstract)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -452,11 +493,11 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
// Gas limit suffices for the floor data cost (EIP-7623) // Gas limit suffices for the floor data cost (EIP-7623)
if rules.IsPrague { if rules.IsPrague {
floorDataGas, err = FloorDataGas(msg.Data) floorDataGas, err = FloorDataGas(msg.AllData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if msg.GasLimit < floorDataGas { if msg.TotalGasLimit < floorDataGas {
return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas) return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas)
} }
} }
@ -471,6 +512,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
if targetAddr := msg.To; targetAddr != nil { if targetAddr := msg.To; targetAddr != nil {
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0, !st.state.Exist(*targetAddr)) st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0, !st.state.Exist(*targetAddr))
} }
if msg.Deployer != nil {
target := msg.Deployer.Target
st.evm.AccessEvents.AddTxDestination(target, false, !st.state.Exist(target))
}
if msg.Paymaster != nil {
target := msg.Paymaster.Target
st.evm.AccessEvents.AddTxDestination(target, false, !st.state.Exist(target))
}
} }
// Check clause 6 // Check clause 6
@ -492,12 +541,44 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// - reset transient storage(eip 1153) // - reset transient storage(eip 1153)
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList) st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
// Run the abstract validations:
// - account deployment, in case the user's account does not yet exist
// - standard validation, i.e. nonce and authorization checks
// - paymaster validation, when the user operation is sponsored
if msg.Abstract {
// Attempt to deploy user's smart account.
if msg.Deployer != nil && len(st.state.GetCode(msg.From)) != 0 {
remaining, err := st.evm.CallWithRole(msg.Deployer.Target, msg.Deployer.Gas, roles.SenderDeployment)
if err != nil {
return nil, fmt.Errorf("deployer failed: %v", err)
}
st.gasRemaining += remaining
}
// Validate abstract tx.
remaining, err := st.evm.CallWithRole(msg.Sender.Target, msg.Sender.Gas, roles.SenderValidation)
if err != nil {
return nil, fmt.Errorf("sender validation failed: %v", err)
}
st.gasRemaining += remaining
// Validate paymaster promise to sponsor.
if msg.Paymaster != nil {
remaining, err := st.evm.CallWithRole(msg.Paymaster.Target, msg.Paymaster.Gas, roles.PaymasterValidation)
if err != nil {
return nil, fmt.Errorf("paymaster failed: %v", err)
}
st.gasRemaining += remaining
}
}
var ( var (
ret []byte ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err vmerr error // vm errors do not effect consensus and are therefore not assigned to err
remaining uint64
) )
if contractCreation { if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value) ret, _, remaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value)
} else { } else {
// Increment the nonce for the next transaction. // Increment the nonce for the next transaction.
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall) st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
@ -520,9 +601,11 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
// Execute the transaction's call. // Execute the transaction's call.
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value) ret, remaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
} }
st.gasRemaining += remaining
// Record the gas used excluding gas refunds. This value represents the actual // Record the gas used excluding gas refunds. This value represents the actual
// gas allowance required to complete execution. // gas allowance required to complete execution.
peakGasUsed := st.gasUsed() peakGasUsed := st.gasUsed()

View file

@ -122,7 +122,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
} }
// Ensure the transaction has more gas than the bare minimum needed to cover // Ensure the transaction has more gas than the bare minimum needed to cover
// the transaction metadata // the transaction metadata
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai) intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai, tx.SenderAuthorization() == nil)
if err != nil { if err != nil {
return err return err
} }

View file

@ -50,6 +50,7 @@ const (
DynamicFeeTxType = 0x02 DynamicFeeTxType = 0x02
BlobTxType = 0x03 BlobTxType = 0x03
SetCodeTxType = 0x04 SetCodeTxType = 0x04
AbstractTxType = 0x06 // skip 0x05 since it magic for set code tx
) )
// Transaction is an Ethereum transaction. // Transaction is an Ethereum transaction.
@ -535,6 +536,30 @@ func (tx *Transaction) SetCodeAuthorities() []common.Address {
return auths return auths
} }
func (tx *Transaction) SenderAuthorization() *AbstractAuthorization {
abstracttx, ok := tx.inner.(*AbstractTx)
if !ok {
return nil
}
return abstracttx.Sender.copy()
}
func (tx *Transaction) DeployerAuthorization() *AbstractAuthorization {
abstracttx, ok := tx.inner.(*AbstractTx)
if !ok {
return nil
}
return abstracttx.Deployer.copy()
}
func (tx *Transaction) PaymasterAuthorization() *PaymasterAuthorization {
abstracttx, ok := tx.inner.(*AbstractTx)
if !ok {
return nil
}
return abstracttx.Paymaster.copy()
}
// SetTime sets the decoding time of a transaction. This is used by tests to set // SetTime sets the decoding time of a transaction. This is used by tests to set
// arbitrary times and by persistent transaction pools when loading old txs from // arbitrary times and by persistent transaction pools when loading old txs from
// disk. // disk.

View file

@ -231,6 +231,9 @@ func newModernSigner(chainID *big.Int, fork forks.Fork) Signer {
if fork >= forks.Prague { if fork >= forks.Prague {
s.txtypes.set(SetCodeTxType) s.txtypes.set(SetCodeTxType)
} }
if fork >= forks.Bogota {
s.txtypes.set(AbstractTxType)
}
return s return s
} }
@ -262,6 +265,9 @@ func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) {
if tx.ChainId().Cmp(s.chainID) != 0 { if tx.ChainId().Cmp(s.chainID) != 0 {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID) return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID)
} }
if tt == AbstractTxType {
return tx.SenderAuthorization().Target, nil
}
// 'modern' txs are defined to use 0 and 1 as their recovery // 'modern' txs are defined to use 0 and 1 as their recovery
// id, add 27 to become equivalent to unprotected Homestead signatures. // id, add 27 to become equivalent to unprotected Homestead signatures.
V, R, S := tx.RawSignatureValues() V, R, S := tx.RawSignatureValues()
@ -287,6 +293,18 @@ func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *bi
return R, S, V, nil return R, S, V, nil
} }
// NewBogotaSigner returns a signer that accepts
// - EIP-7701 abstraction transactions
// - EIP-7702 set code transactions
// - EIP-4844 blob transactions
// - EIP-1559 dynamic fee transactions
// - EIP-2930 access list transactions,
// - EIP-155 replay protected transactions, and
// - legacy Homestead transactions.
func NewBogotaSigner(chainId *big.Int) Signer {
return newModernSigner(chainId, forks.Bogota)
}
// NewPragueSigner returns a signer that accepts // NewPragueSigner returns a signer that accepts
// - EIP-7702 set code transactions // - EIP-7702 set code transactions
// - EIP-4844 blob transactions // - EIP-4844 blob transactions

155
core/types/tx_abstract.go Normal file
View file

@ -0,0 +1,155 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package types
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
type AbstractAuthorization struct {
Target common.Address
Data []byte
Gas uint64
}
func (a *AbstractAuthorization) copy() *AbstractAuthorization {
if a == nil {
return nil
}
return &AbstractAuthorization{
Target: a.Target,
Data: common.CopyBytes(a.Data),
Gas: a.Gas,
}
}
type PaymasterAuthorization struct {
Target common.Address
Data []byte
Gas uint64
PostOpGas uint64
}
func (a *PaymasterAuthorization) copy() *PaymasterAuthorization {
if a == nil {
return nil
}
return &PaymasterAuthorization{
Target: a.Target,
Data: common.CopyBytes(a.Data),
Gas: a.Gas,
PostOpGas: a.PostOpGas,
}
}
type AbstractTx struct {
ChainID *uint256.Int
Nonce uint64
Sender AbstractAuthorization
Deployer *AbstractAuthorization
Paymaster *PaymasterAuthorization
Data []byte // FromExecutionData
Gas uint64
GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
AccessList AccessList
AuthList []SetCodeAuthorization
}
// copy creates a deep copy of the transaction data and initializes all fields.
func (tx *AbstractTx) copy() TxData {
cpy := &AbstractTx{
Nonce: tx.Nonce,
Sender: *tx.Sender.copy(),
Deployer: tx.Deployer.copy(),
Paymaster: tx.Paymaster.copy(),
Data: common.CopyBytes(tx.Data),
Gas: tx.Gas,
// These are copied below.
AccessList: make(AccessList, len(tx.AccessList)),
AuthList: make([]SetCodeAuthorization, len(tx.AuthList)),
ChainID: new(uint256.Int),
GasTipCap: new(uint256.Int),
GasFeeCap: new(uint256.Int),
}
copy(cpy.AccessList, tx.AccessList)
copy(cpy.AuthList, tx.AuthList)
if tx.ChainID != nil {
cpy.ChainID.Set(tx.ChainID)
}
if tx.GasTipCap != nil {
cpy.GasTipCap.Set(tx.GasTipCap)
}
if tx.GasFeeCap != nil {
cpy.GasFeeCap.Set(tx.GasFeeCap)
}
return cpy
}
// accessors for innerTx.
func (tx *AbstractTx) txType() byte { return AbstractTxType }
func (tx *AbstractTx) chainID() *big.Int { return tx.ChainID.ToBig() }
func (tx *AbstractTx) accessList() AccessList { return tx.AccessList }
func (tx *AbstractTx) data() []byte { return tx.Data }
func (tx *AbstractTx) gas() uint64 { return tx.Gas }
func (tx *AbstractTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *AbstractTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
func (tx *AbstractTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *AbstractTx) value() *big.Int { return big.NewInt(0) }
func (tx *AbstractTx) nonce() uint64 { return tx.Nonce }
func (tx *AbstractTx) to() *common.Address { return nil } // TODO: is returning nil here correct?
func (tx *AbstractTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil {
return dst.Set(tx.GasFeeCap.ToBig())
}
tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee)
if tip.Cmp(tx.GasTipCap.ToBig()) > 0 {
tip.Set(tx.GasTipCap.ToBig())
}
return tip.Add(tip, baseFee)
}
func (tx *AbstractTx) rawSignatureValues() (v, r, s *big.Int) {
panic("abstract tx does not have signature")
}
func (tx *AbstractTx) setSignatureValues(chainID, v, r, s *big.Int) {
panic("abstract tx does not have signature")
}
func (tx *AbstractTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}
func (tx *AbstractTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}
func (tx *AbstractTx) sigHash(chainID *big.Int) common.Hash {
panic("abstract tx does not have signature")
}

View file

@ -19,6 +19,7 @@ package vm
import ( import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm/roles"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -42,6 +43,8 @@ type Contract struct {
IsDeployment bool IsDeployment bool
IsSystemCall bool IsSystemCall bool
Role roles.Role
Gas uint64 Gas uint64
value *uint256.Int value *uint256.Int
} }
@ -58,6 +61,7 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
jumpDests: jumpDests, jumpDests: jumpDests,
Gas: gas, Gas: gas,
value: value, value: value,
Role: roles.SenderExecution,
} }
} }

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm/roles"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -40,6 +41,7 @@ var activators = map[int]func(*JumpTable){
1344: enable1344, 1344: enable1344,
1153: enable1153, 1153: enable1153,
4762: enable4762, 4762: enable4762,
7701: enable7701,
7702: enable7702, 7702: enable7702,
7939: enable7939, 7939: enable7939,
8024: enable8024, 8024: enable8024,
@ -579,3 +581,234 @@ func enable7702(jt *JumpTable) {
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702 jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702 jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
} }
// opCurrentRole pushes the current execution role to the stack.
func opCurrentRole(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
role := uint256.NewInt(uint64(scope.Contract.Role))
scope.Stack.push(role)
return nil, nil
}
// opAcceptRole accepts the provided role from the stack.
func opAcceptRole(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// TODO: should this return data also? What will consume it?
role := scope.Stack.pop()
evm.accepted = roles.Role(role.Uint64())
return nil, errStopToken
}
type paramLoader func(*EVM, uint64) []byte
func loadType(evm *EVM, id uint64) []byte {
// TODO: plumb in type to TxContext
return nil
}
func loadNonce(evm *EVM, id uint64) []byte {
// TODO: plumb in nonce to TxContext
return nil
}
func loadSender(evm *EVM, id uint64) []byte {
return nil
}
func loadSenderValidationData(evm *EVM, id uint64) []byte {
return nil
}
func loadDeployer(evm *EVM, id uint64) []byte {
return nil
}
func loadDeployerData(evm *EVM, id uint64) []byte {
return nil
}
func loadPaymaster(evm *EVM, id uint64) []byte {
return nil
}
func loadPaymasterData(evm *EVM, id uint64) []byte {
return nil
}
func loadSenderExecutionData(evm *EVM, id uint64) []byte {
return nil
}
func loadMaxPriorityFeePerGas(evm *EVM, id uint64) []byte {
return nil
}
func loadMaxFeePerGas(evm *EVM, id uint64) []byte {
return nil
}
func loadPaymasterValidationGas(evm *EVM, id uint64) []byte {
return nil
}
func loadSenderExecutionGas(evm *EVM, id uint64) []byte {
return nil
}
func loadPaymasterPostOpGas(evm *EVM, id uint64) []byte {
return nil
}
func loadAccessListHash(evm *EVM, id uint64) []byte {
return nil
}
func loadAuthorizationListHash(evm *EVM, id uint64) []byte {
return nil
}
func loadExecutionStatus(evm *EVM, id uint64) []byte {
return nil
}
func loadExecutionGasUsed(evm *EVM, id uint64) []byte {
return nil
}
func loadTxHashForSignature(evm *EVM, id uint64) []byte {
return nil
}
// idToParam is a map of identifier to transaction parameter loader.
var idToParam = map[uint64]paramLoader{
0x00: loadType,
0x01: loadNonce,
0x02: loadSender,
0x03: loadSenderValidationData,
0x04: loadDeployer,
0x05: loadDeployerData,
0x06: loadPaymaster,
0x07: loadPaymasterData,
0x08: loadSenderExecutionData,
0x0B: loadMaxPriorityFeePerGas,
0x0C: loadMaxFeePerGas,
0x0D: loadSenderValidationData,
0x0E: loadPaymasterValidationGas,
0x0F: loadSenderExecutionGas,
0x10: loadPaymasterPostOpGas,
0x11: loadAccessListHash,
0x12: loadAuthorizationListHash,
0xf1: loadExecutionStatus,
0xf2: loadExecutionGasUsed,
0xff: loadTxHashForSignature,
}
// opTxParamLoad will load the 32-byte word of a transaction parameter at the
// specified index.
func opTxParamLoad(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
idx = stack.pop()
id = stack.pop()
get paramLoader
ok bool
)
id64, overflow := id.Uint64WithOverflow()
if get, ok = idToParam[id64]; !ok || overflow {
return nil, ErrTxParamIdInvalid
}
param := get(evm, id.Uint64())
value := new(uint256.Int)
idx64, overflow := idx.Uint64WithOverflow()
if !overflow {
value.SetBytes(getData(param, idx64, 32))
}
stack.push(value)
return nil, nil
}
// opTxParamSize loads the specified transaction parameter then computes its
// size and pushes the result to the stack.
func opTxParamSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
id = stack.peek()
get paramLoader
ok bool
)
id64, overflow := id.Uint64WithOverflow()
if get, ok = idToParam[id64]; !ok || overflow {
return nil, ErrTxParamIdInvalid
}
param := get(evm, id.Uint64())
stack.push(id.SetUint64(uint64(len(param))))
return nil, nil
}
// opTxParamCopy copies the specified transaction parameter into memory.
func opTxParamCopy(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
memOffset = scope.Stack.pop()
dataOffset = scope.Stack.pop()
length = scope.Stack.pop()
id = stack.pop()
get paramLoader
ok bool
)
id64, overflow := id.Uint64WithOverflow()
if get, ok = idToParam[id64]; !ok || overflow {
return nil, ErrTxParamIdInvalid
}
param := get(evm, id.Uint64())
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
if overflow {
dataOffset64 = math.MaxUint64
}
// These values are checked for overflow during gas cost calculation
memOffset64 := memOffset.Uint64()
length64 := length.Uint64()
scope.Memory.Set(memOffset64, length64, getData(param, dataOffset64, length64))
return nil, nil
}
// enable7701 enables the EIP-7701 changes to support native account
// abstraction.
func enable7701(jt *JumpTable) {
jt[CURRENTROLE] = &operation{
execute: opCurrentRole,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
jt[ACCEPTROLE] = &operation{
execute: opAcceptRole,
constantGas: GasQuickStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
}
jt[TXPARAMLOAD] = &operation{
execute: opTxParamLoad,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
}
jt[TXPARAMSIZE] = &operation{
execute: opTxParamSize,
constantGas: GasQuickStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[TXPARAMCOPY] = &operation{
execute: opTxParamCopy,
constantGas: GasFastestStep,
dynamicGas: gasCallDataCopy,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryCallDataCopy,
}
}

View file

@ -38,6 +38,7 @@ var (
ErrGasUintOverflow = errors.New("gas uint64 overflow") ErrGasUintOverflow = errors.New("gas uint64 overflow")
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef") ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
ErrNonceUintOverflow = errors.New("nonce uint64 overflow") ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
ErrTxParamIdInvalid = errors.New("tx param id invalid")
// errStopToken is an internal token indicating interpreter loop termination, // errStopToken is an internal token indicating interpreter loop termination,
// never returned to outside callers. // never returned to outside callers.

View file

@ -18,6 +18,7 @@ package vm
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
@ -25,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm/roles"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -77,6 +79,8 @@ type TxContext struct {
BlobHashes []common.Hash // Provides information for BLOBHASH BlobHashes []common.Hash // Provides information for BLOBHASH
BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
AccessEvents *state.AccessEvents // Capture all state accesses for this tx AccessEvents *state.AccessEvents // Capture all state accesses for this tx
// TODO: add EIP-7701 fields
} }
// EVM is the Ethereum Virtual Machine base object and provides // EVM is the Ethereum Virtual Machine base object and provides
@ -130,6 +134,8 @@ type EVM struct {
readOnly bool // Whether to throw on stateful modifications readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse returnData []byte // Last CALL's return data for subsequent reuse
accepted roles.Role // Last CALL's accepted role, if any
} }
// NewEVM constructs an EVM instance with the supplied block context, state // NewEVM constructs an EVM instance with the supplied block context, state
@ -317,6 +323,86 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
return ret, gas, err return ret, gas, err
} }
// TODO: should this function take the data input?
func (evm *EVM) CallWithRole(addr common.Address, gas uint64, role roles.Role) (leftOverGas uint64, err error) {
// Capture the tracer start/end events in debug mode
var ret []byte
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALL, params.AAEntryPoint, addr, nil, gas, common.Big0)
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return 0, ErrDepth
}
snapshot := evm.StateDB.Snapshot()
p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP4762 {
// Add proof of absence to witness
// At this point, the read costs have already been charged, either because this
// is a direct tx call, in which case it's covered by the intrinsic gas, or because
// of a CALL instruction, in which case BASIC_DATA has been added to the access
// list in write mode. If there is enough gas paying for the addition of the code
// hash leaf to the access list, then account creation will proceed unimpaired.
// Thus, only pay for the creation of the code hash leaf here.
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false)
if gas < wgas {
evm.StateDB.RevertToSnapshot(snapshot)
return gas, ErrOutOfGas
}
gas -= wgas
}
if !isPrecompile && evm.chainRules.IsEIP158 {
// Calling a non-existing account, don't do anything.
return 0, nil
}
evm.StateDB.CreateAccount(addr)
}
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, nil, gas, evm.Config.Tracer)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
code := evm.resolveCode(addr)
if len(code) == 0 {
ret, err = nil, nil // gas is unchanged
} else {
// The contract is a scoped environment for this execution context only.
contract := NewContract(params.AAEntryPoint, addr, common.U2560, gas, evm.jumpDests)
contract.IsSystemCall = false
contract.Role = role
contract.SetCallCode(evm.resolveCodeHash(addr), code)
ret, err = evm.Run(contract, nil, false)
gas = contract.Gas
}
}
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally,
// when we're in homestead this also counts for code storage gas errors.
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
gas = 0
}
// TODO: consider clearing up unused snapshots:
//} else {
// evm.StateDB.DiscardSnapshot(snapshot)
}
if evm.accepted != role {
return gas, fmt.Errorf("role %s not accepted, got %s", role, evm.accepted)
}
return gas, nil
}
// CallCode executes the contract associated with the addr with the given input // CallCode executes the contract associated with the addr with the given input
// as parameters. It also handles any necessary value transfer required and takes // as parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an // the necessary steps to create accounts and reverses the state in case of an
@ -373,7 +459,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// //
// DelegateCall differs from CallCode in the sense that it executes the given address' // DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller. // code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int, role roles.Role) (ret []byte, leftOverGas uint64, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
// DELEGATECALL inherits value from parent call // DELEGATECALL inherits value from parent call
@ -397,6 +483,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// Note: The value refers to the original value from the parent call. // Note: The value refers to the original value from the parent call.
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
contract.Role = role
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }

View file

@ -812,7 +812,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// Get arguments from the memory. // Get arguments from the memory.
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value) ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value, scope.Contract.Role)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {

View file

@ -220,6 +220,15 @@ const (
DATACOPY OpCode = 0xd3 DATACOPY OpCode = 0xd3
) )
// EIP-7701 operations.
const (
TXPARAMLOAD OpCode = 0xdb
TXPARAMSIZE OpCode = 0xdc
TXPARAMCOPY OpCode = 0xdd
CURRENTROLE OpCode = 0xde
ACCEPTROLE OpCode = 0xdf
)
// 0xe0 range - eof operations. // 0xe0 range - eof operations.
const ( const (
RJUMP OpCode = 0xe0 RJUMP OpCode = 0xe0
@ -422,6 +431,10 @@ var opCodeToString = [256]string{
DATASIZE: "DATASIZE", DATASIZE: "DATASIZE",
DATACOPY: "DATACOPY", DATACOPY: "DATACOPY",
// EIP-7701 ops.
CURRENTROLE: "CURRENTROLE",
ACCEPTROLE: "ACCEPTROLE",
// 0xe0 range. // 0xe0 range.
RJUMP: "RJUMP", RJUMP: "RJUMP",
RJUMPI: "RJUMPI", RJUMPI: "RJUMPI",
@ -608,6 +621,8 @@ var stringToOp = map[string]OpCode{
"DATALOADN": DATALOADN, "DATALOADN": DATALOADN,
"DATASIZE": DATASIZE, "DATASIZE": DATASIZE,
"DATACOPY": DATACOPY, "DATACOPY": DATACOPY,
"CURRENTROLE": CURRENTROLE,
"ACCEPTROLE": ACCEPTROLE,
"RJUMP": RJUMP, "RJUMP": RJUMP,
"RJUMPI": RJUMPI, "RJUMPI": RJUMPI,
"RJUMPV": RJUMPV, "RJUMPV": RJUMPV,

50
core/vm/roles/roles.go Normal file
View file

@ -0,0 +1,50 @@
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package roles
import "fmt"
type Role byte
const (
SenderDeployment = 0xA0
SenderValidation = 0xA1
PaymasterValidation = 0xA2
SenderExecution = 0xA3
PaymasterPostOp = 0xA4
)
func (r Role) String() string {
if s := roleToString[r]; s != "" {
return s
}
return fmt.Sprintf("role %#x not defined", int(r))
}
var roleToString = map[Role]string{
SenderDeployment: "SENDER_DEPLOYMENT",
SenderValidation: "SENDER_VALIDATION",
PaymasterValidation: "PAYMASTER_VALIDATION",
SenderExecution: "SENDER_EXECUTION",
PaymasterPostOp: "PAYMASTER_POST_OP",
}
// var stringToRole = map[string]Role{
// "SENDER_DEPLOYMENT": SenderDeployment,
// "SENDER_VALIDATION": SenderValidation,
// "PAYMASTER_VALIDATION": PaymasterValidation,
// "SENDER_EXECUTION": SenderExecution,
// "PAYMASTER_POST_OP": PaymasterPostOp,
// }

View file

@ -46,6 +46,7 @@ const (
BPO4 BPO4
BPO5 BPO5
Amsterdam Amsterdam
Bogota
) )
// String implements fmt.Stringer. // String implements fmt.Stringer.

View file

@ -37,6 +37,7 @@ const (
CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
TxGasAbstract uint64 = 15000 // Per transaction that has abstract validation.
TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
@ -204,6 +205,9 @@ var (
// SystemAddress is where the system-transaction is sent from as per EIP-4788 // SystemAddress is where the system-transaction is sent from as per EIP-4788
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
// AAEntryPoint is the default caller for top level AbstractTx frames.
AAEntryPoint = common.HexToAddress("0x7701")
// EIP-4788 - Beacon block root in the EVM // EIP-4788 - Beacon block root in the EVM
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")

View file

@ -81,7 +81,7 @@ func (tt *TransactionTest) Run() error {
return return
} }
// Intrinsic gas // Intrinsic gas
requiredGas, err = core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) requiredGas, err = core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai, tx.SenderAuthorization() == nil)
if err != nil { if err != nil {
return return
} }