native: implement NEO.Get/SetMaxGasPerBlock()
This commit is contained in:
parent
7d90d79ae6
commit
5a38208361
2 changed files with 153 additions and 1 deletions
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -135,6 +136,15 @@ func NewNEO() *NEO {
|
|||
md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.AllowStates)
|
||||
n.AddMethod(md, desc, true)
|
||||
|
||||
desc = newDescriptor("getGasPerBlock", smartcontract.IntegerType)
|
||||
md = newMethodAndPrice(n.getGASPerBlock, 100_0000, smartcontract.AllowStates)
|
||||
n.AddMethod(md, desc, false)
|
||||
|
||||
desc = newDescriptor("setGasPerBlock", smartcontract.BoolType,
|
||||
manifest.NewParameter("gasPerBlock", smartcontract.IntegerType))
|
||||
md = newMethodAndPrice(n.setGASPerBlock, 500_0000, smartcontract.AllowModifyStates)
|
||||
n.AddMethod(md, desc, false)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -257,6 +267,80 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem
|
|||
return stackitem.NewBigInteger(gen)
|
||||
}
|
||||
|
||||
func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
gas, err := n.GetGASPerBlock(ic, ic.Block.Index)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return stackitem.NewBigInteger(gas)
|
||||
}
|
||||
|
||||
// GetGASPerBlock returns gas generated for block with provided index.
|
||||
func (n *NEO) GetGASPerBlock(ic *interop.Context, index uint32) (*big.Int, error) {
|
||||
si := ic.DAO.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock})
|
||||
var gr state.GASRecord
|
||||
if err := gr.FromBytes(si.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := len(gr) - 1; i >= 0; i-- {
|
||||
if gr[i].Index <= index {
|
||||
return &gr[i].GASPerBlock, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("contract not initialized")
|
||||
}
|
||||
|
||||
// GetCommitteeAddress returns address of the committee.
|
||||
func (n *NEO) GetCommitteeAddress(bc blockchainer.Blockchainer, d dao.DAO) (util.Uint160, error) {
|
||||
pubs, err := n.GetCommitteeMembers(bc, d)
|
||||
if err != nil {
|
||||
return util.Uint160{}, err
|
||||
}
|
||||
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
||||
if err != nil {
|
||||
return util.Uint160{}, err
|
||||
}
|
||||
return hash.Hash160(script), nil
|
||||
}
|
||||
|
||||
func (n *NEO) setGASPerBlock(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
gas := toBigInt(args[0])
|
||||
ok, err := n.SetGASPerBlock(ic, ic.Block.Index+1, gas)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return stackitem.NewBool(ok)
|
||||
}
|
||||
|
||||
// SetGASPerBlock sets gas generated for blocks after index.
|
||||
func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) (bool, error) {
|
||||
if gas.Sign() == -1 || gas.Cmp(big.NewInt(10*GASFactor)) == 1 {
|
||||
return false, errors.New("invalid value for GASPerBlock")
|
||||
}
|
||||
h, err := n.GetCommitteeAddress(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ok, err := runtime.CheckHashedWitness(ic, h)
|
||||
if err != nil || !ok {
|
||||
return ok, err
|
||||
}
|
||||
si := ic.DAO.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock})
|
||||
var gr state.GASRecord
|
||||
if err := gr.FromBytes(si.Value); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(gr) > 0 && gr[len(gr)-1].Index == index {
|
||||
gr[len(gr)-1].GASPerBlock = *gas
|
||||
} else {
|
||||
gr = append(gr, state.GASIndexPair{
|
||||
Index: index,
|
||||
GASPerBlock: *gas,
|
||||
})
|
||||
}
|
||||
return true, ic.DAO.PutStorageItem(n.ContractID, []byte{prefixGASPerBlock}, &state.StorageItem{Value: gr.Bytes()})
|
||||
}
|
||||
|
||||
// CalculateBonus calculates amount of gas generated for holding `value` NEO from start to end block.
|
||||
func (n *NEO) CalculateBonus(ic *interop.Context, value *big.Int, start, end uint32) (*big.Int, error) {
|
||||
if value.Sign() == 0 || start >= end {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||
"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/internal/testchain"
|
||||
|
@ -109,12 +110,66 @@ func TestNEO_Vote(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNEO_SetGasPerBlock(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
defer bc.Close()
|
||||
|
||||
neo := bc.contracts.NEO
|
||||
tx := transaction.New(netmode.UnitTestNet, []byte{}, 0)
|
||||
ic := bc.newInteropContext(trigger.System, bc.dao, nil, tx)
|
||||
ic.VM = vm.New()
|
||||
|
||||
h, err := neo.GetCommitteeAddress(bc, bc.dao)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
g, err := neo.GetGASPerBlock(ic, 0)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 5*native.GASFactor, g.Int64())
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Run("InvalidSignature", func(t *testing.T) {
|
||||
setSigner(tx, util.Uint160{})
|
||||
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor))
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
})
|
||||
t.Run("TooBigValue", func(t *testing.T) {
|
||||
setSigner(tx, h)
|
||||
_, err := neo.SetGASPerBlock(ic, 10, big.NewInt(10*native.GASFactor+1))
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
setSigner(tx, h)
|
||||
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
t.Run("Again", func(t *testing.T) {
|
||||
setSigner(tx, h)
|
||||
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
||||
g, err := neo.GetGASPerBlock(ic, 9)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 5*native.GASFactor, g.Int64())
|
||||
|
||||
g, err = neo.GetGASPerBlock(ic, 10)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, native.GASFactor, g.Int64())
|
||||
})
|
||||
}
|
||||
|
||||
func TestNEO_CalculateBonus(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
defer bc.Close()
|
||||
|
||||
neo := bc.contracts.NEO
|
||||
ic := bc.newInteropContext(trigger.System, bc.dao, nil, nil)
|
||||
tx := transaction.New(netmode.UnitTestNet, []byte{}, 0)
|
||||
ic := bc.newInteropContext(trigger.System, bc.dao, nil, tx)
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
_, err := neo.CalculateBonus(ic, new(big.Int).SetInt64(-1), 0, 1)
|
||||
require.Error(t, err)
|
||||
|
@ -124,4 +179,17 @@ func TestNEO_CalculateBonus(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, res.Int64())
|
||||
})
|
||||
t.Run("ManyBlocks", func(t *testing.T) {
|
||||
h, err := neo.GetCommitteeAddress(bc, bc.dao)
|
||||
require.NoError(t, err)
|
||||
setSigner(tx, h)
|
||||
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(1*native.GASFactor))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
res, err := neo.CalculateBonus(ic, big.NewInt(100), 5, 15)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, (100*5*5/10)+(100*5*1/10), res.Int64())
|
||||
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue