diff options
Diffstat (limited to 'internal/ethapi/api.go')
-rw-r--r-- | internal/ethapi/api.go | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b67017a90..05204e547 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -743,7 +743,21 @@ type CallArgs struct { Data *hexutil.Bytes `json:"data"` } -func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { +// account indicates the overriding fields of account during the execution of +// a message call. +// Note, state and stateDiff can't be specified at the same time. If state is +// set, message execution will only use the data in the given state. Otherwise +// if statDiff is set, all diff will be applied first and then execute the call +// message. +type account struct { + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance **hexutil.Big `json:"balance"` + State *map[common.Hash]common.Hash `json:"state"` + StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` +} + +func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) state, header, err := b.StateAndHeaderByNumber(ctx, blockNr) @@ -761,6 +775,34 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb } else { addr = *args.From } + // Override the fields of specified contracts before execution. + for addr, account := range overrides { + // Override account nonce. + if account.Nonce != nil { + state.SetNonce(addr, uint64(*account.Nonce)) + } + // Override account(contract) code. + if account.Code != nil { + state.SetCode(addr, *account.Code) + } + // Override account balance. + if account.Balance != nil { + state.SetBalance(addr, (*big.Int)(*account.Balance)) + } + if account.State != nil && account.StateDiff != nil { + return nil, 0, false, fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) + } + // Replace entire state if caller requires. + if account.State != nil { + state.SetStorage(addr, *account.State) + } + // Apply state diff into specified accounts. + if account.StateDiff != nil { + for key, value := range *account.StateDiff { + state.SetState(addr, key, value) + } + } + } // Set default gas & gas price if none were set gas := uint64(math.MaxUint64 / 2) if args.Gas != nil { @@ -827,9 +869,17 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb } // Call executes the given transaction on the state for the given block number. -// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. -func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { - result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second, s.b.RPCGasCap()) +// +// Additionally, the caller can specify a batch of contract for fields overriding. +// +// Note, this function doesn't make and changes in the state/blockchain and is +// useful to execute and retrieve values. +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, overrides *map[common.Address]account) (hexutil.Bytes, error) { + var accounts map[common.Address]account + if overrides != nil { + accounts = *overrides + } + result, _, _, err := DoCall(ctx, s.b, args, blockNr, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap()) return (hexutil.Bytes)(result), err } @@ -860,7 +910,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl executable := func(gas uint64) bool { args.Gas = (*hexutil.Uint64)(&gas) - _, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, vm.Config{}, 0, gasCap) + _, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, nil, vm.Config{}, 0, gasCap) if err != nil || failed { return false } |