skip EoA check in validation mode

This commit is contained in:
Sina Mahmoodi 2024-06-12 15:24:10 +02:00
parent 5db01067d3
commit 4c325ba5b3
6 changed files with 77 additions and 35 deletions

View file

@ -142,27 +142,30 @@ type Message struct {
BlobGasFeeCap *big.Int BlobGasFeeCap *big.Int
BlobHashes []common.Hash BlobHashes []common.Hash
// When SkipAccountChecks 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. It also disables checking that the sender is an EOA. // account nonce in state.
// This field will be set to true for operations like RPC eth_call. // This field will be set to true for operations like RPC eth_call.
SkipAccountChecks bool SkipNonceChecks bool
// When SkipFromEoACheck is true, the message sender is not checked to be an EOA.
SkipFromEoACheck bool
} }
// TransactionToMessage converts a transaction into a Message. // TransactionToMessage converts a transaction into a Message.
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
msg := &Message{ msg := &Message{
Nonce: tx.Nonce(), Nonce: tx.Nonce(),
GasLimit: tx.Gas(), GasLimit: tx.Gas(),
GasPrice: new(big.Int).Set(tx.GasPrice()), GasPrice: new(big.Int).Set(tx.GasPrice()),
GasFeeCap: new(big.Int).Set(tx.GasFeeCap()), GasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
GasTipCap: new(big.Int).Set(tx.GasTipCap()), GasTipCap: new(big.Int).Set(tx.GasTipCap()),
To: tx.To(), To: tx.To(),
Value: tx.Value(), Value: tx.Value(),
Data: tx.Data(), Data: tx.Data(),
AccessList: tx.AccessList(), AccessList: tx.AccessList(),
SkipAccountChecks: false, SkipNonceChecks: false,
BlobHashes: tx.BlobHashes(), SkipFromEoACheck: false,
BlobGasFeeCap: tx.BlobGasFeeCap(), BlobHashes: tx.BlobHashes(),
BlobGasFeeCap: tx.BlobGasFeeCap(),
} }
// If baseFee provided, set gasPrice to effectiveGasPrice. // If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil { if baseFee != nil {
@ -280,7 +283,7 @@ func (st *StateTransition) buyGas() error {
func (st *StateTransition) preCheck() error { func (st *StateTransition) preCheck() error {
// Only check transactions that are not fake // Only check transactions that are not fake
msg := st.msg msg := st.msg
if !msg.SkipAccountChecks { if !msg.SkipNonceChecks {
// Make sure this transaction's nonce is correct. // Make sure this transaction's nonce is correct.
stNonce := st.state.GetNonce(msg.From) stNonce := st.state.GetNonce(msg.From)
if msgNonce := msg.Nonce; stNonce < msgNonce { if msgNonce := msg.Nonce; stNonce < msgNonce {
@ -293,6 +296,8 @@ func (st *StateTransition) preCheck() error {
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax, return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
msg.From.Hex(), stNonce) msg.From.Hex(), stNonce)
} }
}
if !msg.SkipFromEoACheck {
// Make sure the sender is an EOA // Make sure the sender is an EOA
codeHash := st.state.GetCodeHash(msg.From) codeHash := st.state.GetCodeHash(msg.From)
if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash { if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash {

View file

@ -951,7 +951,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
return nil, err return nil, err
} }
var ( var (
msg = args.ToMessage(vmctx.BaseFee, true) msg = args.ToMessage(vmctx.BaseFee, true, true)
tx = args.ToTransaction(args.GasPrice == nil) tx = args.ToTransaction(args.GasPrice == nil)
traceConfig *TraceConfig traceConfig *TraceConfig
) )

View file

@ -1172,7 +1172,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil { if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil {
return nil, err return nil, err
} }
msg := args.ToMessage(header.BaseFee, skipChecks) msg := args.ToMessage(header.BaseFee, skipChecks, skipChecks)
// Lower the basefee to 0 to avoid breaking EVM // Lower the basefee to 0 to avoid breaking EVM
// invariants (basefee < feecap). // invariants (basefee < feecap).
if msg.GasPrice.Sign() == 0 { if msg.GasPrice.Sign() == 0 {
@ -1299,7 +1299,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil { if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil {
return 0, err return 0, err
} }
call := args.ToMessage(header.BaseFee, true) call := args.ToMessage(header.BaseFee, true, true)
// Run the gas estimation and wrap any revertals into a custom return // Run the gas estimation and wrap any revertals into a custom return
estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap) estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
@ -1633,7 +1633,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
statedb := db.Copy() statedb := db.Copy()
// Set the accesslist to the last al // Set the accesslist to the last al
args.AccessList = &accessList args.AccessList = &accessList
msg := args.ToMessage(header.BaseFee, true) msg := args.ToMessage(header.BaseFee, true, true)
// Apply the transaction with the access list tracer // Apply the transaction with the access list tracer
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)

View file

@ -1720,6 +1720,41 @@ func TestSimulateV1(t *testing.T) {
want: nil, want: nil,
expectErr: &invalidTxError{Message: fmt.Sprintf("err: nonce too high: address %s, tx: 2 state: 0 (supplied gas 4712388)", accounts[2].addr), Code: errCodeNonceTooHigh}, expectErr: &invalidTxError{Message: fmt.Sprintf("err: nonce too high: address %s, tx: 2 state: 0 (supplied gas 4712388)", accounts[2].addr), Code: errCodeNonceTooHigh},
}, },
// Contract sends tx in validation mode.
{
name: "validation-checks-from-contract",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{
Balance: newRPCBalance(big.NewInt(2098640803896784)),
Code: hex2Bytes("00"),
Nonce: newUint64(1),
},
},
Calls: []TransactionArgs{{
From: &randomAccounts[2].addr,
To: &cac,
Nonce: newUint64(1),
MaxFeePerGas: newInt(233138868),
MaxPriorityFeePerGas: newInt(1),
}},
}},
validation: &validation,
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xd166",
Miner: coinbase,
BaseFeePerGas: "0xde56ab3",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0xd166",
Logs: []log{},
Status: "0x1",
}},
}},
},
// Successful validation // Successful validation
{ {
name: "validation-checks-success", name: "validation-checks-success",

View file

@ -194,7 +194,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
} }
tx := call.ToTransaction(call.GasPrice == nil) tx := call.ToTransaction(call.GasPrice == nil)
txes[i] = tx txes[i] = tx
msg := call.ToMessage(header.BaseFee, !sim.validate) // EoA check is always skipped, even in validation mode.
msg := call.ToMessage(header.BaseFee, !sim.validate, true)
tracer.reset(tx.Hash(), uint(i)) tracer.reset(tx.Hash(), uint(i))
evm.Reset(core.NewEVMTxContext(msg), sim.state) evm.Reset(core.NewEVMTxContext(msg), sim.state)
result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, gp) result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, gp)

View file

@ -421,7 +421,7 @@ func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int,
// core evm. This method is used in calls and traces that do not require a real // core evm. This method is used in calls and traces that do not require a real
// live transaction. // live transaction.
// Assumes that fields are not nil, i.e. setDefaults or CallDefaults has been called. // Assumes that fields are not nil, i.e. setDefaults or CallDefaults has been called.
func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipChecks bool) *core.Message { func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoACheck bool) *core.Message {
var ( var (
gasPrice *big.Int gasPrice *big.Int
gasFeeCap *big.Int gasFeeCap *big.Int
@ -452,19 +452,20 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipChecks bool) *core.
accessList = *args.AccessList accessList = *args.AccessList
} }
return &core.Message{ return &core.Message{
From: args.from(), From: args.from(),
To: args.To, To: args.To,
Value: (*big.Int)(args.Value), Value: (*big.Int)(args.Value),
Nonce: uint64(*args.Nonce), Nonce: uint64(*args.Nonce),
GasLimit: uint64(*args.Gas), GasLimit: uint64(*args.Gas),
GasPrice: gasPrice, GasPrice: gasPrice,
GasFeeCap: gasFeeCap, GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap, GasTipCap: gasTipCap,
Data: args.data(), Data: args.data(),
AccessList: accessList, AccessList: accessList,
BlobGasFeeCap: (*big.Int)(args.BlobFeeCap), BlobGasFeeCap: (*big.Int)(args.BlobFeeCap),
BlobHashes: args.BlobHashes, BlobHashes: args.BlobHashes,
SkipAccountChecks: skipChecks, SkipNonceChecks: skipNonceCheck,
SkipFromEoACheck: skipEoACheck,
} }
} }