diff options
Diffstat (limited to 'internal/ethapi/api.go')
-rw-r--r-- | internal/ethapi/api.go | 74 |
1 files changed, 47 insertions, 27 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 925f547b6..4c8e784c5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -47,6 +47,8 @@ import ( const defaultGas = 90000 +var emptyHex = "0x" + // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicEthereumAPI struct { @@ -503,58 +505,76 @@ type CallArgs struct { Data hexutil.Bytes `json:"data"` } -func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { +func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) { defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { - return "0x", common.Big0, err + return nil, common.Big0, err } - // Set the account address to interact with - var addr common.Address - if args.From == (common.Address{}) { + // Set sender address or use a default if none specified + addr := args.From + if addr == (common.Address{}) { accounts := s.b.AccountManager().Accounts() - if len(accounts) == 0 { - addr = common.Address{} - } else { + if len(accounts) > 0 { addr = accounts[0].Address } - } else { - addr = args.From } - // Assemble the CALL invocation + // Set default gas & gas price if none were set gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt() - if gas.Cmp(common.Big0) == 0 { + if gas.BitLen() == 0 { gas = big.NewInt(50000000) } - if gasPrice.Cmp(common.Big0) == 0 { + if gasPrice.BitLen() == 0 { gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) } + + // Create new call message msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false) - // Execute the call and return - vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) - if err != nil { - return "0x", common.Big0, err + // Setup context so it may be cancelled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if vmCfg.DisableGasMetering { + ctx, cancel = context.WithTimeout(ctx, time.Second*5) + } else { + ctx, cancel = context.WithCancel(ctx) } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer func() { cancel() }() + + // Get a new instance of the EVM. + evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg) + if err != nil { + return nil, common.Big0, err + } + // Wait for the context to be done and cancel the evm. Even if the + // EVM has finished, cancelling may be done (repeatedly) + go func() { + select { + case <-ctx.Done(): + evm.Cancel() + } + }() + + // Setup the gas pool (also for unmetered requests) + // and apply the message. gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) + res, gas, err := core.ApplyMessage(evm, msg, gp) if err := vmError(); err != nil { - return "0x", common.Big0, err - } - if len(res) == 0 { // backwards compatibility - return "0x", gas, err + return nil, common.Big0, err } - return common.ToHex(res), gas, err + return res, gas, err } // 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) (string, error) { - result, _, err := s.doCall(ctx, args, blockNr) - return result, err +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { + result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true}) + return (hexutil.Bytes)(result), err } // EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. @@ -576,7 +596,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (* mid := (hi + lo) / 2 (*big.Int)(&args.Gas).SetUint64(mid) - _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber) + _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) // If the transaction became invalid or used all the gas (failed), raise the gas limit if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 { |