From 6845b5eff6b0a7776f7cc016682ff57bf567fc28 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 1 Aug 2023 17:26:00 +0200 Subject: [PATCH] add optional validation of txes --- eth/tracers/api.go | 2 +- internal/ethapi/api.go | 11 +++++---- internal/ethapi/api_test.go | 37 +++++++++++++++++++++++++++++ internal/ethapi/transaction_args.go | 9 +++++-- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 5e90180df8..1be3a36b6a 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -908,7 +908,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc config.BlockOverrides.Apply(&vmctx) } // Execute the trace - msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee()) + msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee(), true) if err != nil { return nil, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index fda4f812d2..8676ed6f4c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1093,12 +1093,12 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // Make sure the context is cancelled when the call has completed // this makes sure resources are cleaned up. defer cancel() - return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx, &vm.Config{NoBaseFee: true}, nil) + return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx, &vm.Config{NoBaseFee: true}, nil, false) } -func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts) (*core.ExecutionResult, error) { +func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, validate bool) (*core.ExecutionResult, error) { // Get a new instance of the EVM. - msg, err := args.ToMessage(gp.Gas(), header.BaseFee) + msg, err := args.ToMessage(gp.Gas(), header.BaseFee, !validate) if err != nil { return nil, err } @@ -1217,6 +1217,7 @@ func (r *callResult) MarshalJSON() ([]byte, error) { type multicallOpts struct { BlockStateCalls []CallBatch TraceTransfers bool + Validation bool } // MulticallV1 executes series of transactions on top of a base state. @@ -1289,7 +1290,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo if opts.TraceTransfers { vmConfig.Tracer = newTracer() } - result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles) + result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, opts.Validation) if err != nil { results[bi].Calls[i] = callResult{Error: err.Error(), Status: hexutil.Uint64(types.ReceiptStatusFailed)} continue @@ -1722,7 +1723,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH statedb := db.Copy() // Set the accesslist to the last al args.AccessList = &accessList - msg, err := args.ToMessage(b.RPCGasCap(), header.BaseFee) + msg, err := args.ToMessage(b.RPCGasCap(), header.BaseFee, true) if err != nil { return nil, 0, nil, err } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 78238d96bd..668a1e3f8e 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -677,6 +677,7 @@ func TestMulticallV1(t *testing.T) { randomAccounts = newAccounts(4) latest = rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) includeTransfers = true + validation = true ) type callRes struct { ReturnValue string `json:"return"` @@ -701,6 +702,7 @@ func TestMulticallV1(t *testing.T) { blocks []CallBatch tag rpc.BlockNumberOrHash includeTransfers *bool + validation *bool expectErr error want []blockRes }{ @@ -1210,6 +1212,33 @@ func TestMulticallV1(t *testing.T) { }}, }}, }, + // Enable validation checks. + { + name: "validation-checks", + tag: latest, + blocks: []CallBatch{{ + Calls: []TransactionArgs{{ + From: &accounts[2].addr, + To: &cac, + Nonce: newUint64(2), + }}, + }}, + validation: &validation, + want: []blockRes{{ + Number: "0xa", + Hash: n10hash, + GasLimit: "0x47e7c4", + GasUsed: "0x0", + FeeRecipient: "0x0000000000000000000000000000000000000000", + Calls: []callRes{{ + ReturnValue: "0x", + GasUsed: "0x0", + Logs: []types.Log{}, + Status: "0x0", + Error: fmt.Sprintf("err: nonce too high: address %s, tx: 2 state: 0 (supplied gas 10000000)", accounts[2].addr), + }}, + }}, + }, } for i, tc := range testSuite { @@ -1218,6 +1247,9 @@ func TestMulticallV1(t *testing.T) { if tc.includeTransfers != nil && *tc.includeTransfers { opts.TraceTransfers = true } + if tc.validation != nil && *tc.validation { + opts.Validation = true + } result, err := api.MulticallV1(context.Background(), opts, tc.tag) if tc.expectErr != nil { if err == nil { @@ -1278,6 +1310,11 @@ func hex2Bytes(str string) *hexutil.Bytes { return &rpcBytes } +func newUint64(v uint64) *hexutil.Uint64 { + rpcUint64 := hexutil.Uint64(v) + return &rpcUint64 +} + // testHasher is the helper tool for transaction/receipt list hashing. // The original hasher is trie, in order to get rid of import cycle, // use the testing hasher instead. diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index c74f540b76..0cef8956c4 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -200,7 +200,7 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. -func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*core.Message, error) { +func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, skipChecks bool) (*core.Message, error) { // Reject invalid combinations of pre- and post-1559 fee styles if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") @@ -259,6 +259,10 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* if args.Value != nil { value = args.Value.ToInt() } + var nonce uint64 + if args.Nonce != nil { + nonce = uint64(*args.Nonce) + } data := args.data() var accessList types.AccessList if args.AccessList != nil { @@ -268,13 +272,14 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* From: addr, To: args.To, Value: value, + Nonce: nonce, GasLimit: gas, GasPrice: gasPrice, GasFeeCap: gasFeeCap, GasTipCap: gasTipCap, Data: data, AccessList: accessList, - SkipAccountChecks: true, + SkipAccountChecks: skipChecks, } return msg, nil }