add optional validation of txes

This commit is contained in:
Sina Mahmoodi 2023-08-01 17:26:00 +02:00
parent fd786c081f
commit 6845b5eff6
4 changed files with 51 additions and 8 deletions

View file

@ -908,7 +908,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
config.BlockOverrides.Apply(&vmctx) config.BlockOverrides.Apply(&vmctx)
} }
// Execute the trace // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -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 // Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up. // this makes sure resources are cleaned up.
defer cancel() 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. // 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 { if err != nil {
return nil, err return nil, err
} }
@ -1217,6 +1217,7 @@ func (r *callResult) MarshalJSON() ([]byte, error) {
type multicallOpts struct { type multicallOpts struct {
BlockStateCalls []CallBatch BlockStateCalls []CallBatch
TraceTransfers bool TraceTransfers bool
Validation bool
} }
// MulticallV1 executes series of transactions on top of a base state. // 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 { if opts.TraceTransfers {
vmConfig.Tracer = newTracer() 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 { if err != nil {
results[bi].Calls[i] = callResult{Error: err.Error(), Status: hexutil.Uint64(types.ReceiptStatusFailed)} results[bi].Calls[i] = callResult{Error: err.Error(), Status: hexutil.Uint64(types.ReceiptStatusFailed)}
continue continue
@ -1722,7 +1723,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, err := args.ToMessage(b.RPCGasCap(), header.BaseFee) msg, err := args.ToMessage(b.RPCGasCap(), header.BaseFee, true)
if err != nil { if err != nil {
return nil, 0, nil, err return nil, 0, nil, err
} }

View file

@ -677,6 +677,7 @@ func TestMulticallV1(t *testing.T) {
randomAccounts = newAccounts(4) randomAccounts = newAccounts(4)
latest = rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) latest = rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
includeTransfers = true includeTransfers = true
validation = true
) )
type callRes struct { type callRes struct {
ReturnValue string `json:"return"` ReturnValue string `json:"return"`
@ -701,6 +702,7 @@ func TestMulticallV1(t *testing.T) {
blocks []CallBatch blocks []CallBatch
tag rpc.BlockNumberOrHash tag rpc.BlockNumberOrHash
includeTransfers *bool includeTransfers *bool
validation *bool
expectErr error expectErr error
want []blockRes 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 { for i, tc := range testSuite {
@ -1218,6 +1247,9 @@ func TestMulticallV1(t *testing.T) {
if tc.includeTransfers != nil && *tc.includeTransfers { if tc.includeTransfers != nil && *tc.includeTransfers {
opts.TraceTransfers = true opts.TraceTransfers = true
} }
if tc.validation != nil && *tc.validation {
opts.Validation = true
}
result, err := api.MulticallV1(context.Background(), opts, tc.tag) result, err := api.MulticallV1(context.Background(), opts, tc.tag)
if tc.expectErr != nil { if tc.expectErr != nil {
if err == nil { if err == nil {
@ -1278,6 +1310,11 @@ func hex2Bytes(str string) *hexutil.Bytes {
return &rpcBytes return &rpcBytes
} }
func newUint64(v uint64) *hexutil.Uint64 {
rpcUint64 := hexutil.Uint64(v)
return &rpcUint64
}
// testHasher is the helper tool for transaction/receipt list hashing. // testHasher is the helper tool for transaction/receipt list hashing.
// The original hasher is trie, in order to get rid of import cycle, // The original hasher is trie, in order to get rid of import cycle,
// use the testing hasher instead. // use the testing hasher instead.

View file

@ -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 // 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 // core evm. This method is used in calls and traces that do not require a real
// live transaction. // 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 // Reject invalid combinations of pre- and post-1559 fee styles
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") 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 { if args.Value != nil {
value = args.Value.ToInt() value = args.Value.ToInt()
} }
var nonce uint64
if args.Nonce != nil {
nonce = uint64(*args.Nonce)
}
data := args.data() data := args.data()
var accessList types.AccessList var accessList types.AccessList
if args.AccessList != nil { if args.AccessList != nil {
@ -268,13 +272,14 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*
From: addr, From: addr,
To: args.To, To: args.To,
Value: value, Value: value,
Nonce: nonce,
GasLimit: gas, GasLimit: gas,
GasPrice: gasPrice, GasPrice: gasPrice,
GasFeeCap: gasFeeCap, GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap, GasTipCap: gasTipCap,
Data: data, Data: data,
AccessList: accessList, AccessList: accessList,
SkipAccountChecks: true, SkipAccountChecks: skipChecks,
} }
return msg, nil return msg, nil
} }