core: move GetPrice from core to interop

We have additional logic for getting BaseExecFee policy value. This
logic should be moved to interop context instead of being in Policer,
because Policer is just an interface over Policy contract.

After moving this logic to interop context, we need to use it to define
BaseExecFee instead of (Policer).BaseExecFee. Thus, moving
(*Blockchain).GetPrice to (*Context).GetPrice is necessary.
This commit is contained in:
Anna Shaleva 2021-02-05 11:25:22 +03:00
parent 260bcd373c
commit 6a4e312eac
11 changed files with 33 additions and 35 deletions

View file

@ -641,7 +641,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx) systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
v := systemInterop.SpawnVM() v := systemInterop.SpawnVM()
v.LoadScriptWithFlags(tx.Script, callflag.All) v.LoadScriptWithFlags(tx.Script, callflag.All)
v.SetPriceGetter(bc.getPrice) v.SetPriceGetter(systemInterop.GetPrice)
v.LoadToken = contract.LoadToken(systemInterop) v.LoadToken = contract.LoadToken(systemInterop)
v.GasLimit = tx.SystemFee v.GasLimit = tx.SystemFee
@ -849,7 +849,7 @@ func (bc *Blockchain) runPersist(script []byte, block *block.Block, cache *dao.C
systemInterop := bc.newInteropContext(trig, cache, block, nil) systemInterop := bc.newInteropContext(trig, cache, block, nil)
v := systemInterop.SpawnVM() v := systemInterop.SpawnVM()
v.LoadScriptWithFlags(script, callflag.WriteStates|callflag.AllowCall) v.LoadScriptWithFlags(script, callflag.WriteStates|callflag.AllowCall)
v.SetPriceGetter(bc.getPrice) v.SetPriceGetter(systemInterop.GetPrice)
if err := v.Run(); err != nil { if err := v.Run(); err != nil {
return nil, fmt.Errorf("VM has failed: %w", err) return nil, fmt.Errorf("VM has failed: %w", err)
} else if _, err := systemInterop.DAO.Persist(); err != nil { } else if _, err := systemInterop.DAO.Persist(); err != nil {
@ -1718,7 +1718,7 @@ func (bc *Blockchain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *
d.MPT = nil d.MPT = nil
systemInterop := bc.newInteropContext(t, d, b, tx) systemInterop := bc.newInteropContext(t, d, b, tx)
vm := systemInterop.SpawnVM() vm := systemInterop.SpawnVM()
vm.SetPriceGetter(bc.getPrice) vm.SetPriceGetter(systemInterop.GetPrice)
vm.LoadToken = contract.LoadToken(systemInterop) vm.LoadToken = contract.LoadToken(systemInterop)
return vm return vm
} }
@ -1794,7 +1794,7 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
} }
vm := interopCtx.SpawnVM() vm := interopCtx.SpawnVM()
vm.SetPriceGetter(bc.getPrice) vm.SetPriceGetter(interopCtx.GetPrice)
vm.LoadToken = contract.LoadToken(interopCtx) vm.LoadToken = contract.LoadToken(interopCtx)
vm.GasLimit = gas vm.GasLimit = gas
if err := bc.initVerificationVM(interopCtx, hash, witness); err != nil { if err := bc.initVerificationVM(interopCtx, hash, witness); err != nil {
@ -1918,9 +1918,6 @@ func (bc *Blockchain) GetPolicer() blockchainer.Policer {
// GetBaseExecFee return execution price for `NOP`. // GetBaseExecFee return execution price for `NOP`.
func (bc *Blockchain) GetBaseExecFee() int64 { func (bc *Blockchain) GetBaseExecFee() int64 {
if bc.BlockHeight() == 0 {
return interop.DefaultBaseExecFee
}
return bc.contracts.Policy.GetExecFeeFactorInternal(bc.dao) return bc.contracts.Policy.GetExecFeeFactorInternal(bc.dao)
} }

View file

@ -1,13 +1,15 @@
package fee package fee
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
) )
// ECDSAVerifyPrice is a gas price of a single verification.
const ECDSAVerifyPrice = 1 << 15
// Calculate returns network fee for transaction // Calculate returns network fee for transaction
func Calculate(base int64, script []byte) (int64, int) { func Calculate(base int64, script []byte) (int64, int) {
var ( var (
@ -16,13 +18,13 @@ func Calculate(base int64, script []byte) (int64, int) {
) )
if vm.IsSignatureContract(script) { if vm.IsSignatureContract(script) {
size += 67 + io.GetVarSize(script) size += 67 + io.GetVarSize(script)
netFee += Opcode(base, opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + base*crypto.ECDSAVerifyPrice netFee += Opcode(base, opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + base*ECDSAVerifyPrice
} else if m, pubs, ok := vm.ParseMultiSigContract(script); ok { } else if m, pubs, ok := vm.ParseMultiSigContract(script); ok {
n := len(pubs) n := len(pubs)
sizeInv := 66 * m sizeInv := 66 * m
size += io.GetVarSize(sizeInv) + sizeInv + io.GetVarSize(script) size += io.GetVarSize(sizeInv) + sizeInv + io.GetVarSize(script)
netFee += calculateMultisig(base, m) + calculateMultisig(base, n) netFee += calculateMultisig(base, m) + calculateMultisig(base, n)
netFee += Opcode(base, opcode.PUSHNULL) + base*crypto.ECDSAVerifyPrice*int64(n) netFee += Opcode(base, opcode.PUSHNULL) + base*ECDSAVerifyPrice*int64(n)
} else { } else {
// We can support more contract types in the future. // We can support more contract types in the future.
} }

View file

@ -1,12 +0,0 @@
package core
import (
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
)
// getPrice returns a price for executing op with the provided parameter.
func (bc *Blockchain) getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) int64 {
return fee.Opcode(bc.GetBaseExecFee(), op)
}

View file

@ -199,7 +199,7 @@ func (ic *Context) GetFunction(id uint32) *Function {
// BaseExecFee represents factor to multiply syscall prices with. // BaseExecFee represents factor to multiply syscall prices with.
func (ic *Context) BaseExecFee() int64 { func (ic *Context) BaseExecFee() int64 {
if ic.Chain == nil { if ic.Chain == nil || (ic.Block != nil && ic.Block.Index == 0) {
return DefaultBaseExecFee return DefaultBaseExecFee
} }
return ic.Chain.GetPolicer().GetBaseExecFee() return ic.Chain.GetPolicer().GetBaseExecFee()

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"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/crypto" "github.com/nspcc-dev/neo-go/pkg/crypto"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
@ -15,9 +16,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// ECDSAVerifyPrice is a gas price of a single verification.
const ECDSAVerifyPrice = 1 << 15
// ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve. // ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve.
func ECDSASecp256r1Verify(ic *interop.Context) error { func ECDSASecp256r1Verify(ic *interop.Context) error {
return ecdsaVerify(ic, elliptic.P256()) return ecdsaVerify(ic, elliptic.P256())
@ -69,7 +67,7 @@ func ecdsaCheckMultisig(ic *interop.Context, curve elliptic.Curve) error {
if err != nil { if err != nil {
return fmt.Errorf("wrong parameters: %w", err) return fmt.Errorf("wrong parameters: %w", err)
} }
if !ic.VM.AddGas(ic.BaseExecFee() * ECDSAVerifyPrice * int64(len(pkeys))) { if !ic.VM.AddGas(ic.BaseExecFee() * fee.ECDSAVerifyPrice * int64(len(pkeys))) {
return errors.New("gas limit exceeded") return errors.New("gas limit exceeded")
} }
sigs, err := ic.VM.Estack().PopSigElements() sigs, err := ic.VM.Estack().PopSigElements()

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"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/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"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"
@ -247,7 +248,7 @@ func testCurveCHECKMULTISIGBad(t *testing.T, isR1 bool) {
t.Run("1_2 too many signatures", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, true, 2, []int{0}, []int{0, 1}) }) t.Run("1_2 too many signatures", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, true, 2, []int{0}, []int{0, 1}) })
t.Run("gas limit exceeded", func(t *testing.T) { t.Run("gas limit exceeded", func(t *testing.T) {
v := initCHECKMULTISIGVM(t, isR1, 1, []int{0}, []int{0}) v := initCHECKMULTISIGVM(t, isR1, 1, []int{0}, []int{0})
v.GasLimit = ECDSAVerifyPrice - 1 v.GasLimit = fee.ECDSAVerifyPrice - 1
require.Error(t, v.Run()) require.Error(t, v.Run())
}) })

View file

@ -0,0 +1,11 @@
package interop
import (
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
)
// GetPrice returns a price for executing op with the provided parameter.
func (ic *Context) GetPrice(op opcode.Opcode, parameter []byte) int64 {
return fee.Opcode(ic.BaseExecFee(), op)
}

View file

@ -8,6 +8,7 @@ package core
*/ */
import ( import (
"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/interop/binary" "github.com/nspcc-dev/neo-go/pkg/core/interop/binary"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
@ -98,9 +99,9 @@ var systemInterops = []interop.Function{
var neoInterops = []interop.Function{ var neoInterops = []interop.Function{
{Name: interopnames.NeoCryptoVerifyWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1Verify, {Name: interopnames.NeoCryptoVerifyWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1Verify,
Price: crypto.ECDSAVerifyPrice, ParamCount: 3}, Price: fee.ECDSAVerifyPrice, ParamCount: 3},
{Name: interopnames.NeoCryptoVerifyWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1Verify, {Name: interopnames.NeoCryptoVerifyWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1Verify,
Price: crypto.ECDSAVerifyPrice, ParamCount: 3}, Price: fee.ECDSAVerifyPrice, ParamCount: 3},
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 3}, {Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 3},
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3}, {Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3},
{Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1 << 15, ParamCount: 1}, {Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1 << 15, ParamCount: 1},

View file

@ -28,9 +28,9 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
"github.com/nspcc-dev/neo-go/pkg/rpc/response" "github.com/nspcc-dev/neo-go/pkg/rpc/response"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "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"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"

View file

@ -65,7 +65,7 @@ type VM struct {
state State state State
// callback to get interop price // callback to get interop price
getPrice func(*VM, opcode.Opcode, []byte) int64 getPrice func(opcode.Opcode, []byte) int64
istack *Stack // invocation stack. istack *Stack // invocation stack.
estack *Stack // execution stack. estack *Stack // execution stack.
@ -119,7 +119,7 @@ func (v *VM) newItemStack(n string) *Stack {
// SetPriceGetter registers the given PriceGetterFunc in v. // SetPriceGetter registers the given PriceGetterFunc in v.
// f accepts vm's Context, current instruction and instruction parameter. // f accepts vm's Context, current instruction and instruction parameter.
func (v *VM) SetPriceGetter(f func(*VM, opcode.Opcode, []byte) int64) { func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
v.getPrice = f v.getPrice = f
} }
@ -528,7 +528,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
}() }()
if v.getPrice != nil && ctx.ip < len(ctx.prog) { if v.getPrice != nil && ctx.ip < len(ctx.prog) {
v.gasConsumed += v.getPrice(v, op, parameter) v.gasConsumed += v.getPrice(op, parameter)
if v.GasLimit >= 0 && v.gasConsumed > v.GasLimit { if v.GasLimit >= 0 && v.gasConsumed > v.GasLimit {
panic("gas limit is exceeded") panic("gas limit is exceeded")
} }

View file

@ -62,7 +62,7 @@ func TestVM_SetPriceGetter(t *testing.T) {
require.EqualValues(t, 0, v.GasConsumed()) require.EqualValues(t, 0, v.GasConsumed())
}) })
v.SetPriceGetter(func(_ *VM, op opcode.Opcode, p []byte) int64 { v.SetPriceGetter(func(op opcode.Opcode, p []byte) int64 {
if op == opcode.PUSH4 { if op == opcode.PUSH4 {
return 1 return 1
} else if op == opcode.PUSHDATA1 && bytes.Equal(p, []byte{0xCA, 0xFE}) { } else if op == opcode.PUSHDATA1 && bytes.Equal(p, []byte{0xCA, 0xFE}) {