From 544f2c2cb2be9bd2e7004d5c26905a4a3528f31c Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 8 Apr 2022 12:27:25 +0300 Subject: [PATCH] core: use proper storage price within the whole interop context We shouldn't use StoragePrice from Blockchain because its dao doesn't contain the whole set of changes from previouse transactions in the current block. Instead, we should use an updated storage price for each transaction and retrieve the price from cached DAO. --- pkg/compiler/interop_test.go | 3 +- pkg/core/blockchain.go | 9 +++- pkg/core/interop/context.go | 68 +++++++++++++++------------ pkg/core/interop/crypto/ecdsa_test.go | 3 +- pkg/core/interop_system.go | 2 +- pkg/core/native/interop.go | 2 +- pkg/core/native/management.go | 2 +- 7 files changed, 51 insertions(+), 38 deletions(-) diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 65c43b1f5..3243f4a1d 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -12,6 +12,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" @@ -200,7 +201,7 @@ func TestAppCall(t *testing.T) { } fc := fakechain.NewFakeChain() - ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false, false), interop.DefaultBaseExecFee, contractGetter, nil, nil, nil, zaptest.NewLogger(t)) + ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false, false), interop.DefaultBaseExecFee, native.DefaultStoragePrice, contractGetter, nil, nil, nil, zaptest.NewLogger(t)) t.Run("valid script", func(t *testing.T) { src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE())) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 926e612de..19045208f 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -2305,13 +2305,18 @@ func (bc *Blockchain) ManagementContractHash() util.Uint160 { func (bc *Blockchain) newInteropContext(trigger trigger.Type, d *dao.Simple, block *block.Block, tx *transaction.Transaction) *interop.Context { baseExecFee := int64(interop.DefaultBaseExecFee) - if block == nil || block.Index != 0 { // Use provided dao instead of Blockchain's one to fetch possible ExecFeeFactor // changes that were not yet persisted to Blockchain's dao. baseExecFee = bc.contracts.Policy.GetExecFeeFactorInternal(d) } - ic := interop.NewContext(trigger, bc, d, baseExecFee, bc.contracts.Management.GetContract, bc.contracts.Contracts, block, tx, bc.log) + baseStorageFee := int64(native.DefaultStoragePrice) + if block == nil || block.Index != 0 { + // Use provided dao instead of Blockchain's one to fetch possible StoragePrice + // changes that were not yet persisted to Blockchain's dao. + baseStorageFee = bc.contracts.Policy.GetStoragePriceInternal(d) + } + ic := interop.NewContext(trigger, bc, d, baseExecFee, baseStorageFee, bc.contracts.Management.GetContract, bc.contracts.Contracts, block, tx, bc.log) ic.Functions = systemInterops switch { case tx != nil: diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index f4792645a..22c1486e8 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -41,48 +41,49 @@ type Ledger interface { GetBlock(hash util.Uint256) (*block.Block, error) GetConfig() config.ProtocolConfiguration GetHeaderHash(int) util.Uint256 - GetStoragePrice() int64 } // Context represents context in which interops are executed. type Context struct { - Chain Ledger - Container hash.Hashable - Network uint32 - Natives []Contract - Trigger trigger.Type - Block *block.Block - NonceData [16]byte - Tx *transaction.Transaction - DAO *dao.Simple - Notifications []state.NotificationEvent - Log *zap.Logger - VM *vm.VM - Functions []Function - Invocations map[util.Uint160]int - cancelFuncs []context.CancelFunc - getContract func(*dao.Simple, util.Uint160) (*state.Contract, error) - baseExecFee int64 - signers []transaction.Signer + Chain Ledger + Container hash.Hashable + Network uint32 + Natives []Contract + Trigger trigger.Type + Block *block.Block + NonceData [16]byte + Tx *transaction.Transaction + DAO *dao.Simple + Notifications []state.NotificationEvent + Log *zap.Logger + VM *vm.VM + Functions []Function + Invocations map[util.Uint160]int + cancelFuncs []context.CancelFunc + getContract func(*dao.Simple, util.Uint160) (*state.Contract, error) + baseExecFee int64 + baseStorageFee int64 + signers []transaction.Signer } // NewContext returns new interop context. -func NewContext(trigger trigger.Type, bc Ledger, d *dao.Simple, baseExecFee int64, +func NewContext(trigger trigger.Type, bc Ledger, d *dao.Simple, baseExecFee, baseStorageFee int64, getContract func(*dao.Simple, util.Uint160) (*state.Contract, error), natives []Contract, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context { dao := d.GetPrivate() return &Context{ - Chain: bc, - Network: uint32(bc.GetConfig().Magic), - Natives: natives, - Trigger: trigger, - Block: block, - Tx: tx, - DAO: dao, - Log: log, - Invocations: make(map[util.Uint160]int), - getContract: getContract, - baseExecFee: baseExecFee, + Chain: bc, + Network: uint32(bc.GetConfig().Magic), + Natives: natives, + Trigger: trigger, + Block: block, + Tx: tx, + DAO: dao, + Log: log, + Invocations: make(map[util.Uint160]int), + getContract: getContract, + baseExecFee: baseExecFee, + baseStorageFee: baseStorageFee, } } @@ -288,6 +289,11 @@ func (ic *Context) BaseExecFee() int64 { return ic.baseExecFee } +// BaseStorageFee represents price for storing one byte of data in the contract storage. +func (ic *Context) BaseStorageFee() int64 { + return ic.baseStorageFee +} + // SyscallHandler handles syscall with id. func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error { f := ic.GetFunction(id) diff --git a/pkg/core/interop/crypto/ecdsa_test.go b/pkg/core/interop/crypto/ecdsa_test.go index 5f6839674..9cfb370e1 100644 --- a/pkg/core/interop/crypto/ecdsa_test.go +++ b/pkg/core/interop/crypto/ecdsa_test.go @@ -10,6 +10,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" @@ -72,7 +73,7 @@ func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM { trigger.Verification, fakechain.NewFakeChain(), dao.NewSimple(storage.NewMemoryStore(), false, false), - interop.DefaultBaseExecFee, nil, nil, nil, + interop.DefaultBaseExecFee, native.DefaultStoragePrice, nil, nil, nil, container, nil) ic.Container = container diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 3a6facde2..42312f1f3 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -126,7 +126,7 @@ func putWithContext(ic *interop.Context, stc *StorageContext, key []byte, value sizeInc = (len(si)-1)/4 + 1 + len(value) - len(si) } } - if !ic.VM.AddGas(int64(sizeInc) * ic.Chain.GetStoragePrice()) { + if !ic.VM.AddGas(int64(sizeInc) * ic.BaseStorageFee()) { return errGasLimitExceeded } ic.DAO.PutStorageItem(stc.ID, key, value) diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index c97850138..92fdf3717 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -43,7 +43,7 @@ func Call(ic *interop.Context) error { version, m.MD.Name, ic.VM.Context().GetCallFlags(), m.RequiredFlags) } invokeFee := m.CPUFee*ic.Chain.GetBaseExecFee() + - m.StorageFee*ic.Chain.GetStoragePrice() + m.StorageFee*ic.BaseStorageFee() if !ic.VM.AddGas(invokeFee) { return errors.New("gas limit exceeded") } diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 4591133c3..dc9257331 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -198,7 +198,7 @@ func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stac return nil, nil, fmt.Errorf("invalid manifest: %w", err) } - gas := ic.Chain.GetStoragePrice() * int64(len(nefBytes)+len(manifestBytes)) + gas := ic.BaseStorageFee() * int64(len(nefBytes)+len(manifestBytes)) if isDeploy { fee := m.minimumDeploymentFee(ic.DAO) if fee > gas {