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.
This commit is contained in:
Anna Shaleva 2022-04-08 12:27:25 +03:00
parent 91a4bc5beb
commit 544f2c2cb2
7 changed files with 51 additions and 38 deletions

View file

@ -12,6 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core" "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/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "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/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
@ -200,7 +201,7 @@ func TestAppCall(t *testing.T) {
} }
fc := fakechain.NewFakeChain() 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) { t.Run("valid script", func(t *testing.T) {
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE())) src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))

View file

@ -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 { func (bc *Blockchain) newInteropContext(trigger trigger.Type, d *dao.Simple, block *block.Block, tx *transaction.Transaction) *interop.Context {
baseExecFee := int64(interop.DefaultBaseExecFee) baseExecFee := int64(interop.DefaultBaseExecFee)
if block == nil || block.Index != 0 { if block == nil || block.Index != 0 {
// Use provided dao instead of Blockchain's one to fetch possible ExecFeeFactor // Use provided dao instead of Blockchain's one to fetch possible ExecFeeFactor
// changes that were not yet persisted to Blockchain's dao. // changes that were not yet persisted to Blockchain's dao.
baseExecFee = bc.contracts.Policy.GetExecFeeFactorInternal(d) 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 ic.Functions = systemInterops
switch { switch {
case tx != nil: case tx != nil:

View file

@ -41,48 +41,49 @@ type Ledger interface {
GetBlock(hash util.Uint256) (*block.Block, error) GetBlock(hash util.Uint256) (*block.Block, error)
GetConfig() config.ProtocolConfiguration GetConfig() config.ProtocolConfiguration
GetHeaderHash(int) util.Uint256 GetHeaderHash(int) util.Uint256
GetStoragePrice() int64
} }
// Context represents context in which interops are executed. // Context represents context in which interops are executed.
type Context struct { type Context struct {
Chain Ledger Chain Ledger
Container hash.Hashable Container hash.Hashable
Network uint32 Network uint32
Natives []Contract Natives []Contract
Trigger trigger.Type Trigger trigger.Type
Block *block.Block Block *block.Block
NonceData [16]byte NonceData [16]byte
Tx *transaction.Transaction Tx *transaction.Transaction
DAO *dao.Simple DAO *dao.Simple
Notifications []state.NotificationEvent Notifications []state.NotificationEvent
Log *zap.Logger Log *zap.Logger
VM *vm.VM VM *vm.VM
Functions []Function Functions []Function
Invocations map[util.Uint160]int Invocations map[util.Uint160]int
cancelFuncs []context.CancelFunc cancelFuncs []context.CancelFunc
getContract func(*dao.Simple, util.Uint160) (*state.Contract, error) getContract func(*dao.Simple, util.Uint160) (*state.Contract, error)
baseExecFee int64 baseExecFee int64
signers []transaction.Signer baseStorageFee int64
signers []transaction.Signer
} }
// NewContext returns new interop context. // 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, getContract func(*dao.Simple, util.Uint160) (*state.Contract, error), natives []Contract,
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context { block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
dao := d.GetPrivate() dao := d.GetPrivate()
return &Context{ return &Context{
Chain: bc, Chain: bc,
Network: uint32(bc.GetConfig().Magic), Network: uint32(bc.GetConfig().Magic),
Natives: natives, Natives: natives,
Trigger: trigger, Trigger: trigger,
Block: block, Block: block,
Tx: tx, Tx: tx,
DAO: dao, DAO: dao,
Log: log, Log: log,
Invocations: make(map[util.Uint160]int), Invocations: make(map[util.Uint160]int),
getContract: getContract, getContract: getContract,
baseExecFee: baseExecFee, baseExecFee: baseExecFee,
baseStorageFee: baseStorageFee,
} }
} }
@ -288,6 +289,11 @@ func (ic *Context) BaseExecFee() int64 {
return ic.baseExecFee 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. // SyscallHandler handles syscall with id.
func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error { func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error {
f := ic.GetFunction(id) f := ic.GetFunction(id)

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/dao" "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/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "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/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
@ -72,7 +73,7 @@ func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM {
trigger.Verification, trigger.Verification,
fakechain.NewFakeChain(), fakechain.NewFakeChain(),
dao.NewSimple(storage.NewMemoryStore(), false, false), dao.NewSimple(storage.NewMemoryStore(), false, false),
interop.DefaultBaseExecFee, nil, nil, nil, interop.DefaultBaseExecFee, native.DefaultStoragePrice, nil, nil, nil,
container, container,
nil) nil)
ic.Container = container ic.Container = container

View file

@ -126,7 +126,7 @@ func putWithContext(ic *interop.Context, stc *StorageContext, key []byte, value
sizeInc = (len(si)-1)/4 + 1 + len(value) - len(si) 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 return errGasLimitExceeded
} }
ic.DAO.PutStorageItem(stc.ID, key, value) ic.DAO.PutStorageItem(stc.ID, key, value)

View file

@ -43,7 +43,7 @@ func Call(ic *interop.Context) error {
version, m.MD.Name, ic.VM.Context().GetCallFlags(), m.RequiredFlags) version, m.MD.Name, ic.VM.Context().GetCallFlags(), m.RequiredFlags)
} }
invokeFee := m.CPUFee*ic.Chain.GetBaseExecFee() + invokeFee := m.CPUFee*ic.Chain.GetBaseExecFee() +
m.StorageFee*ic.Chain.GetStoragePrice() m.StorageFee*ic.BaseStorageFee()
if !ic.VM.AddGas(invokeFee) { if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded") return errors.New("gas limit exceeded")
} }

View file

@ -198,7 +198,7 @@ func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stac
return nil, nil, fmt.Errorf("invalid manifest: %w", err) 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 { if isDeploy {
fee := m.minimumDeploymentFee(ic.DAO) fee := m.minimumDeploymentFee(ic.DAO)
if fee > gas { if fee > gas {