native: allow to modify ExecFeeFactor
in the policy contract
This commit is contained in:
parent
1840c1c80d
commit
4946556830
6 changed files with 151 additions and 8 deletions
|
@ -1815,7 +1815,7 @@ func (bc *Blockchain) GetPolicer() blockchainer.Policer {
|
||||||
|
|
||||||
// GetBaseExecFee return execution price for `NOP`.
|
// GetBaseExecFee return execution price for `NOP`.
|
||||||
func (bc *Blockchain) GetBaseExecFee() int64 {
|
func (bc *Blockchain) GetBaseExecFee() int64 {
|
||||||
return interop.DefaultBaseExecFee
|
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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
aers := make([]*state.AppExecResult, len(txs))
|
||||||
|
for i, tx := range txs {
|
||||||
|
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res[0], nil
|
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) {
|
||||||
|
|
|
@ -24,11 +24,14 @@ 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
|
||||||
|
|
||||||
|
@ -40,6 +43,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}
|
||||||
|
@ -59,6 +64,7 @@ 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
|
||||||
|
@ -94,6 +100,15 @@ 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("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,6 +152,7 @@ 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
|
||||||
|
@ -160,6 +176,7 @@ 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
|
||||||
|
@ -256,6 +273,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])
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"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/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||||
|
@ -143,6 +144,62 @@ func TestFeePerByte(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecFeeFactor(t *testing.T) {
|
||||||
|
chain := newTestChain(t)
|
||||||
|
defer chain.Close()
|
||||||
|
policyHash := chain.contracts.Policy.Metadata().Hash
|
||||||
|
|
||||||
|
t.Run("get, internal method", func(t *testing.T) {
|
||||||
|
n := chain.contracts.Policy.GetExecFeeFactorInternal(chain.dao)
|
||||||
|
require.EqualValues(t, interop.DefaultBaseExecFee, n)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("get", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, "getExecFeeFactor")
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(interop.DefaultBaseExecFee)))
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("set, zero fee", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, "setExecFeeFactor", int64(0))
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("set, too big fee", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, "setExecFeeFactor", int64(1001))
|
||||||
|
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, "setExecFeeFactor", int64(123))
|
||||||
|
require.NoError(t, err)
|
||||||
|
txGet1, err := prepareContractMethodInvoke(chain, 100000000, policyHash, "getExecFeeFactor")
|
||||||
|
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(123))
|
||||||
|
require.NoError(t, chain.persist())
|
||||||
|
|
||||||
|
// Get in the next block.
|
||||||
|
res, err := invokeContractMethod(chain, 100000000, policyHash, "getExecFeeFactor")
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkResult(t, res, stackitem.NewBigInteger(big.NewInt(123)))
|
||||||
|
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, "setExecFeeFactor", int64(100))
|
||||||
|
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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/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"
|
||||||
|
@ -570,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)
|
||||||
|
@ -590,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(interop.DefaultBaseExecFee, 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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue