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`.
|
||||
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.
|
||||
|
|
|
@ -413,7 +413,8 @@ func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.A
|
|||
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()
|
||||
emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...)
|
||||
if w.Err != nil {
|
||||
|
@ -427,17 +428,37 @@ func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, me
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := chain.newBlock(tx)
|
||||
err = chain.AddBlock(b)
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func persistBlock(chain *Blockchain, txs ...*transaction.Transaction) ([]*state.AppExecResult, error) {
|
||||
b := chain.newBlock(txs...)
|
||||
err := chain.AddBlock(b)
|
||||
if err != nil {
|
||||
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 {
|
||||
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) {
|
||||
|
|
|
@ -24,11 +24,14 @@ const (
|
|||
|
||||
defaultMaxBlockSize = 1024 * 256
|
||||
defaultMaxTransactionsPerBlock = 512
|
||||
defaultExecFeeFactor = interop.DefaultBaseExecFee
|
||||
defaultFeePerByte = 1000
|
||||
defaultMaxVerificationGas = 50000000
|
||||
defaultMaxBlockSystemFee = 9000 * GASFactor
|
||||
// minBlockSystemFee is the minimum allowed system fee per block.
|
||||
minBlockSystemFee = 4007600
|
||||
// maxExecFeeFactor is the maximum allowed execution fee factor.
|
||||
maxExecFeeFactor = 1000
|
||||
// maxFeePerByte is the maximum allowed fee per byte value.
|
||||
maxFeePerByte = 100_000_000
|
||||
|
||||
|
@ -40,6 +43,8 @@ var (
|
|||
// maxTransactionsPerBlockKey is a key used to store the maximum number of
|
||||
// transactions allowed in block.
|
||||
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
|
||||
// transaction.
|
||||
feePerByteKey = []byte{10}
|
||||
|
@ -59,6 +64,7 @@ type Policy struct {
|
|||
isValid bool
|
||||
maxTransactionsPerBlock uint32
|
||||
maxBlockSize uint32
|
||||
execFeeFactor uint32
|
||||
feePerByte int64
|
||||
maxBlockSystemFee int64
|
||||
maxVerificationGas int64
|
||||
|
@ -94,6 +100,15 @@ func newPolicy() *Policy {
|
|||
md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.ReadStates)
|
||||
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,
|
||||
manifest.NewParameter("value", smartcontract.IntegerType))
|
||||
md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.WriteStates)
|
||||
|
@ -137,6 +152,7 @@ func (p *Policy) Initialize(ic *interop.Context) error {
|
|||
p.isValid = true
|
||||
p.maxTransactionsPerBlock = defaultMaxTransactionsPerBlock
|
||||
p.maxBlockSize = defaultMaxBlockSize
|
||||
p.execFeeFactor = defaultExecFeeFactor
|
||||
p.feePerByte = defaultFeePerByte
|
||||
p.maxBlockSystemFee = defaultMaxBlockSystemFee
|
||||
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.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.maxBlockSystemFee = getInt64WithKey(p.ContractID, ic.DAO, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
||||
p.maxVerificationGas = defaultMaxVerificationGas
|
||||
|
@ -256,6 +273,41 @@ func (p *Policy) GetMaxBlockSystemFeeInternal(dao dao.DAO) int64 {
|
|||
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.
|
||||
func (p *Policy) isBlocked(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
hash := toUint160(args[0])
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"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/interop"
|
||||
"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"
|
||||
|
@ -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) {
|
||||
chain := newTestChain(t)
|
||||
defer chain.Close()
|
||||
|
|
|
@ -25,6 +25,11 @@ func (c *Client) GetFeePerByte() (int64, error) {
|
|||
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) {
|
||||
if !c.initDone {
|
||||
return 0, errNetworkNotInitialized
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"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/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/transaction"
|
||||
"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")
|
||||
}
|
||||
size := io.GetVarSize(tx)
|
||||
var ef int64
|
||||
for i, cosigner := range tx.Signers {
|
||||
if accs[i].Contract.Deployed {
|
||||
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
|
||||
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
|
||||
size += sizeDelta
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue