native: implement NEO.Get/SetMaxGasPerBlock()

This commit is contained in:
Evgenii Stratonikov 2020-08-26 13:06:19 +03:00
parent 7d90d79ae6
commit 5a38208361
2 changed files with 153 additions and 1 deletions

View file

@ -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 {

View file

@ -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())
})
}