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 nep5TokenNative
GAS *GAS 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 votesChanged atomic.Value
nextValidators atomic.Value nextValidators atomic.Value
validators atomic.Value validators atomic.Value
@ -188,6 +193,8 @@ func (n *NEO) Initialize(ic *interop.Context) error {
n.mint(ic, h, big.NewInt(NEOTotalSupply)) n.mint(ic, h, big.NewInt(NEOTotalSupply))
gr := &state.GASRecord{{Index: 0, GASPerBlock: *big.NewInt(5 * GASFactor)}} 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()}) err = ic.DAO.PutStorageItem(n.ContractID, []byte{prefixGASPerBlock}, &state.StorageItem{Value: gr.Bytes()})
if err != nil { if err != nil {
return err return err
@ -249,17 +256,24 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
// PostPersist implements Contract interface. // PostPersist implements Contract interface.
func (n *NEO) PostPersist(ic *interop.Context) error { func (n *NEO) PostPersist(ic *interop.Context) error {
gas, err := n.GetGASPerBlock(ic, ic.Block.Index) gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
if err != nil {
return err
}
pubs := n.GetCommitteeMembers() pubs := n.GetCommitteeMembers()
index := int(ic.Block.Index) % len(ic.Chain.GetConfig().StandbyCommittee) index := int(ic.Block.Index) % len(ic.Chain.GetConfig().StandbyCommittee)
gas.Mul(gas, big.NewInt(committeeRewardRatio)) gas.Mul(gas, big.NewInt(committeeRewardRatio))
n.GAS.mint(ic, pubs[index].GetScriptHash(), gas.Div(gas, big.NewInt(100))) n.GAS.mint(ic, pubs[index].GetScriptHash(), gas.Div(gas, big.NewInt(100)))
n.OnPersistEnd(ic.DAO)
return nil 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 { func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
acc, err := state.NEOBalanceStateFromBytes(si.Value) acc, err := state.NEOBalanceStateFromBytes(si.Value)
if err != nil { 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 { func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
gas, err := n.GetGASPerBlock(ic, ic.Block.Index) gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
if err != nil {
panic(err)
}
return stackitem.NewBigInteger(gas) return stackitem.NewBigInteger(gas)
} }
// GetGASPerBlock returns gas generated for block with provided index. func (n *NEO) getGASRecordFromDAO(d dao.DAO) state.GASRecord {
func (n *NEO) GetGASPerBlock(ic *interop.Context, index uint32) (*big.Int, error) {
si := ic.DAO.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock})
var gr state.GASRecord var gr state.GASRecord
if err := gr.FromBytes(si.Value); err != nil { si := d.GetStorageItem(n.ContractID, []byte{prefixGASPerBlock})
return nil, err _ = 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-- { for i := len(gr) - 1; i >= 0; i-- {
if gr[i].Index <= index { 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. // 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, GASPerBlock: *gas,
}) })
} }
n.gasPerBlockChanged.Store(true)
return true, ic.DAO.PutStorageItem(n.ContractID, []byte{prefixGASPerBlock}, &state.StorageItem{Value: gr.Bytes()}) 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() h := neo.GetCommitteeAddress()
t.Run("Default", func(t *testing.T) { t.Run("Default", func(t *testing.T) {
g, err := neo.GetGASPerBlock(ic, 0) g := neo.GetGASPerBlock(ic.DAO, 0)
require.NoError(t, err)
require.EqualValues(t, 5*native.GASFactor, g.Int64()) require.EqualValues(t, 5*native.GASFactor, g.Int64())
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
@ -149,23 +148,31 @@ func TestNEO_SetGasPerBlock(t *testing.T) {
}) })
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
setSigner(tx, h) 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.NoError(t, err)
require.True(t, ok) require.True(t, ok)
neo.OnPersistEnd(ic.DAO)
_, err = ic.DAO.Persist()
require.NoError(t, err)
t.Run("Again", func(t *testing.T) { t.Run("Again", func(t *testing.T) {
setSigner(tx, h) setSigner(tx, h)
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor)) ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor))
require.NoError(t, err) require.NoError(t, err)
require.True(t, ok) 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) neo.OnPersistEnd(ic.DAO)
require.NoError(t, err) g := neo.GetGASPerBlock(ic.DAO, 9)
require.EqualValues(t, 5*native.GASFactor, g.Int64()) require.EqualValues(t, 5*native.GASFactor, g.Int64())
g, err = neo.GetGASPerBlock(ic, 10) g = neo.GetGASPerBlock(ic.DAO, 10)
require.NoError(t, err)
require.EqualValues(t, native.GASFactor, g.Int64()) require.EqualValues(t, native.GASFactor, g.Int64())
}) })
} }