forked from TrueCloudLab/neoneo-go
Merge pull request #1615 from nspcc-dev/opcodes
core: redefine opcode prices
This commit is contained in:
commit
4dcd06ef44
26 changed files with 577 additions and 433 deletions
|
@ -92,7 +92,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160,
|
||||||
func SignTx(bc blockchainer.Blockchainer, txs ...*transaction.Transaction) error {
|
func SignTx(bc blockchainer.Blockchainer, txs ...*transaction.Transaction) error {
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(ownerScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetPolicer().GetBaseExecFee(), ownerScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
size += sizeDelta
|
size += sizeDelta
|
||||||
tx.NetworkFee += int64(size) * bc.FeePerByte()
|
tx.NetworkFee += int64(size) * bc.FeePerByte()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"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"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
"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/native"
|
"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"
|
||||||
|
@ -32,7 +33,7 @@ func TestNewService(t *testing.T) {
|
||||||
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 100000)
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 100000)
|
||||||
tx.ValidUntilBlock = 1
|
tx.ValidUntilBlock = 1
|
||||||
addSender(t, tx)
|
addSender(t, tx)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
signTx(t, srv.Chain, tx)
|
||||||
require.NoError(t, srv.Chain.PoolTx(tx))
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
||||||
|
|
||||||
var txx []block.Transaction
|
var txx []block.Transaction
|
||||||
|
@ -170,7 +171,7 @@ func TestService_GetVerified(t *testing.T) {
|
||||||
txs = append(txs, tx)
|
txs = append(txs, tx)
|
||||||
}
|
}
|
||||||
addSender(t, txs...)
|
addSender(t, txs...)
|
||||||
signTx(t, srv.Chain.FeePerByte(), txs...)
|
signTx(t, srv.Chain, txs...)
|
||||||
require.NoError(t, srv.Chain.PoolTx(txs[3]))
|
require.NoError(t, srv.Chain.PoolTx(txs[3]))
|
||||||
|
|
||||||
hashes := []util.Uint256{txs[0].Hash(), txs[1].Hash(), txs[2].Hash()}
|
hashes := []util.Uint256{txs[0].Hash(), txs[1].Hash(), txs[2].Hash()}
|
||||||
|
@ -257,7 +258,7 @@ func TestService_getTx(t *testing.T) {
|
||||||
tx.Nonce = 1234
|
tx.Nonce = 1234
|
||||||
tx.ValidUntilBlock = 1
|
tx.ValidUntilBlock = 1
|
||||||
addSender(t, tx)
|
addSender(t, tx)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
signTx(t, srv.Chain, tx)
|
||||||
h := tx.Hash()
|
h := tx.Hash()
|
||||||
|
|
||||||
require.Equal(t, nil, srv.getTx(h))
|
require.Equal(t, nil, srv.getTx(h))
|
||||||
|
@ -351,7 +352,7 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
||||||
tx.ValidUntilBlock = 1
|
tx.ValidUntilBlock = 1
|
||||||
addSender(t, tx)
|
addSender(t, tx)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
signTx(t, srv.Chain, tx)
|
||||||
require.NoError(t, srv.Chain.PoolTx(tx))
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
||||||
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
|
@ -360,7 +361,7 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
||||||
tx.ValidUntilBlock = 1
|
tx.ValidUntilBlock = 1
|
||||||
addSender(t, tx)
|
addSender(t, tx)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
signTx(t, srv.Chain, tx)
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
||||||
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
})
|
})
|
||||||
|
@ -369,12 +370,12 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
tx1.NetworkFee = 20_000_000 * native.GASFactor
|
tx1.NetworkFee = 20_000_000 * native.GASFactor
|
||||||
tx1.ValidUntilBlock = 1
|
tx1.ValidUntilBlock = 1
|
||||||
addSender(t, tx1)
|
addSender(t, tx1)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx1)
|
signTx(t, srv.Chain, tx1)
|
||||||
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
||||||
tx2.NetworkFee = 20_000_000 * native.GASFactor
|
tx2.NetworkFee = 20_000_000 * native.GASFactor
|
||||||
tx2.ValidUntilBlock = 1
|
tx2.ValidUntilBlock = 1
|
||||||
addSender(t, tx2)
|
addSender(t, tx2)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx2)
|
signTx(t, srv.Chain, tx2)
|
||||||
require.NoError(t, srv.Chain.PoolTx(tx1))
|
require.NoError(t, srv.Chain.PoolTx(tx1))
|
||||||
require.Error(t, srv.Chain.PoolTx(tx2))
|
require.Error(t, srv.Chain.PoolTx(tx2))
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx2)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx2)
|
||||||
|
@ -391,7 +392,7 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
tx := transaction.New(netmode.UnitTestNet, script, 100000)
|
tx := transaction.New(netmode.UnitTestNet, script, 100000)
|
||||||
tx.ValidUntilBlock = 1
|
tx.ValidUntilBlock = 1
|
||||||
addSender(t, tx)
|
addSender(t, tx)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
signTx(t, srv.Chain, tx)
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
||||||
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
})
|
})
|
||||||
|
@ -399,7 +400,7 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, 100000)
|
||||||
tx.ValidUntilBlock = 1
|
tx.ValidUntilBlock = 1
|
||||||
addSender(t, tx)
|
addSender(t, tx)
|
||||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
signTx(t, srv.Chain, tx)
|
||||||
tx.Scripts[0].InvocationScript[16] = ^tx.Scripts[0].InvocationScript[16]
|
tx.Scripts[0].InvocationScript[16] = ^tx.Scripts[0].InvocationScript[16]
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
||||||
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
|
@ -410,7 +411,7 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
txes[i] = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, srv.Chain.GetPolicer().GetMaxBlockSystemFee()/2+1)
|
txes[i] = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, srv.Chain.GetPolicer().GetMaxBlockSystemFee()/2+1)
|
||||||
txes[i].ValidUntilBlock = 1
|
txes[i].ValidUntilBlock = 1
|
||||||
addSender(t, txes[i])
|
addSender(t, txes[i])
|
||||||
signTx(t, srv.Chain.FeePerByte(), txes[i])
|
signTx(t, srv.Chain, txes[i])
|
||||||
}
|
}
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0, txes...)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, txes...)
|
||||||
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
|
@ -500,7 +501,7 @@ func addSender(t *testing.T, txs ...*transaction.Transaction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func signTx(t *testing.T, feePerByte int64, txs ...*transaction.Transaction) {
|
func signTx(t *testing.T, bc blockchainer.Blockchainer, txs ...*transaction.Transaction) {
|
||||||
validators := make([]*keys.PublicKey, 4)
|
validators := make([]*keys.PublicKey, 4)
|
||||||
privNetKeys := make([]*keys.PrivateKey, 4)
|
privNetKeys := make([]*keys.PrivateKey, 4)
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
|
@ -512,10 +513,10 @@ func signTx(t *testing.T, feePerByte int64, txs ...*transaction.Transaction) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetPolicer().GetBaseExecFee(), rawScript)
|
||||||
tx.NetworkFee += +netFee
|
tx.NetworkFee += +netFee
|
||||||
size += sizeDelta
|
size += sizeDelta
|
||||||
tx.NetworkFee += int64(size) * feePerByte
|
tx.NetworkFee += int64(size) * bc.FeePerByte()
|
||||||
data := tx.GetSignedPart()
|
data := tx.GetSignedPart()
|
||||||
|
|
||||||
buf := io.NewBufBinWriter()
|
buf := io.NewBufBinWriter()
|
||||||
|
|
|
@ -620,7 +620,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, smartcontract.All)
|
v.LoadScriptWithFlags(tx.Script, smartcontract.All)
|
||||||
v.SetPriceGetter(getPrice)
|
v.SetPriceGetter(bc.getPrice)
|
||||||
v.GasLimit = tx.SystemFee
|
v.GasLimit = tx.SystemFee
|
||||||
|
|
||||||
err := v.Run()
|
err := v.Run()
|
||||||
|
@ -753,7 +753,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, smartcontract.WriteStates|smartcontract.AllowCall)
|
v.LoadScriptWithFlags(script, smartcontract.WriteStates|smartcontract.AllowCall)
|
||||||
v.SetPriceGetter(getPrice)
|
v.SetPriceGetter(bc.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 {
|
||||||
|
@ -1622,7 +1622,7 @@ func (bc *Blockchain) GetTestVM(tx *transaction.Transaction, b *block.Block) *vm
|
||||||
d.MPT = nil
|
d.MPT = nil
|
||||||
systemInterop := bc.newInteropContext(trigger.Application, d, b, tx)
|
systemInterop := bc.newInteropContext(trigger.Application, d, b, tx)
|
||||||
vm := systemInterop.SpawnVM()
|
vm := systemInterop.SpawnVM()
|
||||||
vm.SetPriceGetter(getPrice)
|
vm.SetPriceGetter(bc.getPrice)
|
||||||
return vm
|
return vm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1692,7 +1692,7 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
|
||||||
}
|
}
|
||||||
|
|
||||||
vm := interopCtx.SpawnVM()
|
vm := interopCtx.SpawnVM()
|
||||||
vm.SetPriceGetter(getPrice)
|
vm.SetPriceGetter(bc.getPrice)
|
||||||
vm.GasLimit = gas
|
vm.GasLimit = gas
|
||||||
if err := bc.initVerificationVM(interopCtx, hash, witness); err != nil {
|
if err := bc.initVerificationVM(interopCtx, hash, witness); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -1813,6 +1813,11 @@ func (bc *Blockchain) GetPolicer() blockchainer.Policer {
|
||||||
return bc
|
return bc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBaseExecFee return execution price for `NOP`.
|
||||||
|
func (bc *Blockchain) GetBaseExecFee() int64 {
|
||||||
|
return bc.contracts.Policy.GetExecFeeFactorInternal(bc.dao)
|
||||||
|
}
|
||||||
|
|
||||||
// GetMaxBlockSize returns maximum allowed block size from native Policy contract.
|
// GetMaxBlockSize returns maximum allowed block size from native Policy contract.
|
||||||
func (bc *Blockchain) GetMaxBlockSize() uint32 {
|
func (bc *Blockchain) GetMaxBlockSize() uint32 {
|
||||||
return bc.contracts.Policy.GetMaxBlockSizeInternal(bc.dao)
|
return bc.contracts.Policy.GetMaxBlockSizeInternal(bc.dao)
|
||||||
|
@ -1828,4 +1833,9 @@ func (bc *Blockchain) GetMaxVerificationGAS() int64 {
|
||||||
return bc.contracts.Policy.GetMaxVerificationGas(bc.dao)
|
return bc.contracts.Policy.GetMaxVerificationGas(bc.dao)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStoragePrice returns current storage price.
|
||||||
|
func (bc *Blockchain) GetStoragePrice() int64 {
|
||||||
|
return bc.contracts.Policy.GetStoragePriceInternal(bc.dao)
|
||||||
|
}
|
||||||
|
|
||||||
// -- end Policer.
|
// -- end Policer.
|
||||||
|
|
|
@ -338,7 +338,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("AlmostEnoughNetworkFee", func(t *testing.T) {
|
t.Run("AlmostEnoughNetworkFee", func(t *testing.T) {
|
||||||
tx := bc.newTestTx(h, testScript)
|
tx := bc.newTestTx(h, testScript)
|
||||||
verificationNetFee, calcultedScriptSize := fee.Calculate(accs[0].Contract.Script)
|
verificationNetFee, calcultedScriptSize := fee.Calculate(bc.GetBaseExecFee(), accs[0].Contract.Script)
|
||||||
expectedSize := io.GetVarSize(tx) + calcultedScriptSize
|
expectedSize := io.GetVarSize(tx) + calcultedScriptSize
|
||||||
calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
||||||
tx.NetworkFee = calculatedNetFee - 1
|
tx.NetworkFee = calculatedNetFee - 1
|
||||||
|
@ -348,7 +348,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("EnoughNetworkFee", func(t *testing.T) {
|
t.Run("EnoughNetworkFee", func(t *testing.T) {
|
||||||
tx := bc.newTestTx(h, testScript)
|
tx := bc.newTestTx(h, testScript)
|
||||||
verificationNetFee, calcultedScriptSize := fee.Calculate(accs[0].Contract.Script)
|
verificationNetFee, calcultedScriptSize := fee.Calculate(bc.GetBaseExecFee(), accs[0].Contract.Script)
|
||||||
expectedSize := io.GetVarSize(tx) + calcultedScriptSize
|
expectedSize := io.GetVarSize(tx) + calcultedScriptSize
|
||||||
calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
||||||
tx.NetworkFee = calculatedNetFee
|
tx.NetworkFee = calculatedNetFee
|
||||||
|
@ -359,7 +359,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
t.Run("CalculateNetworkFee, signature script", func(t *testing.T) {
|
t.Run("CalculateNetworkFee, signature script", func(t *testing.T) {
|
||||||
tx := bc.newTestTx(h, testScript)
|
tx := bc.newTestTx(h, testScript)
|
||||||
expectedSize := io.GetVarSize(tx)
|
expectedSize := io.GetVarSize(tx)
|
||||||
verificationNetFee, calculatedScriptSize := fee.Calculate(accs[0].Contract.Script)
|
verificationNetFee, calculatedScriptSize := fee.Calculate(bc.GetBaseExecFee(), accs[0].Contract.Script)
|
||||||
expectedSize += calculatedScriptSize
|
expectedSize += calculatedScriptSize
|
||||||
expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
||||||
tx.NetworkFee = expectedNetFee
|
tx.NetworkFee = expectedNetFee
|
||||||
|
@ -378,7 +378,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
require.NoError(t, multisigAcc.ConvertMultisig(1, pKeys))
|
require.NoError(t, multisigAcc.ConvertMultisig(1, pKeys))
|
||||||
multisigHash := hash.Hash160(multisigAcc.Contract.Script)
|
multisigHash := hash.Hash160(multisigAcc.Contract.Script)
|
||||||
tx := bc.newTestTx(multisigHash, testScript)
|
tx := bc.newTestTx(multisigHash, testScript)
|
||||||
verificationNetFee, calculatedScriptSize := fee.Calculate(multisigAcc.Contract.Script)
|
verificationNetFee, calculatedScriptSize := fee.Calculate(bc.GetBaseExecFee(), multisigAcc.Contract.Script)
|
||||||
expectedSize := io.GetVarSize(tx) + calculatedScriptSize
|
expectedSize := io.GetVarSize(tx) + calculatedScriptSize
|
||||||
expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
||||||
tx.NetworkFee = expectedNetFee
|
tx.NetworkFee = expectedNetFee
|
||||||
|
@ -478,7 +478,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
rawScript := testchain.CommitteeVerificationScript()
|
rawScript := testchain.CommitteeVerificationScript()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||||
data := tx.GetSignedPart()
|
data := tx.GetSignedPart()
|
||||||
|
@ -517,7 +517,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
Scopes: transaction.None,
|
Scopes: transaction.None,
|
||||||
}}
|
}}
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(oracleScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), oracleScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||||
return tx
|
return tx
|
||||||
|
@ -614,7 +614,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
rawScript := testchain.CommitteeVerificationScript()
|
rawScript := testchain.CommitteeVerificationScript()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||||
data := tx.GetSignedPart()
|
data := tx.GetSignedPart()
|
||||||
|
@ -652,7 +652,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
rawScript := testchain.CommitteeVerificationScript()
|
rawScript := testchain.CommitteeVerificationScript()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||||
data := tx.GetSignedPart()
|
data := tx.GetSignedPart()
|
||||||
|
@ -692,7 +692,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
rawScript := testchain.CommitteeVerificationScript()
|
rawScript := testchain.CommitteeVerificationScript()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||||
data := tx.GetSignedPart()
|
data := tx.GetSignedPart()
|
||||||
|
@ -772,7 +772,7 @@ func TestVerifyTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
rawScript := testchain.CommitteeVerificationScript()
|
rawScript := testchain.CommitteeVerificationScript()
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||||
data := tx.GetSignedPart()
|
data := tx.GetSignedPart()
|
||||||
|
@ -964,14 +964,14 @@ func TestVerifyTx(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(testchain.MultisigVerificationScript())
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), testchain.MultisigVerificationScript())
|
||||||
tx.NetworkFee = netFee + // multisig witness verification price
|
tx.NetworkFee = netFee + // multisig witness verification price
|
||||||
int64(size)*bc.FeePerByte() + // fee for unsigned size
|
int64(size)*bc.FeePerByte() + // fee for unsigned size
|
||||||
int64(sizeDelta)*bc.FeePerByte() + //fee for multisig size
|
int64(sizeDelta)*bc.FeePerByte() + //fee for multisig size
|
||||||
66*bc.FeePerByte() + // fee for Notary signature size (66 bytes for Invocation script and 0 bytes for Verification script)
|
66*bc.FeePerByte() + // fee for Notary signature size (66 bytes for Invocation script and 0 bytes for Verification script)
|
||||||
2*bc.FeePerByte() + // fee for the length of each script in Notary witness (they are nil, so we did not take them into account during `size` calculation)
|
2*bc.FeePerByte() + // fee for the length of each script in Notary witness (they are nil, so we did not take them into account during `size` calculation)
|
||||||
transaction.NotaryServiceFeePerKey + // fee for Notary attribute
|
transaction.NotaryServiceFeePerKey + // fee for Notary attribute
|
||||||
fee.Opcode( // Notary verification script
|
fee.Opcode(bc.GetBaseExecFee(), // Notary verification script
|
||||||
opcode.PUSHDATA1, opcode.RET, // invocation script
|
opcode.PUSHDATA1, opcode.RET, // invocation script
|
||||||
opcode.DEPTH, opcode.PACK, opcode.PUSHDATA1, opcode.RET, // arguments for native verification call
|
opcode.DEPTH, opcode.PACK, opcode.PUSHDATA1, opcode.RET, // arguments for native verification call
|
||||||
opcode.PUSHDATA1, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call
|
opcode.PUSHDATA1, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call
|
||||||
|
|
|
@ -2,7 +2,9 @@ package blockchainer
|
||||||
|
|
||||||
// Policer is an interface that abstracts the implementation of policy methods.
|
// Policer is an interface that abstracts the implementation of policy methods.
|
||||||
type Policer interface {
|
type Policer interface {
|
||||||
|
GetBaseExecFee() int64
|
||||||
GetMaxBlockSize() uint32
|
GetMaxBlockSize() uint32
|
||||||
GetMaxBlockSystemFee() int64
|
GetMaxBlockSystemFee() int64
|
||||||
GetMaxVerificationGAS() int64
|
GetMaxVerificationGAS() int64
|
||||||
|
GetStoragePrice() int64
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,31 +9,31 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Calculate returns network fee for transaction
|
// Calculate returns network fee for transaction
|
||||||
func Calculate(script []byte) (int64, int) {
|
func Calculate(base int64, script []byte) (int64, int) {
|
||||||
var (
|
var (
|
||||||
netFee int64
|
netFee int64
|
||||||
size int
|
size int
|
||||||
)
|
)
|
||||||
if vm.IsSignatureContract(script) {
|
if vm.IsSignatureContract(script) {
|
||||||
size += 67 + io.GetVarSize(script)
|
size += 67 + io.GetVarSize(script)
|
||||||
netFee += Opcode(opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + crypto.ECDSAVerifyPrice
|
netFee += Opcode(base, opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + base*crypto.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(m) + calculateMultisig(n)
|
netFee += calculateMultisig(base, m) + calculateMultisig(base, n)
|
||||||
netFee += Opcode(opcode.PUSHNULL) + crypto.ECDSAVerifyPrice*int64(n)
|
netFee += Opcode(base, opcode.PUSHNULL) + base*crypto.ECDSAVerifyPrice*int64(n)
|
||||||
} else {
|
} else {
|
||||||
// We can support more contract types in the future.
|
// We can support more contract types in the future.
|
||||||
}
|
}
|
||||||
return netFee, size
|
return netFee, size
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateMultisig(n int) int64 {
|
func calculateMultisig(base int64, n int) int64 {
|
||||||
result := Opcode(opcode.PUSHDATA1) * int64(n)
|
result := Opcode(base, opcode.PUSHDATA1) * int64(n)
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
emit.Int(bw.BinWriter, int64(n))
|
emit.Int(bw.BinWriter, int64(n))
|
||||||
// it's a hack because prices of small PUSH* opcodes are equal
|
// it's a hack because coefficients of small PUSH* opcodes are equal
|
||||||
result += Opcode(opcode.Opcode(bw.Bytes()[0]))
|
result += Opcode(base, opcode.Opcode(bw.Bytes()[0]))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,199 +1,201 @@
|
||||||
package fee
|
package fee
|
||||||
|
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
)
|
||||||
|
|
||||||
// Opcode returns the deployment prices of specified opcodes.
|
// Opcode returns the deployment coefficients of specified opcodes.
|
||||||
func Opcode(opcodes ...opcode.Opcode) int64 {
|
func Opcode(base int64, opcodes ...opcode.Opcode) int64 {
|
||||||
var result int64
|
var result int64
|
||||||
for _, op := range opcodes {
|
for _, op := range opcodes {
|
||||||
result += prices[op]
|
result += coefficients[op]
|
||||||
}
|
}
|
||||||
return result
|
return result * base
|
||||||
}
|
}
|
||||||
|
|
||||||
var prices = map[opcode.Opcode]int64{
|
var coefficients = map[opcode.Opcode]int64{
|
||||||
opcode.PUSHINT8: 30,
|
opcode.PUSHINT8: 1 << 0,
|
||||||
opcode.PUSHINT32: 30,
|
opcode.PUSHINT16: 1 << 0,
|
||||||
opcode.PUSHINT64: 30,
|
opcode.PUSHINT32: 1 << 0,
|
||||||
opcode.PUSHINT16: 30,
|
opcode.PUSHINT64: 1 << 0,
|
||||||
opcode.PUSHINT128: 120,
|
opcode.PUSHINT128: 1 << 2,
|
||||||
opcode.PUSHINT256: 120,
|
opcode.PUSHINT256: 1 << 2,
|
||||||
opcode.PUSHA: 120,
|
opcode.PUSHA: 1 << 2,
|
||||||
opcode.PUSHNULL: 30,
|
opcode.PUSHNULL: 1 << 0,
|
||||||
opcode.PUSHDATA1: 180,
|
opcode.PUSHDATA1: 1 << 3,
|
||||||
opcode.PUSHDATA2: 13000,
|
opcode.PUSHDATA2: 1 << 9,
|
||||||
opcode.PUSHDATA4: 110000,
|
opcode.PUSHDATA4: 1 << 12,
|
||||||
opcode.PUSHM1: 30,
|
opcode.PUSHM1: 1 << 0,
|
||||||
opcode.PUSH0: 30,
|
opcode.PUSH0: 1 << 0,
|
||||||
opcode.PUSH1: 30,
|
opcode.PUSH1: 1 << 0,
|
||||||
opcode.PUSH2: 30,
|
opcode.PUSH2: 1 << 0,
|
||||||
opcode.PUSH3: 30,
|
opcode.PUSH3: 1 << 0,
|
||||||
opcode.PUSH4: 30,
|
opcode.PUSH4: 1 << 0,
|
||||||
opcode.PUSH5: 30,
|
opcode.PUSH5: 1 << 0,
|
||||||
opcode.PUSH6: 30,
|
opcode.PUSH6: 1 << 0,
|
||||||
opcode.PUSH7: 30,
|
opcode.PUSH7: 1 << 0,
|
||||||
opcode.PUSH8: 30,
|
opcode.PUSH8: 1 << 0,
|
||||||
opcode.PUSH9: 30,
|
opcode.PUSH9: 1 << 0,
|
||||||
opcode.PUSH10: 30,
|
opcode.PUSH10: 1 << 0,
|
||||||
opcode.PUSH11: 30,
|
opcode.PUSH11: 1 << 0,
|
||||||
opcode.PUSH12: 30,
|
opcode.PUSH12: 1 << 0,
|
||||||
opcode.PUSH13: 30,
|
opcode.PUSH13: 1 << 0,
|
||||||
opcode.PUSH14: 30,
|
opcode.PUSH14: 1 << 0,
|
||||||
opcode.PUSH15: 30,
|
opcode.PUSH15: 1 << 0,
|
||||||
opcode.PUSH16: 30,
|
opcode.PUSH16: 1 << 0,
|
||||||
opcode.NOP: 30,
|
opcode.NOP: 1 << 0,
|
||||||
opcode.JMP: 70,
|
opcode.JMP: 1 << 1,
|
||||||
opcode.JMPL: 70,
|
opcode.JMPL: 1 << 1,
|
||||||
opcode.JMPIF: 70,
|
opcode.JMPIF: 1 << 1,
|
||||||
opcode.JMPIFL: 70,
|
opcode.JMPIFL: 1 << 1,
|
||||||
opcode.JMPIFNOT: 70,
|
opcode.JMPIFNOT: 1 << 1,
|
||||||
opcode.JMPIFNOTL: 70,
|
opcode.JMPIFNOTL: 1 << 1,
|
||||||
opcode.JMPEQ: 70,
|
opcode.JMPEQ: 1 << 1,
|
||||||
opcode.JMPEQL: 70,
|
opcode.JMPEQL: 1 << 1,
|
||||||
opcode.JMPNE: 70,
|
opcode.JMPNE: 1 << 1,
|
||||||
opcode.JMPNEL: 70,
|
opcode.JMPNEL: 1 << 1,
|
||||||
opcode.JMPGT: 70,
|
opcode.JMPGT: 1 << 1,
|
||||||
opcode.JMPGTL: 70,
|
opcode.JMPGTL: 1 << 1,
|
||||||
opcode.JMPGE: 70,
|
opcode.JMPGE: 1 << 1,
|
||||||
opcode.JMPGEL: 70,
|
opcode.JMPGEL: 1 << 1,
|
||||||
opcode.JMPLT: 70,
|
opcode.JMPLT: 1 << 1,
|
||||||
opcode.JMPLTL: 70,
|
opcode.JMPLTL: 1 << 1,
|
||||||
opcode.JMPLE: 70,
|
opcode.JMPLE: 1 << 1,
|
||||||
opcode.JMPLEL: 70,
|
opcode.JMPLEL: 1 << 1,
|
||||||
opcode.CALL: 22000,
|
opcode.CALL: 1 << 9,
|
||||||
opcode.CALLL: 22000,
|
opcode.CALLL: 1 << 9,
|
||||||
opcode.CALLA: 22000,
|
opcode.CALLA: 1 << 9,
|
||||||
opcode.ABORT: 30,
|
opcode.ABORT: 0,
|
||||||
opcode.ASSERT: 30,
|
opcode.ASSERT: 1 << 0,
|
||||||
opcode.THROW: 22000,
|
opcode.THROW: 1 << 9,
|
||||||
opcode.TRY: 100,
|
opcode.TRY: 1 << 2,
|
||||||
opcode.TRYL: 100,
|
opcode.TRYL: 1 << 2,
|
||||||
opcode.ENDTRY: 100,
|
opcode.ENDTRY: 1 << 2,
|
||||||
opcode.ENDTRYL: 100,
|
opcode.ENDTRYL: 1 << 2,
|
||||||
opcode.ENDFINALLY: 100,
|
opcode.ENDFINALLY: 1 << 2,
|
||||||
opcode.RET: 0,
|
opcode.RET: 0,
|
||||||
opcode.SYSCALL: 0,
|
opcode.SYSCALL: 0,
|
||||||
opcode.DEPTH: 60,
|
opcode.DEPTH: 1 << 1,
|
||||||
opcode.DROP: 60,
|
opcode.DROP: 1 << 1,
|
||||||
opcode.NIP: 60,
|
opcode.NIP: 1 << 1,
|
||||||
opcode.XDROP: 400,
|
opcode.XDROP: 1 << 4,
|
||||||
opcode.CLEAR: 400,
|
opcode.CLEAR: 1 << 4,
|
||||||
opcode.DUP: 60,
|
opcode.DUP: 1 << 1,
|
||||||
opcode.OVER: 60,
|
opcode.OVER: 1 << 1,
|
||||||
opcode.PICK: 60,
|
opcode.PICK: 1 << 1,
|
||||||
opcode.TUCK: 60,
|
opcode.TUCK: 1 << 1,
|
||||||
opcode.SWAP: 60,
|
opcode.SWAP: 1 << 1,
|
||||||
opcode.ROT: 60,
|
opcode.ROT: 1 << 1,
|
||||||
opcode.ROLL: 400,
|
opcode.ROLL: 1 << 4,
|
||||||
opcode.REVERSE3: 60,
|
opcode.REVERSE3: 1 << 1,
|
||||||
opcode.REVERSE4: 60,
|
opcode.REVERSE4: 1 << 1,
|
||||||
opcode.REVERSEN: 400,
|
opcode.REVERSEN: 1 << 4,
|
||||||
opcode.INITSSLOT: 400,
|
opcode.INITSSLOT: 1 << 4,
|
||||||
opcode.INITSLOT: 1600,
|
opcode.INITSLOT: 1 << 6,
|
||||||
opcode.LDSFLD0: 60,
|
opcode.LDSFLD0: 1 << 1,
|
||||||
opcode.LDSFLD1: 60,
|
opcode.LDSFLD1: 1 << 1,
|
||||||
opcode.LDSFLD2: 60,
|
opcode.LDSFLD2: 1 << 1,
|
||||||
opcode.LDSFLD3: 60,
|
opcode.LDSFLD3: 1 << 1,
|
||||||
opcode.LDSFLD4: 60,
|
opcode.LDSFLD4: 1 << 1,
|
||||||
opcode.LDSFLD5: 60,
|
opcode.LDSFLD5: 1 << 1,
|
||||||
opcode.LDSFLD6: 60,
|
opcode.LDSFLD6: 1 << 1,
|
||||||
opcode.LDSFLD: 60,
|
opcode.LDSFLD: 1 << 1,
|
||||||
opcode.STSFLD0: 60,
|
opcode.STSFLD0: 1 << 1,
|
||||||
opcode.STSFLD1: 60,
|
opcode.STSFLD1: 1 << 1,
|
||||||
opcode.STSFLD2: 60,
|
opcode.STSFLD2: 1 << 1,
|
||||||
opcode.STSFLD3: 60,
|
opcode.STSFLD3: 1 << 1,
|
||||||
opcode.STSFLD4: 60,
|
opcode.STSFLD4: 1 << 1,
|
||||||
opcode.STSFLD5: 60,
|
opcode.STSFLD5: 1 << 1,
|
||||||
opcode.STSFLD6: 60,
|
opcode.STSFLD6: 1 << 1,
|
||||||
opcode.STSFLD: 60,
|
opcode.STSFLD: 1 << 1,
|
||||||
opcode.LDLOC0: 60,
|
opcode.LDLOC0: 1 << 1,
|
||||||
opcode.LDLOC1: 60,
|
opcode.LDLOC1: 1 << 1,
|
||||||
opcode.LDLOC2: 60,
|
opcode.LDLOC2: 1 << 1,
|
||||||
opcode.LDLOC3: 60,
|
opcode.LDLOC3: 1 << 1,
|
||||||
opcode.LDLOC4: 60,
|
opcode.LDLOC4: 1 << 1,
|
||||||
opcode.LDLOC5: 60,
|
opcode.LDLOC5: 1 << 1,
|
||||||
opcode.LDLOC6: 60,
|
opcode.LDLOC6: 1 << 1,
|
||||||
opcode.LDLOC: 60,
|
opcode.LDLOC: 1 << 1,
|
||||||
opcode.STLOC0: 60,
|
opcode.STLOC0: 1 << 1,
|
||||||
opcode.STLOC1: 60,
|
opcode.STLOC1: 1 << 1,
|
||||||
opcode.STLOC2: 60,
|
opcode.STLOC2: 1 << 1,
|
||||||
opcode.STLOC3: 60,
|
opcode.STLOC3: 1 << 1,
|
||||||
opcode.STLOC4: 60,
|
opcode.STLOC4: 1 << 1,
|
||||||
opcode.STLOC5: 60,
|
opcode.STLOC5: 1 << 1,
|
||||||
opcode.STLOC6: 60,
|
opcode.STLOC6: 1 << 1,
|
||||||
opcode.STLOC: 60,
|
opcode.STLOC: 1 << 1,
|
||||||
opcode.LDARG0: 60,
|
opcode.LDARG0: 1 << 1,
|
||||||
opcode.LDARG1: 60,
|
opcode.LDARG1: 1 << 1,
|
||||||
opcode.LDARG2: 60,
|
opcode.LDARG2: 1 << 1,
|
||||||
opcode.LDARG3: 60,
|
opcode.LDARG3: 1 << 1,
|
||||||
opcode.LDARG4: 60,
|
opcode.LDARG4: 1 << 1,
|
||||||
opcode.LDARG5: 60,
|
opcode.LDARG5: 1 << 1,
|
||||||
opcode.LDARG6: 60,
|
opcode.LDARG6: 1 << 1,
|
||||||
opcode.LDARG: 60,
|
opcode.LDARG: 1 << 1,
|
||||||
opcode.STARG0: 60,
|
opcode.STARG0: 1 << 1,
|
||||||
opcode.STARG1: 60,
|
opcode.STARG1: 1 << 1,
|
||||||
opcode.STARG2: 60,
|
opcode.STARG2: 1 << 1,
|
||||||
opcode.STARG3: 60,
|
opcode.STARG3: 1 << 1,
|
||||||
opcode.STARG4: 60,
|
opcode.STARG4: 1 << 1,
|
||||||
opcode.STARG5: 60,
|
opcode.STARG5: 1 << 1,
|
||||||
opcode.STARG6: 60,
|
opcode.STARG6: 1 << 1,
|
||||||
opcode.STARG: 60,
|
opcode.STARG: 1 << 1,
|
||||||
opcode.NEWBUFFER: 80000,
|
opcode.NEWBUFFER: 1 << 8,
|
||||||
opcode.MEMCPY: 80000,
|
opcode.MEMCPY: 1 << 11,
|
||||||
opcode.CAT: 80000,
|
opcode.CAT: 1 << 11,
|
||||||
opcode.SUBSTR: 80000,
|
opcode.SUBSTR: 1 << 11,
|
||||||
opcode.LEFT: 80000,
|
opcode.LEFT: 1 << 11,
|
||||||
opcode.RIGHT: 80000,
|
opcode.RIGHT: 1 << 11,
|
||||||
opcode.INVERT: 100,
|
opcode.INVERT: 1 << 2,
|
||||||
opcode.AND: 200,
|
opcode.AND: 1 << 3,
|
||||||
opcode.OR: 200,
|
opcode.OR: 1 << 3,
|
||||||
opcode.XOR: 200,
|
opcode.XOR: 1 << 3,
|
||||||
opcode.EQUAL: 1000,
|
opcode.EQUAL: 1 << 5,
|
||||||
opcode.NOTEQUAL: 1000,
|
opcode.NOTEQUAL: 1 << 5,
|
||||||
opcode.SIGN: 100,
|
opcode.SIGN: 1 << 2,
|
||||||
opcode.ABS: 100,
|
opcode.ABS: 1 << 2,
|
||||||
opcode.NEGATE: 100,
|
opcode.NEGATE: 1 << 2,
|
||||||
opcode.INC: 100,
|
opcode.INC: 1 << 2,
|
||||||
opcode.DEC: 100,
|
opcode.DEC: 1 << 2,
|
||||||
opcode.ADD: 200,
|
opcode.ADD: 1 << 3,
|
||||||
opcode.SUB: 200,
|
opcode.SUB: 1 << 3,
|
||||||
opcode.MUL: 300,
|
opcode.MUL: 1 << 3,
|
||||||
opcode.DIV: 300,
|
opcode.DIV: 1 << 3,
|
||||||
opcode.MOD: 300,
|
opcode.MOD: 1 << 3,
|
||||||
opcode.SHL: 300,
|
opcode.SHL: 1 << 3,
|
||||||
opcode.SHR: 300,
|
opcode.SHR: 1 << 3,
|
||||||
opcode.NOT: 100,
|
opcode.NOT: 1 << 2,
|
||||||
opcode.BOOLAND: 200,
|
opcode.BOOLAND: 1 << 3,
|
||||||
opcode.BOOLOR: 200,
|
opcode.BOOLOR: 1 << 3,
|
||||||
opcode.NZ: 100,
|
opcode.NZ: 1 << 2,
|
||||||
opcode.NUMEQUAL: 200,
|
opcode.NUMEQUAL: 1 << 3,
|
||||||
opcode.NUMNOTEQUAL: 200,
|
opcode.NUMNOTEQUAL: 1 << 3,
|
||||||
opcode.LT: 200,
|
opcode.LT: 1 << 3,
|
||||||
opcode.LTE: 200,
|
opcode.LTE: 1 << 3,
|
||||||
opcode.GT: 200,
|
opcode.GT: 1 << 3,
|
||||||
opcode.GTE: 200,
|
opcode.GTE: 1 << 3,
|
||||||
opcode.MIN: 200,
|
opcode.MIN: 1 << 3,
|
||||||
opcode.MAX: 200,
|
opcode.MAX: 1 << 3,
|
||||||
opcode.WITHIN: 200,
|
opcode.WITHIN: 1 << 3,
|
||||||
opcode.PACK: 15000,
|
opcode.PACK: 1 << 9,
|
||||||
opcode.UNPACK: 15000,
|
opcode.UNPACK: 1 << 9,
|
||||||
opcode.NEWARRAY0: 400,
|
opcode.NEWARRAY0: 1 << 4,
|
||||||
opcode.NEWARRAY: 15000,
|
opcode.NEWARRAY: 1 << 9,
|
||||||
opcode.NEWARRAYT: 15000,
|
opcode.NEWARRAYT: 1 << 9,
|
||||||
opcode.NEWSTRUCT0: 400,
|
opcode.NEWSTRUCT0: 1 << 4,
|
||||||
opcode.NEWSTRUCT: 15000,
|
opcode.NEWSTRUCT: 1 << 9,
|
||||||
opcode.NEWMAP: 200,
|
opcode.NEWMAP: 1 << 3,
|
||||||
opcode.SIZE: 150,
|
opcode.SIZE: 1 << 2,
|
||||||
opcode.HASKEY: 270000,
|
opcode.HASKEY: 1 << 6,
|
||||||
opcode.KEYS: 500,
|
opcode.KEYS: 1 << 4,
|
||||||
opcode.VALUES: 270000,
|
opcode.VALUES: 1 << 13,
|
||||||
opcode.PICKITEM: 270000,
|
opcode.PICKITEM: 1 << 6,
|
||||||
opcode.APPEND: 270000,
|
opcode.APPEND: 1 << 13,
|
||||||
opcode.SETITEM: 270000,
|
opcode.SETITEM: 1 << 13,
|
||||||
opcode.REVERSEITEMS: 270000,
|
opcode.REVERSEITEMS: 1 << 13,
|
||||||
opcode.REMOVE: 500,
|
opcode.REMOVE: 1 << 4,
|
||||||
opcode.CLEARITEMS: 400,
|
opcode.CLEARITEMS: 1 << 4,
|
||||||
opcode.ISNULL: 60,
|
opcode.ISNULL: 1 << 1,
|
||||||
opcode.ISTYPE: 60,
|
opcode.ISTYPE: 1 << 1,
|
||||||
opcode.CONVERT: 80000,
|
opcode.CONVERT: 1 << 11,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// getPrice returns a price for executing op with the provided parameter.
|
// getPrice returns a price for executing op with the provided parameter.
|
||||||
func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) int64 {
|
func (bc *Blockchain) getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) int64 {
|
||||||
return fee.Opcode(op)
|
return fee.Opcode(bc.GetBaseExecFee(), op)
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,13 +398,13 @@ func addSigners(txs ...*transaction.Transaction) {
|
||||||
|
|
||||||
func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.Account) error {
|
func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.Account) error {
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(sender.Contract.Script)
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), sender.Contract.Script)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
size += sizeDelta
|
size += sizeDelta
|
||||||
for _, cosigner := range tx.Signers {
|
for _, cosigner := range tx.Signers {
|
||||||
contract := bc.GetContractState(cosigner.Account)
|
contract := bc.GetContractState(cosigner.Account)
|
||||||
if contract != nil {
|
if contract != nil {
|
||||||
netFee, sizeDelta = fee.Calculate(contract.Script)
|
netFee, sizeDelta = fee.Calculate(bc.GetBaseExecFee(), contract.Script)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
size += sizeDelta
|
size += sizeDelta
|
||||||
}
|
}
|
||||||
|
@ -413,7 +413,8 @@ func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.A
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
func prepareContractMethodInvoke(chain *Blockchain, sysfee int64,
|
||||||
|
hash util.Uint160, method string, args ...interface{}) (*transaction.Transaction, error) {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...)
|
emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...)
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
|
@ -427,17 +428,37 @@ func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, me
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b := chain.newBlock(tx)
|
return tx, nil
|
||||||
err = chain.AddBlock(b)
|
}
|
||||||
|
|
||||||
|
func persistBlock(chain *Blockchain, txs ...*transaction.Transaction) ([]*state.AppExecResult, error) {
|
||||||
|
b := chain.newBlock(txs...)
|
||||||
|
err := chain.AddBlock(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aers := make([]*state.AppExecResult, len(txs))
|
||||||
|
for i, tx := range txs {
|
||||||
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res[0], nil
|
aers[i] = &res[0]
|
||||||
|
}
|
||||||
|
return aers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
||||||
|
tx, err := prepareContractMethodInvoke(chain, sysfee, hash, method, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aers, err := persistBlock(chain, tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return aers[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeContractMethodBy(t *testing.T, chain *Blockchain, signer *wallet.Account, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
func invokeContractMethodBy(t *testing.T, chain *Blockchain, signer *wallet.Account, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
||||||
|
|
|
@ -20,6 +20,11 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultBaseExecFee specifies default multiplier for opcode and syscall prices.
|
||||||
|
DefaultBaseExecFee = 30
|
||||||
|
)
|
||||||
|
|
||||||
// Context represents context in which interops are executed.
|
// Context represents context in which interops are executed.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Chain blockchainer.Blockchainer
|
Chain blockchainer.Blockchainer
|
||||||
|
@ -155,6 +160,14 @@ func (ic *Context) GetFunction(id uint32) *Function {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BaseExecFee represents factor to multiply syscall prices with.
|
||||||
|
func (ic *Context) BaseExecFee() int64 {
|
||||||
|
if ic.Chain == nil {
|
||||||
|
return DefaultBaseExecFee
|
||||||
|
}
|
||||||
|
return ic.Chain.GetPolicer().GetBaseExecFee()
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -165,7 +178,7 @@ func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error {
|
||||||
if !cf.Has(f.RequiredFlags) {
|
if !cf.Has(f.RequiredFlags) {
|
||||||
return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags)
|
return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags)
|
||||||
}
|
}
|
||||||
if !ic.VM.AddGas(f.Price) {
|
if !ic.VM.AddGas(f.Price * ic.BaseExecFee()) {
|
||||||
return errors.New("insufficient amount of gas")
|
return errors.New("insufficient amount of gas")
|
||||||
}
|
}
|
||||||
return f.Func(ic)
|
return f.Func(ic)
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ECDSAVerifyPrice is a gas price of a single verification.
|
// ECDSAVerifyPrice is a gas price of a single verification.
|
||||||
const ECDSAVerifyPrice = 1000000
|
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 {
|
||||||
|
@ -69,7 +69,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(ECDSAVerifyPrice * int64(len(pkeys))) {
|
if !ic.VM.AddGas(ic.BaseExecFee() * 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()
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
"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/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -201,6 +200,7 @@ func storageDelete(ic *interop.Context) error {
|
||||||
if stc.ReadOnly {
|
if stc.ReadOnly {
|
||||||
return errors.New("StorageContext is read only")
|
return errors.New("StorageContext is read only")
|
||||||
}
|
}
|
||||||
|
ic.VM.AddGas(ic.Chain.GetPolicer().GetStoragePrice())
|
||||||
key := ic.VM.Estack().Pop().Bytes()
|
key := ic.VM.Estack().Pop().Bytes()
|
||||||
si := ic.DAO.GetStorageItem(stc.ID, key)
|
si := ic.DAO.GetStorageItem(stc.ID, key)
|
||||||
if si != nil && si.IsConst {
|
if si != nil && si.IsConst {
|
||||||
|
@ -276,7 +276,7 @@ func putWithContextAndFlags(ic *interop.Context, stc *StorageContext, key []byte
|
||||||
sizeInc = (len(si.Value)-1)/4 + 1 + len(value) - len(si.Value)
|
sizeInc = (len(si.Value)-1)/4 + 1 + len(value) - len(si.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ic.VM.AddGas(int64(sizeInc) * native.StoragePrice) {
|
if !ic.VM.AddGas(int64(sizeInc) * ic.Chain.GetPolicer().GetStoragePrice()) {
|
||||||
return errGasLimitExceeded
|
return errGasLimitExceeded
|
||||||
}
|
}
|
||||||
si.Value = value
|
si.Value = value
|
||||||
|
|
|
@ -33,80 +33,80 @@ func SpawnVM(ic *interop.Context) *vm.VM {
|
||||||
|
|
||||||
// All lists are sorted, keep 'em this way, please.
|
// All lists are sorted, keep 'em this way, please.
|
||||||
var systemInterops = []interop.Function{
|
var systemInterops = []interop.Function{
|
||||||
{Name: interopnames.SystemBinaryAtoi, Func: binary.Atoi, Price: 100000, ParamCount: 2},
|
{Name: interopnames.SystemBinaryAtoi, Func: binary.Atoi, Price: 1 << 12, ParamCount: 2},
|
||||||
{Name: interopnames.SystemBinaryBase58Decode, Func: binary.DecodeBase58, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemBinaryBase58Decode, Func: binary.DecodeBase58, Price: 1 << 12, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBinaryBase58Encode, Func: binary.EncodeBase58, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemBinaryBase58Encode, Func: binary.EncodeBase58, Price: 1 << 12, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBinaryBase64Decode, Func: binary.DecodeBase64, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemBinaryBase64Decode, Func: binary.DecodeBase64, Price: 1 << 12, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBinaryBase64Encode, Func: binary.EncodeBase64, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemBinaryBase64Encode, Func: binary.EncodeBase64, Price: 1 << 12, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBinaryDeserialize, Func: binary.Deserialize, Price: 500000, ParamCount: 1},
|
{Name: interopnames.SystemBinaryDeserialize, Func: binary.Deserialize, Price: 1 << 14, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBinaryItoa, Func: binary.Itoa, Price: 100000, ParamCount: 2},
|
{Name: interopnames.SystemBinaryItoa, Func: binary.Itoa, Price: 1 << 12, ParamCount: 2},
|
||||||
{Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 1 << 12, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 2500000,
|
{Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 1 << 16,
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBlockchainGetHeight, Func: bcGetHeight, Price: 400,
|
{Name: interopnames.SystemBlockchainGetHeight, Func: bcGetHeight, Price: 1 << 4,
|
||||||
RequiredFlags: smartcontract.ReadStates},
|
RequiredFlags: smartcontract.ReadStates},
|
||||||
{Name: interopnames.SystemBlockchainGetTransaction, Func: bcGetTransaction, Price: 1000000,
|
{Name: interopnames.SystemBlockchainGetTransaction, Func: bcGetTransaction, Price: 1 << 15,
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBlockchainGetTransactionFromBlock, Func: bcGetTransactionFromBlock, Price: 1000000,
|
{Name: interopnames.SystemBlockchainGetTransactionFromBlock, Func: bcGetTransactionFromBlock, Price: 1 << 15,
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 2},
|
RequiredFlags: smartcontract.ReadStates, ParamCount: 2},
|
||||||
{Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1000000,
|
{Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1 << 15,
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
||||||
{Name: interopnames.SystemCallbackCreate, Func: callback.Create, Price: 400, ParamCount: 3, DisallowCallback: true},
|
{Name: interopnames.SystemCallbackCreate, Func: callback.Create, Price: 1 << 4, ParamCount: 3, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemCallbackCreateFromMethod, Func: callback.CreateFromMethod, Price: 1000000, RequiredFlags: smartcontract.ReadStates, ParamCount: 2, DisallowCallback: true},
|
{Name: interopnames.SystemCallbackCreateFromMethod, Func: callback.CreateFromMethod, Price: 1 << 15, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemCallbackCreateFromSyscall, Func: callback.CreateFromSyscall, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemCallbackCreateFromSyscall, Func: callback.CreateFromSyscall, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemCallbackInvoke, Func: callback.Invoke, Price: 1000000, RequiredFlags: smartcontract.AllowCall, ParamCount: 2, DisallowCallback: true},
|
{Name: interopnames.SystemCallbackInvoke, Func: callback.Invoke, Price: 1 << 15, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1000000,
|
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15,
|
||||||
RequiredFlags: smartcontract.AllowCall, ParamCount: 3, DisallowCallback: true},
|
RequiredFlags: smartcontract.AllowCall, ParamCount: 3, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractCallEx, Func: contract.CallEx, Price: 1000000,
|
{Name: interopnames.SystemContractCallEx, Func: contract.CallEx, Price: 1 << 15,
|
||||||
RequiredFlags: smartcontract.AllowCall, ParamCount: 4, DisallowCallback: true},
|
RequiredFlags: smartcontract.AllowCall, ParamCount: 4, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 10000, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 1 << 8, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 30000, RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
{Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 1 << 10, RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
||||||
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 30000, DisallowCallback: true},
|
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
{Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
{Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorConcat, Func: enumerator.Concat, Price: 400, ParamCount: 2, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorConcat, Func: enumerator.Concat, Price: 1 << 4, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1000000, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1 << 15, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorValue, Func: enumerator.Value, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorValue, Func: enumerator.Value, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemIteratorConcat, Func: iterator.Concat, Price: 400, ParamCount: 2, DisallowCallback: true},
|
{Name: interopnames.SystemIteratorConcat, Func: iterator.Concat, Price: 1 << 4, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemIteratorCreate, Func: iterator.Create, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemIteratorCreate, Func: iterator.Create, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemIteratorKey, Func: iterator.Key, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemIteratorKey, Func: iterator.Key, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemIteratorKeys, Func: iterator.Keys, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemIteratorKeys, Func: iterator.Keys, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemIteratorValues, Func: iterator.Values, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemIteratorValues, Func: iterator.Values, Price: 1 << 4, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemJSONDeserialize, Func: json.Deserialize, Price: 500000, ParamCount: 1},
|
{Name: interopnames.SystemJSONDeserialize, Func: json.Deserialize, Price: 1 << 14, ParamCount: 1},
|
||||||
{Name: interopnames.SystemJSONSerialize, Func: json.Serialize, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemJSONSerialize, Func: json.Serialize, Price: 1 << 12, ParamCount: 1},
|
||||||
{Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 30000,
|
{Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 1 << 10,
|
||||||
RequiredFlags: smartcontract.NoneFlag, ParamCount: 1},
|
RequiredFlags: smartcontract.NoneFlag, ParamCount: 1},
|
||||||
{Name: interopnames.SystemRuntimeGasLeft, Func: runtime.GasLeft, Price: 400},
|
{Name: interopnames.SystemRuntimeGasLeft, Func: runtime.GasLeft, Price: 1 << 4},
|
||||||
{Name: interopnames.SystemRuntimeGetCallingScriptHash, Func: runtime.GetCallingScriptHash, Price: 400},
|
{Name: interopnames.SystemRuntimeGetCallingScriptHash, Func: runtime.GetCallingScriptHash, Price: 1 << 4},
|
||||||
{Name: interopnames.SystemRuntimeGetEntryScriptHash, Func: runtime.GetEntryScriptHash, Price: 400},
|
{Name: interopnames.SystemRuntimeGetEntryScriptHash, Func: runtime.GetEntryScriptHash, Price: 1 << 4},
|
||||||
{Name: interopnames.SystemRuntimeGetExecutingScriptHash, Func: runtime.GetExecutingScriptHash, Price: 400},
|
{Name: interopnames.SystemRuntimeGetExecutingScriptHash, Func: runtime.GetExecutingScriptHash, Price: 1 << 4},
|
||||||
{Name: interopnames.SystemRuntimeGetInvocationCounter, Func: runtime.GetInvocationCounter, Price: 400},
|
{Name: interopnames.SystemRuntimeGetInvocationCounter, Func: runtime.GetInvocationCounter, Price: 1 << 4},
|
||||||
{Name: interopnames.SystemRuntimeGetNotifications, Func: runtime.GetNotifications, Price: 10000, ParamCount: 1},
|
{Name: interopnames.SystemRuntimeGetNotifications, Func: runtime.GetNotifications, Price: 1 << 8, ParamCount: 1},
|
||||||
{Name: interopnames.SystemRuntimeGetScriptContainer, Func: engineGetScriptContainer, Price: 250},
|
{Name: interopnames.SystemRuntimeGetScriptContainer, Func: engineGetScriptContainer, Price: 1 << 3},
|
||||||
{Name: interopnames.SystemRuntimeGetTime, Func: runtime.GetTime, Price: 250},
|
{Name: interopnames.SystemRuntimeGetTime, Func: runtime.GetTime, Price: 1 << 3, RequiredFlags: smartcontract.ReadStates},
|
||||||
{Name: interopnames.SystemRuntimeGetTrigger, Func: runtime.GetTrigger, Price: 250},
|
{Name: interopnames.SystemRuntimeGetTrigger, Func: runtime.GetTrigger, Price: 1 << 3},
|
||||||
{Name: interopnames.SystemRuntimeLog, Func: runtime.Log, Price: 1000000, RequiredFlags: smartcontract.AllowNotify,
|
{Name: interopnames.SystemRuntimeLog, Func: runtime.Log, Price: 1 << 15, RequiredFlags: smartcontract.AllowNotify,
|
||||||
ParamCount: 1, DisallowCallback: true},
|
ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1000000, RequiredFlags: smartcontract.AllowNotify,
|
{Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1 << 15, RequiredFlags: smartcontract.AllowNotify,
|
||||||
ParamCount: 2, DisallowCallback: true},
|
ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemRuntimePlatform, Func: runtime.Platform, Price: 250},
|
{Name: interopnames.SystemRuntimePlatform, Func: runtime.Platform, Price: 1 << 3},
|
||||||
{Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: native.StoragePrice,
|
{Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: 0,
|
||||||
RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true},
|
RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1000000, RequiredFlags: smartcontract.ReadStates,
|
{Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1 << 15, RequiredFlags: smartcontract.ReadStates,
|
||||||
ParamCount: 2, DisallowCallback: true},
|
ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStorageGet, Func: storageGet, Price: 1000000, RequiredFlags: smartcontract.ReadStates,
|
{Name: interopnames.SystemStorageGet, Func: storageGet, Price: 1 << 15, RequiredFlags: smartcontract.ReadStates,
|
||||||
ParamCount: 2, DisallowCallback: true},
|
ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStorageGetContext, Func: storageGetContext, Price: 400,
|
{Name: interopnames.SystemStorageGetContext, Func: storageGetContext, Price: 1 << 4,
|
||||||
RequiredFlags: smartcontract.ReadStates, DisallowCallback: true},
|
RequiredFlags: smartcontract.ReadStates, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStorageGetReadOnlyContext, Func: storageGetReadOnlyContext, Price: 400,
|
{Name: interopnames.SystemStorageGetReadOnlyContext, Func: storageGetReadOnlyContext, Price: 1 << 4,
|
||||||
RequiredFlags: smartcontract.ReadStates, DisallowCallback: true},
|
RequiredFlags: smartcontract.ReadStates, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStoragePut, Func: storagePut, Price: 0, RequiredFlags: smartcontract.WriteStates,
|
{Name: interopnames.SystemStoragePut, Func: storagePut, Price: 0, RequiredFlags: smartcontract.WriteStates,
|
||||||
ParamCount: 3, DisallowCallback: true}, // These don't have static price in C# code.
|
ParamCount: 3, DisallowCallback: true}, // These don't have static price in C# code.
|
||||||
{Name: interopnames.SystemStoragePutEx, Func: storagePutEx, Price: 0, RequiredFlags: smartcontract.WriteStates,
|
{Name: interopnames.SystemStoragePutEx, Func: storagePutEx, Price: 0, RequiredFlags: smartcontract.WriteStates,
|
||||||
ParamCount: 4, DisallowCallback: true},
|
ParamCount: 4, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStorageAsReadOnly, Func: storageContextAsReadOnly, Price: 400,
|
{Name: interopnames.SystemStorageAsReadOnly, Func: storageContextAsReadOnly, Price: 1 << 4,
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 1, DisallowCallback: true},
|
RequiredFlags: smartcontract.ReadStates, ParamCount: 1, DisallowCallback: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +117,8 @@ var neoInterops = []interop.Function{
|
||||||
Price: crypto.ECDSAVerifyPrice, ParamCount: 3},
|
Price: crypto.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: 1000000, ParamCount: 1},
|
{Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1 << 15, ParamCount: 1},
|
||||||
{Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1000000, ParamCount: 1},
|
{Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1 << 15, ParamCount: 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
// initIDinInteropsSlice initializes IDs from names in one given
|
// initIDinInteropsSlice initializes IDs from names in one given
|
||||||
|
|
|
@ -24,6 +24,10 @@ type FeerStub struct {
|
||||||
balance int64
|
balance int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *FeerStub) GetBaseExecFee() int64 {
|
||||||
|
return 30
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *FeerStub) FeePerByte() int64 {
|
func (fs *FeerStub) FeePerByte() int64 {
|
||||||
return fs.feePerByte
|
return fs.feePerByte
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ func Call(ic *interop.Context) error {
|
||||||
if !ic.VM.Context().GetCallFlags().Has(m.RequiredFlags) {
|
if !ic.VM.Context().GetCallFlags().Has(m.RequiredFlags) {
|
||||||
return fmt.Errorf("missing call flags for native %s `%s` operation call: %05b vs %05b", name, operation, ic.VM.Context().GetCallFlags(), m.RequiredFlags)
|
return fmt.Errorf("missing call flags for native %s `%s` operation call: %05b vs %05b", name, operation, ic.VM.Context().GetCallFlags(), m.RequiredFlags)
|
||||||
}
|
}
|
||||||
|
// Native contract prices are not multiplied by `BaseExecFee`.
|
||||||
if !ic.VM.AddGas(m.Price) {
|
if !ic.VM.AddGas(m.Price) {
|
||||||
return errors.New("gas limit exceeded")
|
return errors.New("gas limit exceeded")
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ func getLimitedSlice(arg stackitem.Item, max int) ([]byte, error) {
|
||||||
|
|
||||||
// getNefAndManifestFromItems converts input arguments into NEF and manifest
|
// getNefAndManifestFromItems converts input arguments into NEF and manifest
|
||||||
// adding appropriate deployment GAS price and sanitizing inputs.
|
// adding appropriate deployment GAS price and sanitizing inputs.
|
||||||
func getNefAndManifestFromItems(args []stackitem.Item, v *vm.VM) (*nef.File, *manifest.Manifest, error) {
|
func getNefAndManifestFromItems(ic *interop.Context, args []stackitem.Item) (*nef.File, *manifest.Manifest, error) {
|
||||||
nefBytes, err := getLimitedSlice(args[0], math.MaxInt32) // Upper limits are checked during NEF deserialization.
|
nefBytes, err := getLimitedSlice(args[0], math.MaxInt32) // Upper limits are checked during NEF deserialization.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
||||||
|
@ -129,7 +129,7 @@ func getNefAndManifestFromItems(args []stackitem.Item, v *vm.VM) (*nef.File, *ma
|
||||||
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !v.AddGas(int64(StoragePrice * (len(nefBytes) + len(manifestBytes)))) {
|
if !ic.VM.AddGas(ic.Chain.GetPolicer().GetStoragePrice() * int64(len(nefBytes)+len(manifestBytes))) {
|
||||||
return nil, nil, errGasLimitExceeded
|
return nil, nil, errGasLimitExceeded
|
||||||
}
|
}
|
||||||
var resManifest *manifest.Manifest
|
var resManifest *manifest.Manifest
|
||||||
|
@ -154,7 +154,7 @@ func getNefAndManifestFromItems(args []stackitem.Item, v *vm.VM) (*nef.File, *ma
|
||||||
// deploy is an implementation of public deploy method, it's run under
|
// deploy is an implementation of public deploy method, it's run under
|
||||||
// VM protections, so it's OK for it to panic instead of returning errors.
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
||||||
func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
neff, manif, err := getNefAndManifestFromItems(args, ic.VM)
|
neff, manif, err := getNefAndManifestFromItems(ic, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, mani
|
||||||
// update is an implementation of public update method, it's run under
|
// update is an implementation of public update method, it's run under
|
||||||
// VM protections, so it's OK for it to panic instead of returning errors.
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
||||||
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
neff, manif, err := getNefAndManifestFromItems(args, ic.VM)
|
neff, manif, err := getNefAndManifestFromItems(ic, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,18 @@ const (
|
||||||
|
|
||||||
defaultMaxBlockSize = 1024 * 256
|
defaultMaxBlockSize = 1024 * 256
|
||||||
defaultMaxTransactionsPerBlock = 512
|
defaultMaxTransactionsPerBlock = 512
|
||||||
|
defaultExecFeeFactor = interop.DefaultBaseExecFee
|
||||||
defaultFeePerByte = 1000
|
defaultFeePerByte = 1000
|
||||||
defaultMaxVerificationGas = 50000000
|
defaultMaxVerificationGas = 50000000
|
||||||
defaultMaxBlockSystemFee = 9000 * GASFactor
|
defaultMaxBlockSystemFee = 9000 * GASFactor
|
||||||
// minBlockSystemFee is the minimum allowed system fee per block.
|
// minBlockSystemFee is the minimum allowed system fee per block.
|
||||||
minBlockSystemFee = 4007600
|
minBlockSystemFee = 4007600
|
||||||
|
// maxExecFeeFactor is the maximum allowed execution fee factor.
|
||||||
|
maxExecFeeFactor = 1000
|
||||||
// maxFeePerByte is the maximum allowed fee per byte value.
|
// maxFeePerByte is the maximum allowed fee per byte value.
|
||||||
maxFeePerByte = 100_000_000
|
maxFeePerByte = 100_000_000
|
||||||
|
// maxStoragePrice is the maximum allowed price for a byte of storage.
|
||||||
|
maxStoragePrice = 10000000
|
||||||
|
|
||||||
// blockedAccountPrefix is a prefix used to store blocked account.
|
// blockedAccountPrefix is a prefix used to store blocked account.
|
||||||
blockedAccountPrefix = 15
|
blockedAccountPrefix = 15
|
||||||
|
@ -40,6 +45,8 @@ var (
|
||||||
// maxTransactionsPerBlockKey is a key used to store the maximum number of
|
// maxTransactionsPerBlockKey is a key used to store the maximum number of
|
||||||
// transactions allowed in block.
|
// transactions allowed in block.
|
||||||
maxTransactionsPerBlockKey = []byte{23}
|
maxTransactionsPerBlockKey = []byte{23}
|
||||||
|
// execFeeFactorKey is a key used to store execution fee factor.
|
||||||
|
execFeeFactorKey = []byte{18}
|
||||||
// feePerByteKey is a key used to store the minimum fee per byte for
|
// feePerByteKey is a key used to store the minimum fee per byte for
|
||||||
// transaction.
|
// transaction.
|
||||||
feePerByteKey = []byte{10}
|
feePerByteKey = []byte{10}
|
||||||
|
@ -47,6 +54,8 @@ var (
|
||||||
maxBlockSizeKey = []byte{12}
|
maxBlockSizeKey = []byte{12}
|
||||||
// maxBlockSystemFeeKey is a key used to store the maximum block system fee value.
|
// maxBlockSystemFeeKey is a key used to store the maximum block system fee value.
|
||||||
maxBlockSystemFeeKey = []byte{17}
|
maxBlockSystemFeeKey = []byte{17}
|
||||||
|
// storagePriceKey is a key used to store storage price.
|
||||||
|
storagePriceKey = []byte{19}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Policy represents Policy native contract.
|
// Policy represents Policy native contract.
|
||||||
|
@ -59,9 +68,11 @@ type Policy struct {
|
||||||
isValid bool
|
isValid bool
|
||||||
maxTransactionsPerBlock uint32
|
maxTransactionsPerBlock uint32
|
||||||
maxBlockSize uint32
|
maxBlockSize uint32
|
||||||
|
execFeeFactor uint32
|
||||||
feePerByte int64
|
feePerByte int64
|
||||||
maxBlockSystemFee int64
|
maxBlockSystemFee int64
|
||||||
maxVerificationGas int64
|
maxVerificationGas int64
|
||||||
|
storagePrice uint32
|
||||||
blockedAccounts []util.Uint160
|
blockedAccounts []util.Uint160
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +105,24 @@ func newPolicy() *Policy {
|
||||||
md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.ReadStates)
|
md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.ReadStates)
|
||||||
p.AddMethod(md, desc)
|
p.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("getExecFeeFactor", smartcontract.IntegerType)
|
||||||
|
md = newMethodAndPrice(p.getExecFeeFactor, 1000000, smartcontract.ReadStates)
|
||||||
|
p.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("setExecFeeFactor", smartcontract.BoolType,
|
||||||
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
||||||
|
md = newMethodAndPrice(p.setExecFeeFactor, 3000000, smartcontract.WriteStates)
|
||||||
|
p.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("getStoragePrice", smartcontract.IntegerType)
|
||||||
|
md = newMethodAndPrice(p.getStoragePrice, 1000000, smartcontract.ReadStates)
|
||||||
|
p.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("setStoragePrice", smartcontract.BoolType,
|
||||||
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
||||||
|
md = newMethodAndPrice(p.setStoragePrice, 3000000, smartcontract.WriteStates)
|
||||||
|
p.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("setMaxBlockSize", smartcontract.BoolType,
|
desc = newDescriptor("setMaxBlockSize", smartcontract.BoolType,
|
||||||
manifest.NewParameter("value", smartcontract.IntegerType))
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
||||||
md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.WriteStates)
|
md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.WriteStates)
|
||||||
|
@ -137,9 +166,11 @@ func (p *Policy) Initialize(ic *interop.Context) error {
|
||||||
p.isValid = true
|
p.isValid = true
|
||||||
p.maxTransactionsPerBlock = defaultMaxTransactionsPerBlock
|
p.maxTransactionsPerBlock = defaultMaxTransactionsPerBlock
|
||||||
p.maxBlockSize = defaultMaxBlockSize
|
p.maxBlockSize = defaultMaxBlockSize
|
||||||
|
p.execFeeFactor = defaultExecFeeFactor
|
||||||
p.feePerByte = defaultFeePerByte
|
p.feePerByte = defaultFeePerByte
|
||||||
p.maxBlockSystemFee = defaultMaxBlockSystemFee
|
p.maxBlockSystemFee = defaultMaxBlockSystemFee
|
||||||
p.maxVerificationGas = defaultMaxVerificationGas
|
p.maxVerificationGas = defaultMaxVerificationGas
|
||||||
|
p.storagePrice = StoragePrice
|
||||||
p.blockedAccounts = make([]util.Uint160, 0)
|
p.blockedAccounts = make([]util.Uint160, 0)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -160,9 +191,11 @@ func (p *Policy) PostPersist(ic *interop.Context) error {
|
||||||
|
|
||||||
p.maxTransactionsPerBlock = getUint32WithKey(p.ContractID, ic.DAO, maxTransactionsPerBlockKey, defaultMaxTransactionsPerBlock)
|
p.maxTransactionsPerBlock = getUint32WithKey(p.ContractID, ic.DAO, maxTransactionsPerBlockKey, defaultMaxTransactionsPerBlock)
|
||||||
p.maxBlockSize = getUint32WithKey(p.ContractID, ic.DAO, maxBlockSizeKey, defaultMaxBlockSize)
|
p.maxBlockSize = getUint32WithKey(p.ContractID, ic.DAO, maxBlockSizeKey, defaultMaxBlockSize)
|
||||||
|
p.execFeeFactor = getUint32WithKey(p.ContractID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
|
||||||
p.feePerByte = getInt64WithKey(p.ContractID, ic.DAO, feePerByteKey, defaultFeePerByte)
|
p.feePerByte = getInt64WithKey(p.ContractID, ic.DAO, feePerByteKey, defaultFeePerByte)
|
||||||
p.maxBlockSystemFee = getInt64WithKey(p.ContractID, ic.DAO, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
p.maxBlockSystemFee = getInt64WithKey(p.ContractID, ic.DAO, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
||||||
p.maxVerificationGas = defaultMaxVerificationGas
|
p.maxVerificationGas = defaultMaxVerificationGas
|
||||||
|
p.storagePrice = getUint32WithKey(p.ContractID, ic.DAO, storagePriceKey, StoragePrice)
|
||||||
|
|
||||||
p.blockedAccounts = make([]util.Uint160, 0)
|
p.blockedAccounts = make([]util.Uint160, 0)
|
||||||
siMap, err := ic.DAO.GetStorageItemsWithPrefix(p.ContractID, []byte{blockedAccountPrefix})
|
siMap, err := ic.DAO.GetStorageItemsWithPrefix(p.ContractID, []byte{blockedAccountPrefix})
|
||||||
|
@ -256,6 +289,41 @@ func (p *Policy) GetMaxBlockSystemFeeInternal(dao dao.DAO) int64 {
|
||||||
return getInt64WithKey(p.ContractID, dao, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
return getInt64WithKey(p.ContractID, dao, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Policy) getExecFeeFactor(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
|
return stackitem.NewBigInteger(big.NewInt(int64(p.GetExecFeeFactorInternal(ic.DAO))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExecFeeFactorInternal returns current execution fee factor.
|
||||||
|
func (p *Policy) GetExecFeeFactorInternal(d dao.DAO) int64 {
|
||||||
|
p.lock.RLock()
|
||||||
|
defer p.lock.RUnlock()
|
||||||
|
if p.isValid {
|
||||||
|
return int64(p.execFeeFactor)
|
||||||
|
}
|
||||||
|
return int64(getUint32WithKey(p.ContractID, d, execFeeFactorKey, defaultExecFeeFactor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Policy) setExecFeeFactor(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
value := toUint32(args[0])
|
||||||
|
if value <= 0 || maxExecFeeFactor < value {
|
||||||
|
panic(fmt.Errorf("ExecFeeFactor must be between 0 and %d", maxExecFeeFactor))
|
||||||
|
}
|
||||||
|
ok, err := checkValidators(ic)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if !ok {
|
||||||
|
return stackitem.NewBool(false)
|
||||||
|
}
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
err = setUint32WithKey(p.ContractID, ic.DAO, execFeeFactorKey, uint32(value))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
p.isValid = false
|
||||||
|
return stackitem.NewBool(true)
|
||||||
|
}
|
||||||
|
|
||||||
// isBlocked is Policy contract method and checks whether provided account is blocked.
|
// isBlocked is Policy contract method and checks whether provided account is blocked.
|
||||||
func (p *Policy) isBlocked(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (p *Policy) isBlocked(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
hash := toUint160(args[0])
|
hash := toUint160(args[0])
|
||||||
|
@ -280,6 +348,41 @@ func (p *Policy) IsBlockedInternal(dao dao.DAO, hash util.Uint160) bool {
|
||||||
return dao.GetStorageItem(p.ContractID, key) != nil
|
return dao.GetStorageItem(p.ContractID, key) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Policy) getStoragePrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
|
return stackitem.NewBigInteger(big.NewInt(p.GetStoragePriceInternal(ic.DAO)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStoragePriceInternal returns current execution fee factor.
|
||||||
|
func (p *Policy) GetStoragePriceInternal(d dao.DAO) int64 {
|
||||||
|
p.lock.RLock()
|
||||||
|
defer p.lock.RUnlock()
|
||||||
|
if p.isValid {
|
||||||
|
return int64(p.storagePrice)
|
||||||
|
}
|
||||||
|
return int64(getUint32WithKey(p.ContractID, d, storagePriceKey, StoragePrice))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
value := toUint32(args[0])
|
||||||
|
if value <= 0 || maxStoragePrice < value {
|
||||||
|
panic(fmt.Errorf("StoragePrice must be between 0 and %d", maxStoragePrice))
|
||||||
|
}
|
||||||
|
ok, err := checkValidators(ic)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if !ok {
|
||||||
|
return stackitem.NewBool(false)
|
||||||
|
}
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
err = setUint32WithKey(p.ContractID, ic.DAO, storagePriceKey, uint32(value))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
p.isValid = false
|
||||||
|
return stackitem.NewBool(true)
|
||||||
|
}
|
||||||
|
|
||||||
// setMaxTransactionsPerBlock is Policy contract method and sets the upper limit
|
// setMaxTransactionsPerBlock is Policy contract method and sets the upper limit
|
||||||
// of transactions per block.
|
// of transactions per block.
|
||||||
func (p *Policy) setMaxTransactionsPerBlock(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (p *Policy) setMaxTransactionsPerBlock(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
|
|
@ -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/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
@ -17,6 +18,7 @@ import (
|
||||||
"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"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -54,7 +56,7 @@ func (bc *Blockchain) registerNative(c interop.Contract) {
|
||||||
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
|
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
const testSumPrice = 1000000
|
const testSumPrice = 1 << 15 * interop.DefaultBaseExecFee // same as contract.Call
|
||||||
|
|
||||||
func newTestNative() *testNative {
|
func newTestNative() *testNative {
|
||||||
tn := &testNative{
|
tn := &testNative{
|
||||||
|
@ -176,8 +178,12 @@ func TestNativeContract_Invoke(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// System.Contract.Call + "sum" itself + opcodes for pushing arguments (PACK is 15000)
|
// System.Contract.Call + "sum" itself + opcodes for pushing arguments.
|
||||||
res, err := invokeContractMethod(chain, testSumPrice*2+18000, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
price := int64(testSumPrice * 2)
|
||||||
|
price += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8, opcode.PUSHDATA1)
|
||||||
|
price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL)
|
||||||
|
price += fee.Opcode(chain.GetBaseExecFee(), opcode.PACK)
|
||||||
|
res, err := invokeContractMethod(chain, price, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
checkResult(t, res, stackitem.Make(42))
|
checkResult(t, res, stackitem.Make(42))
|
||||||
require.NoError(t, chain.persist())
|
require.NoError(t, chain.persist())
|
||||||
|
@ -190,10 +196,9 @@ func TestNativeContract_Invoke(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enough for Call and other opcodes, but not enough for "sum" call.
|
// Enough for Call and other opcodes, but not enough for "sum" call.
|
||||||
res, err = invokeContractMethod(chain, testSumPrice*2+8000, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
res, err = invokeContractMethod(chain, price-1, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
checkFAULTState(t, res)
|
checkFAULTState(t, res)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNativeContract_InvokeInternal(t *testing.T) {
|
func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"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/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
@ -15,174 +14,129 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func testPolicyGetSet(t *testing.T, chain *Blockchain, name string, defaultValue, minValue, maxValue int64) {
|
||||||
|
policyHash := chain.contracts.Policy.Metadata().Hash
|
||||||
|
getName := "get" + name
|
||||||
|
setName := "set" + name
|
||||||
|
|
||||||
|
t.Run("set, not signed by committee", func(t *testing.T) {
|
||||||
|
signer, err := wallet.NewAccount()
|
||||||
|
require.NoError(t, err)
|
||||||
|
invokeRes, err := invokeContractMethodBy(t, chain, signer, policyHash, setName, minValue+1)
|
||||||
|
checkResult(t, invokeRes, stackitem.NewBool(false))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("get", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, getName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkResult(t, res, stackitem.Make(defaultValue))
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("set, zero fee", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, setName, minValue-1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
if maxValue != 0 {
|
||||||
|
t.Run("set, too big fee", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, setName, maxValue+1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("set, success", func(t *testing.T) {
|
||||||
|
// Set and get in the same block.
|
||||||
|
txSet, err := prepareContractMethodInvoke(chain, 100000000, policyHash, setName, defaultValue+1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
txGet1, err := prepareContractMethodInvoke(chain, 100000000, policyHash, getName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
aers, err := persistBlock(chain, txSet, txGet1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkResult(t, aers[0], stackitem.NewBool(true))
|
||||||
|
checkResult(t, aers[1], stackitem.Make(defaultValue+1))
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
|
||||||
|
// Get in the next block.
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, getName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkResult(t, res, stackitem.Make(defaultValue+1))
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestMaxTransactionsPerBlock(t *testing.T) {
|
func TestMaxTransactionsPerBlock(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
policyHash := chain.contracts.Policy.Metadata().Hash
|
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.T) {
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
n := chain.contracts.Policy.GetMaxTransactionsPerBlockInternal(chain.dao)
|
n := chain.contracts.Policy.GetMaxTransactionsPerBlockInternal(chain.dao)
|
||||||
require.Equal(t, 512, int(n))
|
require.Equal(t, 512, int(n))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("get, contract method", func(t *testing.T) {
|
testPolicyGetSet(t, chain, "MaxTransactionsPerBlock", 512, 0, block.MaxTransactionsPerBlock)
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "getMaxTransactionsPerBlock")
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(512)))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setMaxTransactionsPerBlock", bigint.ToBytes(big.NewInt(1024)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBool(true))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
n := chain.contracts.Policy.GetMaxTransactionsPerBlockInternal(chain.dao)
|
|
||||||
require.Equal(t, 1024, int(n))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, too big value", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setMaxTransactionsPerBlock", bigint.ToBytes(big.NewInt(block.MaxContentsPerBlock)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkFAULTState(t, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, not signed by committee", func(t *testing.T) {
|
|
||||||
signer, err := wallet.NewAccount()
|
|
||||||
require.NoError(t, err)
|
|
||||||
invokeRes, err := invokeContractMethodBy(t, chain, signer, policyHash, "setMaxTransactionsPerBlock", bigint.ToBytes(big.NewInt(1024)))
|
|
||||||
checkResult(t, invokeRes, stackitem.NewBool(false))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMaxBlockSize(t *testing.T) {
|
func TestMaxBlockSize(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
policyHash := chain.contracts.Policy.Metadata().Hash
|
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.T) {
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
n := chain.contracts.Policy.GetMaxBlockSizeInternal(chain.dao)
|
n := chain.contracts.Policy.GetMaxBlockSizeInternal(chain.dao)
|
||||||
require.Equal(t, 1024*256, int(n))
|
require.Equal(t, 1024*256, int(n))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("get, contract method", func(t *testing.T) {
|
testPolicyGetSet(t, chain, "MaxBlockSize", 1024*256, 0, payload.MaxSize)
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "getMaxBlockSize")
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(1024*256)))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setMaxBlockSize", bigint.ToBytes(big.NewInt(102400)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBool(true))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
res, err = invokeContractMethod(chain, 100000000, policyHash, "getMaxBlockSize")
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(102400)))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, too big value", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setMaxBlockSize", bigint.ToBytes(big.NewInt(payload.MaxSize+1)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkFAULTState(t, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, not signed by committee", func(t *testing.T) {
|
|
||||||
signer, err := wallet.NewAccount()
|
|
||||||
require.NoError(t, err)
|
|
||||||
invokeRes, err := invokeContractMethodBy(t, chain, signer, policyHash, "setMaxBlockSize", bigint.ToBytes(big.NewInt(102400)))
|
|
||||||
checkResult(t, invokeRes, stackitem.NewBool(false))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeePerByte(t *testing.T) {
|
func TestFeePerByte(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
policyHash := chain.contracts.Policy.Metadata().Hash
|
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.T) {
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao)
|
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao)
|
||||||
require.Equal(t, 1000, int(n))
|
require.Equal(t, 1000, int(n))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("get, contract method", func(t *testing.T) {
|
testPolicyGetSet(t, chain, "FeePerByte", 1000, 0, 100_000_000)
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "getFeePerByte")
|
}
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(1000)))
|
func TestExecFeeFactor(t *testing.T) {
|
||||||
require.NoError(t, chain.persist())
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
|
n := chain.contracts.Policy.GetExecFeeFactorInternal(chain.dao)
|
||||||
|
require.EqualValues(t, interop.DefaultBaseExecFee, n)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("set", func(t *testing.T) {
|
testPolicyGetSet(t, chain, "ExecFeeFactor", interop.DefaultBaseExecFee, 1, 1000)
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setFeePerByte", bigint.ToBytes(big.NewInt(1024)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBool(true))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao)
|
|
||||||
require.Equal(t, 1024, int(n))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, negative value", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setFeePerByte", bigint.ToBytes(big.NewInt(-1)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkFAULTState(t, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, too big value", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setFeePerByte", bigint.ToBytes(big.NewInt(100_000_000+1)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkFAULTState(t, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, not signed by committee", func(t *testing.T) {
|
|
||||||
signer, err := wallet.NewAccount()
|
|
||||||
require.NoError(t, err)
|
|
||||||
invokeRes, err := invokeContractMethodBy(t, chain, signer, policyHash, "setFeePerByte", bigint.ToBytes(big.NewInt(1024)))
|
|
||||||
checkResult(t, invokeRes, stackitem.NewBool(false))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockSystemFee(t *testing.T) {
|
func TestBlockSystemFee(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
policyHash := chain.contracts.Policy.Metadata().Hash
|
|
||||||
|
|
||||||
t.Run("get, internal method", func(t *testing.T) {
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
n := chain.contracts.Policy.GetMaxBlockSystemFeeInternal(chain.dao)
|
n := chain.contracts.Policy.GetMaxBlockSystemFeeInternal(chain.dao)
|
||||||
require.Equal(t, 9000*native.GASFactor, int(n))
|
require.Equal(t, 9000*native.GASFactor, int(n))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("get", func(t *testing.T) {
|
testPolicyGetSet(t, chain, "MaxBlockSystemFee", 9000*native.GASFactor, 4007600, 0)
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "getMaxBlockSystemFee")
|
}
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(9000*native.GASFactor)))
|
func TestStoragePrice(t *testing.T) {
|
||||||
require.NoError(t, chain.persist())
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
|
n := chain.contracts.Policy.GetStoragePriceInternal(chain.dao)
|
||||||
|
require.Equal(t, int64(native.StoragePrice), n)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("set, too low fee", func(t *testing.T) {
|
testPolicyGetSet(t, chain, "StoragePrice", native.StoragePrice, 1, 10000000)
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setMaxBlockSystemFee", bigint.ToBytes(big.NewInt(4007600)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkFAULTState(t, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, success", func(t *testing.T) {
|
|
||||||
res, err := invokeContractMethod(chain, 100000000, policyHash, "setMaxBlockSystemFee", bigint.ToBytes(big.NewInt(100000000)))
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBool(true))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
res, err = invokeContractMethod(chain, 100000000, policyHash, "getMaxBlockSystemFee")
|
|
||||||
require.NoError(t, err)
|
|
||||||
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(100000000)))
|
|
||||||
require.NoError(t, chain.persist())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set, not signed by committee", func(t *testing.T) {
|
|
||||||
signer, err := wallet.NewAccount()
|
|
||||||
require.NoError(t, err)
|
|
||||||
invokeRes, err := invokeContractMethodBy(t, chain, signer, policyHash, "setMaxBlockSystemFee", bigint.ToBytes(big.NewInt(100000000)))
|
|
||||||
checkResult(t, invokeRes, stackitem.NewBool(false))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockedAccounts(t *testing.T) {
|
func TestBlockedAccounts(t *testing.T) {
|
||||||
|
|
|
@ -14,7 +14,9 @@ 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/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
||||||
|
"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/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
||||||
|
@ -101,7 +103,12 @@ func (chain *testChain) GetNotaryBalance(acc util.Uint160) *big.Int {
|
||||||
func (chain *testChain) GetPolicer() blockchainer.Policer {
|
func (chain *testChain) GetPolicer() blockchainer.Policer {
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
func (chain *testChain) GetBaseExecFee() int64 {
|
||||||
|
return interop.DefaultBaseExecFee
|
||||||
|
}
|
||||||
|
func (chain *testChain) GetStoragePrice() int64 {
|
||||||
|
return native.StoragePrice
|
||||||
|
}
|
||||||
func (chain *testChain) GetMaxVerificationGAS() int64 {
|
func (chain *testChain) GetMaxVerificationGAS() int64 {
|
||||||
if chain.maxVerificationGAS != 0 {
|
if chain.maxVerificationGAS != 0 {
|
||||||
return chain.maxVerificationGAS
|
return chain.maxVerificationGAS
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/consensus"
|
"github.com/nspcc-dev/neo-go/pkg/consensus"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"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/network/capability"
|
"github.com/nspcc-dev/neo-go/pkg/network/capability"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||||
|
@ -798,6 +799,7 @@ func (f feerStub) FeePerByte() int64 { return 1 }
|
||||||
func (f feerStub) GetUtilityTokenBalance(util.Uint160) *big.Int { return big.NewInt(100000000) }
|
func (f feerStub) GetUtilityTokenBalance(util.Uint160) *big.Int { return big.NewInt(100000000) }
|
||||||
func (f feerStub) BlockHeight() uint32 { return f.blockHeight }
|
func (f feerStub) BlockHeight() uint32 { return f.blockHeight }
|
||||||
func (f feerStub) P2PSigExtensionsEnabled() bool { return false }
|
func (f feerStub) P2PSigExtensionsEnabled() bool { return false }
|
||||||
|
func (f feerStub) GetBaseExecFee() int64 { return interop.DefaultBaseExecFee }
|
||||||
|
|
||||||
func TestMemPool(t *testing.T) {
|
func TestMemPool(t *testing.T) {
|
||||||
s, shutdown := startTestServer(t)
|
s, shutdown := startTestServer(t)
|
||||||
|
|
|
@ -25,6 +25,11 @@ func (c *Client) GetFeePerByte() (int64, error) {
|
||||||
return c.invokeNativePolicyMethod("getFeePerByte")
|
return c.invokeNativePolicyMethod("getFeePerByte")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExecFeeFactor invokes `getExecFeeFactor` method on a native Policy contract.
|
||||||
|
func (c *Client) GetExecFeeFactor() (int64, error) {
|
||||||
|
return c.invokeNativePolicyMethod("getExecFeeFactor")
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
|
func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
|
||||||
if !c.initDone {
|
if !c.initDone {
|
||||||
return 0, errNetworkNotInitialized
|
return 0, errNetworkNotInitialized
|
||||||
|
|
|
@ -569,6 +569,7 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs
|
||||||
return errors.New("number of signers must match number of scripts")
|
return errors.New("number of signers must match number of scripts")
|
||||||
}
|
}
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
|
var ef int64
|
||||||
for i, cosigner := range tx.Signers {
|
for i, cosigner := range tx.Signers {
|
||||||
if accs[i].Contract.Deployed {
|
if accs[i].Contract.Deployed {
|
||||||
res, err := c.InvokeFunction(cosigner.Account, manifest.MethodVerify, []smartcontract.Parameter{}, tx.Signers)
|
res, err := c.InvokeFunction(cosigner.Account, manifest.MethodVerify, []smartcontract.Parameter{}, tx.Signers)
|
||||||
|
@ -589,7 +590,15 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs
|
||||||
size += io.GetVarSize([]byte{}) * 2 // both scripts are empty
|
size += io.GetVarSize([]byte{}) * 2 // both scripts are empty
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
netFee, sizeDelta := fee.Calculate(accs[i].Contract.Script)
|
|
||||||
|
if ef == 0 {
|
||||||
|
var err error
|
||||||
|
ef, err = c.GetExecFeeFactor()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't get `ExecFeeFactor`: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
netFee, sizeDelta := fee.Calculate(ef, accs[i].Contract.Script)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
size += sizeDelta
|
size += sizeDelta
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ func TestAddNetworkFee(t *testing.T) {
|
||||||
}}
|
}}
|
||||||
require.NoError(t, c.AddNetworkFee(tx, 10, accs[0]))
|
require.NoError(t, c.AddNetworkFee(tx, 10, accs[0]))
|
||||||
require.NoError(t, accs[0].SignTx(tx))
|
require.NoError(t, accs[0].SignTx(tx))
|
||||||
cFee, _ := fee.Calculate(accs[0].Contract.Script)
|
cFee, _ := fee.Calculate(chain.GetBaseExecFee(), accs[0].Contract.Script)
|
||||||
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+10, tx.NetworkFee)
|
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+10, tx.NetworkFee)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -126,8 +126,8 @@ func TestAddNetworkFee(t *testing.T) {
|
||||||
require.NoError(t, accs[0].SignTx(tx))
|
require.NoError(t, accs[0].SignTx(tx))
|
||||||
require.NoError(t, accs[1].SignTx(tx))
|
require.NoError(t, accs[1].SignTx(tx))
|
||||||
require.NoError(t, accs[2].SignTx(tx))
|
require.NoError(t, accs[2].SignTx(tx))
|
||||||
cFee, _ := fee.Calculate(accs[0].Contract.Script)
|
cFee, _ := fee.Calculate(chain.GetBaseExecFee(), accs[0].Contract.Script)
|
||||||
cFeeM, _ := fee.Calculate(accs[1].Contract.Script)
|
cFeeM, _ := fee.Calculate(chain.GetBaseExecFee(), accs[1].Contract.Script)
|
||||||
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee)
|
require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee)
|
||||||
})
|
})
|
||||||
t.Run("Contract", func(t *testing.T) {
|
t.Run("Contract", func(t *testing.T) {
|
||||||
|
|
|
@ -11,6 +11,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"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"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/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/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -102,3 +103,7 @@ func (fs *FeerStub) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
|
||||||
func (fs FeerStub) P2PSigExtensionsEnabled() bool {
|
func (fs FeerStub) P2PSigExtensionsEnabled() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs FeerStub) GetBaseExecFee() int64 {
|
||||||
|
return interop.DefaultBaseExecFee
|
||||||
|
}
|
||||||
|
|
|
@ -1003,7 +1003,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
|
|
||||||
addNetworkFee := func(tx *transaction.Transaction) {
|
addNetworkFee := func(tx *transaction.Transaction) {
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
netFee, sizeDelta := fee.Calculate(acc0.Contract.Script)
|
netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc0.Contract.Script)
|
||||||
tx.NetworkFee += netFee
|
tx.NetworkFee += netFee
|
||||||
size += sizeDelta
|
size += sizeDelta
|
||||||
tx.NetworkFee += int64(size) * chain.FeePerByte()
|
tx.NetworkFee += int64(size) * chain.FeePerByte()
|
||||||
|
|
Loading…
Reference in a new issue