diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index e7b306ef9..5eb99da93 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -26,7 +26,9 @@ type NEO struct { nep5TokenNative GAS *GAS - validators atomic.Value + votesChanged atomic.Value + nextValidators atomic.Value + validators atomic.Value } // keyWithVotes is a serialized key with votes balance. It's not deserialized @@ -79,6 +81,8 @@ func NewNEO() *NEO { nep5.ContractID = neoContractID n.nep5TokenNative = *nep5 + n.votesChanged.Store(true) + n.nextValidators.Store(keys.PublicKeys(nil)) n.validators.Store(keys.PublicKeys(nil)) onp := n.Methods["onPersist"] @@ -152,10 +156,28 @@ func (n *NEO) Initialize(ic *interop.Context) error { // OnPersist implements Contract interface. func (n *NEO) OnPersist(ic *interop.Context) error { + if !n.votesChanged.Load().(bool) { + return nil + } pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO) if err != nil { return err } + prev := n.nextValidators.Load().(keys.PublicKeys) + if len(prev) == len(pubs) { + var needUpdate bool + for i := range pubs { + if !pubs[i].Equal(prev[i]) { + needUpdate = true + break + } + } + if !needUpdate { + return nil + } + } + n.votesChanged.Store(false) + n.nextValidators.Store(pubs) si := new(state.StorageItem) si.Value = pubs.Bytes() return ic.DAO.PutStorageItem(n.ContractID, nextValidatorsKey, si) @@ -328,6 +350,7 @@ const ( // ModifyAccountVotes modifies votes of the specified account by value (can be negative). // typ specifies if this modify is occurring during transfer or vote (with old or new validator). func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int, typ int) error { + n.votesChanged.Store(true) if acc.VoteTo != nil { key := makeValidatorKey(acc.VoteTo) si := d.GetStorageItem(n.ContractID, key) diff --git a/pkg/core/native_neo_test.go b/pkg/core/native_neo_test.go index 3fe7591b9..9cce25773 100644 --- a/pkg/core/native_neo_test.go +++ b/pkg/core/native_neo_test.go @@ -70,6 +70,11 @@ func TestNEO_Vote(t *testing.T) { require.NoError(t, err) require.Equal(t, standBySorted, pubs) + require.NoError(t, neo.OnPersist(ic)) + pubs, err = neo.GetNextBlockValidatorsInternal(bc, ic.DAO) + require.NoError(t, err) + require.EqualValues(t, standBySorted, pubs) + // Register and give some value to the last validator. require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[0])) priv := testchain.PrivateKeyByID(0) @@ -84,6 +89,11 @@ func TestNEO_Vote(t *testing.T) { sort.Sort(sortedCandidates) require.EqualValues(t, sortedCandidates, pubs) + require.NoError(t, neo.OnPersist(ic)) + pubs, err = neo.GetNextBlockValidatorsInternal(bc, ic.DAO) + require.NoError(t, err) + require.EqualValues(t, sortedCandidates, pubs) + require.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0])) require.Error(t, neo.VoteInternal(ic, h, candidates[0]))