Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: use proper BaseExecFee and StoragePrice for interop context #2432

Merged
merged 4 commits into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pkg/compiler/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -200,7 +201,7 @@ func TestAppCall(t *testing.T) {
}

fc := fakechain.NewFakeChain()
ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false, false), 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()))
Expand Down
1 change: 1 addition & 0 deletions pkg/consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Ledger interface {
PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) error
SubscribeForBlocks(ch chan<- *coreb.Block)
UnsubscribeFromBlocks(ch chan<- *coreb.Block)
GetBaseExecFee() int64
interop.Ledger
mempool.Feer
}
Expand Down
17 changes: 16 additions & 1 deletion pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2304,7 +2304,19 @@ func (bc *Blockchain) ManagementContractHash() util.Uint160 {
}

func (bc *Blockchain) newInteropContext(trigger trigger.Type, d *dao.Simple, block *block.Block, tx *transaction.Transaction) *interop.Context {
ic := interop.NewContext(trigger, bc, d, bc.contracts.Management.GetContract, bc.contracts.Contracts, block, tx, bc.log)
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)
}
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:
Expand All @@ -2329,6 +2341,9 @@ func (bc *Blockchain) RegisterPostBlock(f func(func(*transaction.Transaction, *m

// GetBaseExecFee return execution price for `NOP`.
func (bc *Blockchain) GetBaseExecFee() int64 {
if bc.BlockHeight() == 0 {
return interop.DefaultBaseExecFee
}
return bc.contracts.Policy.GetExecFeeFactorInternal(bc.dao)
}

Expand Down
21 changes: 21 additions & 0 deletions pkg/core/blockchain_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"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/transaction"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
Expand Down Expand Up @@ -320,3 +322,22 @@ func setSigner(tx *transaction.Transaction, h util.Uint160) {
Scopes: transaction.Global,
}}
}

// This test checks that value of BaseExecFee returned from corresponding Blockchain's method matches
// the one provided to the constructor of new interop context.
func TestBlockchain_BaseExecFeeBaseStoragePrice_Compat(t *testing.T) {
bc := newTestChain(t)

check := func(t *testing.T) {
ic := bc.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), bc.config.StateRootInHeader, bc.config.P2PSigExtensions), bc.topBlock.Load().(*block.Block), nil)
require.Equal(t, bc.GetBaseExecFee(), ic.BaseExecFee())
require.Equal(t, bc.GetStoragePrice(), ic.BaseStorageFee())
}
t.Run("zero block", func(t *testing.T) {
check(t)
})
t.Run("non-zero block", func(t *testing.T) {
require.NoError(t, bc.AddBlock(bc.newBlock()))
check(t)
})
}
74 changes: 37 additions & 37 deletions pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,57 +37,52 @@ const (
type Ledger interface {
BlockHeight() uint32
CurrentBlockHash() util.Uint256
GetBaseExecFee() int64
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,
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 {
baseExecFee := int64(DefaultBaseExecFee)
dao := d.GetPrivate()

if bc != nil && (block == nil || block.Index != 0) {
baseExecFee = bc.GetBaseExecFee()
}
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,
}
}

Expand Down Expand Up @@ -293,6 +288,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)
Expand Down
3 changes: 2 additions & 1 deletion pkg/core/interop/crypto/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -72,7 +73,7 @@ func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM {
trigger.Verification,
fakechain.NewFakeChain(),
dao.NewSimple(storage.NewMemoryStore(), false, false),
nil, nil, nil,
interop.DefaultBaseExecFee, native.DefaultStoragePrice, nil, nil, nil,
container,
nil)
ic.Container = container
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/interop_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions pkg/core/native/interop.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func Call(ic *interop.Context) error {
return fmt.Errorf("missing call flags for native %d `%s` operation call: %05b vs %05b",
version, m.MD.Name, ic.VM.Context().GetCallFlags(), m.RequiredFlags)
}
invokeFee := m.CPUFee*ic.Chain.GetBaseExecFee() +
m.StorageFee*ic.Chain.GetStoragePrice()
invokeFee := m.CPUFee*ic.BaseExecFee() +
m.StorageFee*ic.BaseStorageFee()
if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 15 additions & 6 deletions pkg/core/native/native_test/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,25 @@ func testGetSet(t *testing.T, c *neotest.ContractInvoker, name string, defaultVa
c.AddNewBlock(t, txSet, txGet)
c.CheckHalt(t, txSet.Hash(), stackitem.Null{})

if name != "GasPerBlock" { // GasPerBlock is set on the next block
c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue+1))
} else {
switch name {
case "GasPerBlock":
// GasPerBlock is set on the next block
c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue))
c.AddNewBlock(t)
randomInvoker.Invoke(t, defaultValue+1, getName)
case "ExecFeeFactor":
// ExecFeeFactor was risen, so the second transaction will fail because
// of gas limit exceeding (its fees are out-of-date).
c.CheckFault(t, txGet.Hash(), "gas limit exceeded")
// Set in a separate block.
committeeInvoker.Invoke(t, stackitem.Null{}, setName, defaultValue+1)
// Get in the next block.
randomInvoker.Invoke(t, defaultValue+1, getName)
default:
c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue+1))
// Get in the next block.
randomInvoker.Invoke(t, defaultValue+1, getName)
}

// Get in the next block.
randomInvoker.Invoke(t, defaultValue+1, getName)
})
}

Expand Down