mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 20:56:42 +00:00
all: initial sketch of 7701
This commit is contained in:
parent
57f84866bc
commit
2859da852d
18 changed files with 703 additions and 27 deletions
|
|
@ -134,7 +134,7 @@ func Transaction(ctx *cli.Context) error {
|
|||
}
|
||||
// Check intrinsic gas
|
||||
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 {
|
||||
r.Error = err
|
||||
results = append(results, r)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
data := make([]byte, nbytes)
|
||||
return func(i int, gen *BlockGen) {
|
||||
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()
|
||||
gasPrice := big.NewInt(0)
|
||||
if gen.header.BaseFee != nil {
|
||||
|
|
|
|||
|
|
@ -63,12 +63,12 @@ var (
|
|||
func TestProcessVerkle(t *testing.T) {
|
||||
var (
|
||||
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
|
||||
// will not contain that copied data.
|
||||
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
|
||||
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)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ 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/core/vm/roles"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"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.
|
||||
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
|
||||
var gas uint64
|
||||
if isContractCreation && isHomestead {
|
||||
gas = params.TxGasContractCreation
|
||||
} else if isAbstract {
|
||||
gas = params.TxGasAbstract
|
||||
} else {
|
||||
gas = params.TxGas
|
||||
}
|
||||
|
|
@ -157,6 +160,15 @@ type Message struct {
|
|||
BlobHashes []common.Hash
|
||||
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
|
||||
// account nonce in state.
|
||||
//
|
||||
|
|
@ -189,6 +201,14 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
|||
SkipTransactionChecks: false,
|
||||
BlobHashes: tx.BlobHashes(),
|
||||
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 != nil {
|
||||
|
|
@ -199,6 +219,27 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
|||
}
|
||||
var err error
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -264,11 +305,11 @@ func (st *stateTransition) to() common.Address {
|
|||
}
|
||||
|
||||
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)
|
||||
balanceCheck := new(big.Int).Set(mgval)
|
||||
if st.msg.GasFeeCap != nil {
|
||||
balanceCheck.SetUint64(st.msg.GasLimit)
|
||||
balanceCheck.SetUint64(st.msg.TotalGasLimit)
|
||||
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
|
||||
}
|
||||
balanceCheck.Add(balanceCheck, st.msg.Value)
|
||||
|
|
@ -289,21 +330,21 @@ func (st *stateTransition) buyGas() error {
|
|||
if overflow {
|
||||
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 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||
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.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
|
||||
}
|
||||
|
||||
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)
|
||||
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
|
||||
st.state.SubBalance(st.msg.Payer, mgvalU256, tracing.BalanceDecreaseGasBuy)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -327,8 +368,8 @@ func (st *stateTransition) preCheck() error {
|
|||
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||
if !msg.SkipTransactionChecks {
|
||||
// Verify tx gas limit does not exceed EIP-7825 cap.
|
||||
if isOsaka && msg.GasLimit > params.MaxTxGas {
|
||||
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
||||
if isOsaka && msg.TotalGasLimit > params.MaxTxGas {
|
||||
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.TotalGasLimit)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
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
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -452,11 +493,11 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
}
|
||||
// Gas limit suffices for the floor data cost (EIP-7623)
|
||||
if rules.IsPrague {
|
||||
floorDataGas, err = FloorDataGas(msg.Data)
|
||||
floorDataGas, err = FloorDataGas(msg.AllData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.GasLimit < floorDataGas {
|
||||
if msg.TotalGasLimit < 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 {
|
||||
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
|
||||
|
|
@ -492,12 +541,44 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// - reset transient storage(eip 1153)
|
||||
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 (
|
||||
ret []byte
|
||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
remaining uint64
|
||||
)
|
||||
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 {
|
||||
// Increment the nonce for the next transaction.
|
||||
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.
|
||||
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
|
||||
// gas allowance required to complete execution.
|
||||
peakGasUsed := st.gasUsed()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ const (
|
|||
DynamicFeeTxType = 0x02
|
||||
BlobTxType = 0x03
|
||||
SetCodeTxType = 0x04
|
||||
AbstractTxType = 0x06 // skip 0x05 since it magic for set code tx
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
|
|
@ -535,6 +536,30 @@ func (tx *Transaction) SetCodeAuthorities() []common.Address {
|
|||
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
|
||||
// arbitrary times and by persistent transaction pools when loading old txs from
|
||||
// disk.
|
||||
|
|
|
|||
|
|
@ -231,6 +231,9 @@ func newModernSigner(chainID *big.Int, fork forks.Fork) Signer {
|
|||
if fork >= forks.Prague {
|
||||
s.txtypes.set(SetCodeTxType)
|
||||
}
|
||||
if fork >= forks.Bogota {
|
||||
s.txtypes.set(AbstractTxType)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
@ -262,6 +265,9 @@ func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) {
|
|||
if tx.ChainId().Cmp(s.chainID) != 0 {
|
||||
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
|
||||
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// - EIP-7702 set code transactions
|
||||
// - EIP-4844 blob transactions
|
||||
|
|
|
|||
155
core/types/tx_abstract.go
Normal file
155
core/types/tx_abstract.go
Normal 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")
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ package vm
|
|||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/vm/roles"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -42,6 +43,8 @@ type Contract struct {
|
|||
IsDeployment bool
|
||||
IsSystemCall bool
|
||||
|
||||
Role roles.Role
|
||||
|
||||
Gas uint64
|
||||
value *uint256.Int
|
||||
}
|
||||
|
|
@ -58,6 +61,7 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
|
|||
jumpDests: jumpDests,
|
||||
Gas: gas,
|
||||
value: value,
|
||||
Role: roles.SenderExecution,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
233
core/vm/eips.go
233
core/vm/eips.go
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/vm/roles"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -40,6 +41,7 @@ var activators = map[int]func(*JumpTable){
|
|||
1344: enable1344,
|
||||
1153: enable1153,
|
||||
4762: enable4762,
|
||||
7701: enable7701,
|
||||
7702: enable7702,
|
||||
7939: enable7939,
|
||||
8024: enable8024,
|
||||
|
|
@ -579,3 +581,234 @@ func enable7702(jt *JumpTable) {
|
|||
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ var (
|
|||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
||||
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
||||
ErrTxParamIdInvalid = errors.New("tx param id invalid")
|
||||
|
||||
// errStopToken is an internal token indicating interpreter loop termination,
|
||||
// never returned to outside callers.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package vm
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"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/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -77,6 +79,8 @@ type TxContext struct {
|
|||
BlobHashes []common.Hash // Provides information for BLOBHASH
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
|
|
@ -317,6 +323,86 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
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
|
||||
// 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
|
||||
|
|
@ -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'
|
||||
// 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
|
||||
if evm.Config.Tracer != nil {
|
||||
// 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.
|
||||
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
contract.Role = role
|
||||
ret, err = evm.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
|
|
|
|||
|
|
@ -812,7 +812,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// Get arguments from the memory.
|
||||
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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -220,6 +220,15 @@ const (
|
|||
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.
|
||||
const (
|
||||
RJUMP OpCode = 0xe0
|
||||
|
|
@ -422,6 +431,10 @@ var opCodeToString = [256]string{
|
|||
DATASIZE: "DATASIZE",
|
||||
DATACOPY: "DATACOPY",
|
||||
|
||||
// EIP-7701 ops.
|
||||
CURRENTROLE: "CURRENTROLE",
|
||||
ACCEPTROLE: "ACCEPTROLE",
|
||||
|
||||
// 0xe0 range.
|
||||
RJUMP: "RJUMP",
|
||||
RJUMPI: "RJUMPI",
|
||||
|
|
@ -608,6 +621,8 @@ var stringToOp = map[string]OpCode{
|
|||
"DATALOADN": DATALOADN,
|
||||
"DATASIZE": DATASIZE,
|
||||
"DATACOPY": DATACOPY,
|
||||
"CURRENTROLE": CURRENTROLE,
|
||||
"ACCEPTROLE": ACCEPTROLE,
|
||||
"RJUMP": RJUMP,
|
||||
"RJUMPI": RJUMPI,
|
||||
"RJUMPV": RJUMPV,
|
||||
|
|
|
|||
50
core/vm/roles/roles.go
Normal file
50
core/vm/roles/roles.go
Normal 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,
|
||||
// }
|
||||
|
|
@ -46,6 +46,7 @@ const (
|
|||
BPO4
|
||||
BPO5
|
||||
Amsterdam
|
||||
Bogota
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const (
|
|||
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.
|
||||
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.
|
||||
QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
|
||||
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 = 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
|
||||
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
|
||||
BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func (tt *TransactionTest) Run() error {
|
|||
return
|
||||
}
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue