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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"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/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/crypto/keys"
|
||||||
"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/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -135,6 +136,15 @@ func NewNEO() *NEO {
|
||||||
md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.AllowStates)
|
md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.AllowStates)
|
||||||
n.AddMethod(md, desc, true)
|
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
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +267,80 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem
|
||||||
return stackitem.NewBigInteger(gen)
|
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.
|
// 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) {
|
func (n *NEO) CalculateBonus(ic *interop.Context, value *big.Int, start, end uint32) (*big.Int, error) {
|
||||||
if value.Sign() == 0 || start >= end {
|
if value.Sign() == 0 || start >= end {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"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/native"
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/testchain"
|
"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) {
|
func TestNEO_CalculateBonus(t *testing.T) {
|
||||||
bc := newTestChain(t)
|
bc := newTestChain(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
neo := bc.contracts.NEO
|
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) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
_, err := neo.CalculateBonus(ic, new(big.Int).SetInt64(-1), 0, 1)
|
_, err := neo.CalculateBonus(ic, new(big.Int).SetInt64(-1), 0, 1)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
@ -124,4 +179,17 @@ func TestNEO_CalculateBonus(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, 0, res.Int64())
|
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