Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/evm/internal/t8ntool/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions core/bintrie_witness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)

Expand Down Expand Up @@ -82,13 +84,55 @@ func NewEVMTxContext(msg *Message) vm.TxContext {
Origin: msg.From,
GasPrice: new(big.Int).Set(msg.GasPrice),
BlobHashes: msg.BlobHashes,

// EIP-7701 fields
TxType: msg.TxType,
Nonce: msg.Nonce,
Sender: msg.Sender,
Deployer: msg.Deployer,
Paymaster: msg.Paymaster,
SenderExecutionData: msg.Data,
SenderExecutionGas: msg.GasLimit,
}
if msg.BlobGasFeeCap != nil {
ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap)
}
if msg.GasTipCap != nil {
ctx.MaxPriorityFeePerGas = new(big.Int).Set(msg.GasTipCap)
}
if msg.GasFeeCap != nil {
ctx.MaxFeePerGas = new(big.Int).Set(msg.GasFeeCap)
}

// Compute AccessListHash
if len(msg.AccessList) > 0 {
ctx.AccessListHash = rlpHash(msg.AccessList)
}

// Compute AuthorizationListHash
if len(msg.SetCodeAuthorizations) > 0 {
ctx.AuthorizationListHash = rlpHash(msg.SetCodeAuthorizations)
}

// TxHashForSignature is nil for AA transactions
// For non-AA transactions, we leave it nil as we don't have access to the
// original transaction here. If needed, this should be set by the caller.
if !msg.Abstract {
// TODO: compute TxHashForSignature for non-AA transactions if needed
}

return ctx
}

// rlpHash computes the keccak256 hash of the RLP encoding of x.
func rlpHash(x interface{}) common.Hash {
encoded, err := rlp.EncodeToBytes(x)
if err != nil {
return common.Hash{}
}
return crypto.Keccak256Hash(encoded)
}

// GetHashFn returns a GetHashFunc which retrieves header hashes by number
func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash {
// Cache will initially contain [refHash.parent],
Expand Down
123 changes: 104 additions & 19 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -143,6 +146,7 @@ func toWordSize(size uint64) uint64 {
// A Message contains the data derived from a single transaction that is relevant to state
// processing.
type Message struct {
TxType uint8
To *common.Address
From common.Address
Nonce uint64
Expand All @@ -157,6 +161,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
Comment on lines +170 to +171
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really dislike these fields, will find a cleaner way to expose this data. Mostly need to decide if we will repurpose Data and GasLimit for this and make separate SenderExecutionData and SenderExecutionGas to fulfill their current role.


// When SkipNonceChecks is true, the message nonce is not checked against the
// account nonce in state.
//
Expand All @@ -175,6 +188,7 @@ type Message struct {
// TransactionToMessage converts a transaction into a Message.
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
msg := &Message{
TxType: tx.Type(),
Nonce: tx.Nonce(),
GasLimit: tx.Gas(),
GasPrice: new(big.Int).Set(tx.GasPrice()),
Expand All @@ -189,6 +203,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 {
Expand All @@ -199,6 +221,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
}

Expand Down Expand Up @@ -264,11 +307,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)
Expand All @@ -289,21 +332,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
}

Expand All @@ -327,8 +370,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)
Expand Down Expand Up @@ -443,7 +486,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
}
Expand All @@ -452,11 +495,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)
}
}
Expand All @@ -471,6 +514,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
Expand All @@ -492,12 +543,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
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)
Expand All @@ -520,9 +603,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()
Expand Down
2 changes: 1 addition & 1 deletion core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
25 changes: 25 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Loading
Loading