aboutsummaryrefslogtreecommitdiffstats
path: root/internal
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2017-02-02 22:25:42 +0800
committerGitHub <noreply@github.com>2017-02-02 22:25:42 +0800
commit8b57c494908637a5c0e74f8f7a13b3218e026757 (patch)
treed5c7842eea6959c3b91dba00e8452a79d735a2a3 /internal
parent296450451b090393b2dd11d057c5b72cb4d92356 (diff)
downloadgo-tangerine-8b57c494908637a5c0e74f8f7a13b3218e026757.tar
go-tangerine-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.gz
go-tangerine-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.bz2
go-tangerine-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.lz
go-tangerine-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.xz
go-tangerine-8b57c494908637a5c0e74f8f7a13b3218e026757.tar.zst
go-tangerine-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')
-rw-r--r--internal/ethapi/api.go74
-rw-r--r--internal/ethapi/backend.go2
-rw-r--r--internal/ethapi/tracer_test.go4
3 files changed, 50 insertions, 30 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 {
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index ebb14a5b5..214214f51 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -51,7 +51,7 @@ type Backend interface {
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetTd(blockHash common.Hash) *big.Int
- GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.EVM, func() error, error)
+ GetEVM(ctx context.Context, msg core.Message, state State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error)
// TxPool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
RemoveTx(txHash common.Hash)
diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go
index 65a23f55e..693afe802 100644
--- a/internal/ethapi/tracer_test.go
+++ b/internal/ethapi/tracer_test.go
@@ -45,7 +45,7 @@ func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(account{}, account{}, big.NewInt(0), big.NewInt(10000))
+ contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
_, err := env.Interpreter().Run(contract, []byte{})
@@ -134,7 +134,7 @@ func TestHaltBetweenSteps(t *testing.T) {
}
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
+ contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
timeout := errors.New("stahp")