diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2017-02-02 22:25:42 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-02 22:25:42 +0800 |
commit | 8b57c494908637a5c0e74f8f7a13b3218e026757 (patch) | |
tree | d5c7842eea6959c3b91dba00e8452a79d735a2a3 /internal/ethapi/api.go | |
parent | 296450451b090393b2dd11d057c5b72cb4d92356 (diff) | |
download | dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.tar dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.gz dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.bz2 dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.lz dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.xz dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.zst dexon-8b57c494908637a5c0e74f8f7a13b3218e026757.zip |
params: core, core/vm, miner: 64bit gas instructions (#3514)
Reworked the EVM gas instructions to use 64bit integers rather than
arbitrary size big ints. All gas operations, be it additions,
multiplications or divisions, are checked and guarded against 64 bit
integer overflows.
In additon, most of the protocol paramaters in the params package have
been converted to uint64 and are now constants rather than variables.
* common/math: added overflow check ops
* core: vmenv, env renamed to evm
* eth, internal/ethapi, les: unmetered eth_call and cancel methods
* core/vm: implemented big.Int pool for evm instructions
* core/vm: unexported intPool methods & verification methods
* core/vm: added memoryGasCost overflow check and test
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 { |