From 7256efd1ed2b82029d983fd55001676e5a147f83 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 9 Jun 2020 12:23:14 +0300 Subject: [PATCH] vm: implement (*VM).AddGas Calculating interop prices can be tricky. It is more convenitent to burn gas inside interops where necessary parameters are popped. --- pkg/core/gas_price.go | 3 +++ pkg/core/interop_neo.go | 4 ++++ pkg/vm/vm.go | 6 ++++++ pkg/vm/vm_test.go | 8 ++++++++ 4 files changed, 21 insertions(+) diff --git a/pkg/core/gas_price.go b/pkg/core/gas_price.go index e7318b52e..b9130b0e1 100644 --- a/pkg/core/gas_price.go +++ b/pkg/core/gas_price.go @@ -11,6 +11,9 @@ import ( // of 0.001 GAS = Fixed8(10^5). const interopGasRatio = 100000 +// StoragePrice is a price for storing 1 byte of storage. +const StoragePrice = 100000 + // getPrice returns a price for executing op with the provided parameter. // Some SYSCALLs have variable price depending on their arguments. func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) util.Fixed8 { diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 08f07de56..b80031e28 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -73,6 +74,9 @@ func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract, if len(manifestBytes) > manifest.MaxManifestSize { return nil, errors.New("manifest is too big") } + if !v.AddGas(util.Fixed8(StoragePrice * (len(script) + len(manifestBytes)))) { + return nil, errors.New("gas limit exceeded") + } var m manifest.Manifest r := io.NewBinReaderFromBuf(manifestBytes) m.DecodeBinary(r) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 6d0c36f29..3f933595f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -136,6 +136,12 @@ func (v *VM) SetGasLimit(max util.Fixed8) { v.gasLimit = max } +// AddGas consumes specified amount of gas. It returns true iff gas limit wasn't exceeded. +func (v *VM) AddGas(gas util.Fixed8) bool { + v.gasConsumed += gas + return v.gasLimit == 0 || v.gasConsumed <= v.gasLimit +} + // Estack returns the evaluation stack so interop hooks can utilize this. func (v *VM) Estack() *Stack { return v.estack diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 746df0494..ae9a06fd0 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -98,6 +98,14 @@ func TestVM_SetPriceGetter(t *testing.T) { }) } +func TestAddGas(t *testing.T) { + v := New() + v.SetGasLimit(10) + require.True(t, v.AddGas(5)) + require.True(t, v.AddGas(5)) + require.False(t, v.AddGas(5)) +} + func TestBytesToPublicKey(t *testing.T) { v := New() cache := v.GetPublicKeys()