native: cache GASPerBlock values

Close #1421.
This commit is contained in:
Evgenii Stratonikov 2020-09-28 10:51:25 +03:00
parent 75753afc33
commit e0e7fd5367
2 changed files with 50 additions and 22 deletions

View file

@ -27,6 +27,11 @@ type NEO struct {
nep5TokenNative
GAS *GAS
// gasPerBlock represents current value of generated gas per block.
// It is append-only and doesn't need to be copied when used.
gasPerBlock atomic.Value
gasPerBlockChanged atomic.Value
votesChanged atomic.Value
nextValidators atomic.Value
validators atomic.Value
@ -188,6 +193,8 @@ func (n *NEO) Initialize(ic *interop.Context) error {
n.mint(ic, h, big.NewInt(NEOTotalSupply))
gr := &state.GASRecord{{Index: 0, GASPerBlock: *big.NewInt(5 * GASFactor)}}
n.gasPerBlock.Store(*gr)
n.gasPerBlockChanged.Store(false)
err = ic.DAO.PutStorageItem(n.ContractID, []byte{prefixGASPerBlock}, &state.StorageItem{Value: gr.Bytes()})
if err != nil {
return err
@ -249,17 +256,24 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
// PostPersist implements Contract interface.
func (n *NEO) PostPersist(ic *interop.Context) error {
gas, err := n.GetGASPerBlock(ic, ic.Block.Index)
if err != nil {
return err
}
gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
pubs := n.GetCommitteeMembers()
index := int(ic.Block.Index) % len(ic.Chain.GetConfig().StandbyCommittee)
gas.Mul(gas, big.NewInt(committeeRewardRatio))
n.GAS.mint(ic, pubs[index].GetScriptHash(), gas.Div(gas, big.NewInt(100)))
n.OnPersistEnd(ic.DAO)
return nil
}
// OnPersistEnd updates cached values if they've been changed.
func (n *NEO) OnPersistEnd(d dao.DAO) {
if n.gasPerBlockChanged.Load().(bool) {
gr := n.getGASRecordFromDAO(d)
n.gasPerBlock.Store(gr)
n.gasPerBlockChanged.Store(false)
}
}
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
acc, err := state.NEOBalanceStateFromBytes(si.Value)
if err != nil {
@ -322,26 +336,32 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem
}
func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
gas, err := n.GetGASPerBlock(ic, ic.Block.Index)
if err != nil {
panic(err)
}
gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
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})
func (n *NEO) getGASRecordFromDAO(d dao.DAO) state.GASRecord {
var gr state.GASRecord
if err := gr.FromBytes(si.Value); err != nil {
return nil, err
si := d.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock})
_ = gr.FromBytes(si.Value)
return gr
}
// GetGASPerBlock returns gas generated for block with provided index.
func (n *NEO) GetGASPerBlock(d dao.DAO, index uint32) *big.Int {
var gr state.GASRecord
if n.gasPerBlockChanged.Load().(bool) {
gr = n.getGASRecordFromDAO(d)
} else {
gr = n.gasPerBlock.Load().(state.GASRecord)
}
for i := len(gr) - 1; i >= 0; i-- {
if gr[i].Index <= index {
return &gr[i].GASPerBlock, nil
g := gr[i].GASPerBlock
return &g
}
}
return nil, errors.New("contract not initialized")
panic("contract not initialized")
}
// GetCommitteeAddress returns address of the committee.
@ -381,6 +401,7 @@ func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) (b
GASPerBlock: *gas,
})
}
n.gasPerBlockChanged.Store(true)
return true, ic.DAO.PutStorageItem(n.ContractID, []byte{prefixGASPerBlock}, &state.StorageItem{Value: gr.Bytes()})
}

View file

@ -130,8 +130,7 @@ func TestNEO_SetGasPerBlock(t *testing.T) {
h := neo.GetCommitteeAddress()
t.Run("Default", func(t *testing.T) {
g, err := neo.GetGASPerBlock(ic, 0)
require.NoError(t, err)
g := neo.GetGASPerBlock(ic.DAO, 0)
require.EqualValues(t, 5*native.GASFactor, g.Int64())
})
t.Run("Invalid", func(t *testing.T) {
@ -149,23 +148,31 @@ func TestNEO_SetGasPerBlock(t *testing.T) {
})
t.Run("Valid", func(t *testing.T) {
setSigner(tx, h)
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor))
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor*2))
require.NoError(t, err)
require.True(t, ok)
neo.OnPersistEnd(ic.DAO)
_, err = ic.DAO.Persist()
require.NoError(t, err)
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)
t.Run("NotPersisted", func(t *testing.T) {
g := neo.GetGASPerBlock(bc.dao, 10)
// Old value should be returned.
require.EqualValues(t, 2*native.GASFactor, g.Int64())
})
})
g, err := neo.GetGASPerBlock(ic, 9)
require.NoError(t, err)
neo.OnPersistEnd(ic.DAO)
g := neo.GetGASPerBlock(ic.DAO, 9)
require.EqualValues(t, 5*native.GASFactor, g.Int64())
g, err = neo.GetGASPerBlock(ic, 10)
require.NoError(t, err)
g = neo.GetGASPerBlock(ic.DAO, 10)
require.EqualValues(t, native.GASFactor, g.Int64())
})
}