From e0e7fd536769fa39f7f5c1e4da131c1041ceca35 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 28 Sep 2020 10:51:25 +0300 Subject: [PATCH] native: cache GASPerBlock values Close #1421. --- pkg/core/native/native_neo.go | 51 ++++++++++++++++++++++++----------- pkg/core/native_neo_test.go | 21 ++++++++++----- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index f922c5f25..e69175457 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -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()}) } diff --git a/pkg/core/native_neo_test.go b/pkg/core/native_neo_test.go index 4c200c0fc..6f2dcebf0 100644 --- a/pkg/core/native_neo_test.go +++ b/pkg/core/native_neo_test.go @@ -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()) }) }