native: cache committee members
This commit is contained in:
parent
43b3e15330
commit
83e94d3bbc
4 changed files with 73 additions and 91 deletions
|
@ -1212,10 +1212,7 @@ func (bc *Blockchain) verifyTxAttributes(tx *transaction.Transaction) error {
|
|||
for i := range tx.Attributes {
|
||||
switch tx.Attributes[i].Type {
|
||||
case transaction.HighPriority:
|
||||
pubs, err := bc.contracts.NEO.GetCommitteeMembers(bc, bc.dao)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pubs := bc.contracts.NEO.GetCommitteeMembers()
|
||||
s, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1376,10 +1373,7 @@ func (bc *Blockchain) GetStandByCommittee() keys.PublicKeys {
|
|||
|
||||
// GetCommittee returns the sorted list of public keys of nodes in committee.
|
||||
func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) {
|
||||
pubs, err := bc.contracts.NEO.GetCommitteeMembers(bc, bc.dao)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubs := bc.contracts.NEO.GetCommitteeMembers()
|
||||
sort.Sort(pubs)
|
||||
return pubs, nil
|
||||
}
|
||||
|
@ -1391,7 +1385,7 @@ func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) {
|
|||
|
||||
// GetNextBlockValidators returns next block validators.
|
||||
func (bc *Blockchain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
|
||||
return bc.contracts.NEO.GetNextBlockValidatorsInternal(bc, bc.dao)
|
||||
return bc.contracts.NEO.GetNextBlockValidatorsInternal(), nil
|
||||
}
|
||||
|
||||
// GetEnrollments returns all registered validators.
|
||||
|
|
|
@ -2,7 +2,6 @@ package native
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
|
@ -89,10 +88,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
|
|||
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
|
||||
g.burn(ic, tx.Sender(), absAmount)
|
||||
}
|
||||
validators, err := g.NEO.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get block validators: %w", err)
|
||||
}
|
||||
validators := g.NEO.GetNextBlockValidatorsInternal()
|
||||
primary := validators[ic.Block.ConsensusData.PrimaryIndex].GetScriptHash()
|
||||
var netFee int64
|
||||
for _, tx := range ic.Block.Transactions {
|
||||
|
|
|
@ -30,6 +30,10 @@ type NEO struct {
|
|||
votesChanged atomic.Value
|
||||
nextValidators atomic.Value
|
||||
validators atomic.Value
|
||||
// committee contains cached committee members and
|
||||
// is updated during block persist. It's value
|
||||
// is always equal to value stored by `prefixCommittee`.
|
||||
committee atomic.Value
|
||||
}
|
||||
|
||||
// keyWithVotes is a serialized key with votes balance. It's not deserialized
|
||||
|
@ -63,9 +67,8 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
// nextValidatorsKey is a key used to store validators for the
|
||||
// next block.
|
||||
nextValidatorsKey = []byte{14}
|
||||
// prefixCommittee is a key used to store committee.
|
||||
prefixCommittee = []byte{14}
|
||||
)
|
||||
|
||||
// makeValidatorKey creates a key from account script hash.
|
||||
|
@ -93,6 +96,7 @@ func NewNEO() *NEO {
|
|||
n.votesChanged.Store(true)
|
||||
n.nextValidators.Store(keys.PublicKeys(nil))
|
||||
n.validators.Store(keys.PublicKeys(nil))
|
||||
n.committee.Store(keys.PublicKeys(nil))
|
||||
|
||||
onp := n.Methods["onPersist"]
|
||||
onp.Func = getOnPersistWrapper(n.onPersist)
|
||||
|
@ -158,6 +162,15 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
|||
return errors.New("already initialized")
|
||||
}
|
||||
|
||||
committee := ic.Chain.GetStandByCommittee()
|
||||
n.committee.Store(committee)
|
||||
n.updateNextValidators(committee, ic.Chain)
|
||||
|
||||
err := ic.DAO.PutStorageItem(n.ContractID, prefixCommittee, &state.StorageItem{Value: committee.Bytes()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h, err := getStandbyValidatorsHash(ic)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -177,45 +190,47 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) updateNextValidators(committee keys.PublicKeys, bc blockchainer.Blockchainer) {
|
||||
nextVals := committee[:bc.GetConfig().ValidatorsCount].Copy()
|
||||
sort.Sort(nextVals)
|
||||
n.nextValidators.Store(nextVals)
|
||||
}
|
||||
|
||||
func (n *NEO) updateCommittee(ic *interop.Context) error {
|
||||
votesChanged := n.votesChanged.Load().(bool)
|
||||
if !votesChanged {
|
||||
// We need to put in storage anyway, as it affects dumps
|
||||
committee := n.committee.Load().(keys.PublicKeys)
|
||||
si := &state.StorageItem{Value: committee.Bytes()}
|
||||
return ic.DAO.PutStorageItem(n.ContractID, prefixCommittee, si)
|
||||
}
|
||||
|
||||
committee, err := n.ComputeCommitteeMembers(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.committee.Store(committee)
|
||||
n.updateNextValidators(committee, ic.Chain)
|
||||
n.votesChanged.Store(false)
|
||||
si := &state.StorageItem{Value: committee.Bytes()}
|
||||
return ic.DAO.PutStorageItem(n.ContractID, prefixCommittee, si)
|
||||
}
|
||||
|
||||
// OnPersist implements Contract interface.
|
||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||
if err := n.updateCommittee(ic); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gas, err := n.GetGASPerBlock(ic, ic.Block.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pubs, err := n.GetCommitteeMembers(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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)))
|
||||
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
||||
|
@ -304,10 +319,7 @@ func (n *NEO) GetGASPerBlock(ic *interop.Context, index uint32) (*big.Int, error
|
|||
|
||||
// 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
|
||||
}
|
||||
pubs := n.GetCommitteeMembers()
|
||||
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
||||
if err != nil {
|
||||
return util.Uint160{}, err
|
||||
|
@ -578,10 +590,7 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
|
|||
if vals := n.validators.Load().(keys.PublicKeys); vals != nil {
|
||||
return vals.Copy(), nil
|
||||
}
|
||||
result, err := n.GetCommitteeMembers(bc, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := n.GetCommitteeMembers()
|
||||
count := bc.GetConfig().ValidatorsCount
|
||||
if len(result) < count {
|
||||
count = len(result)
|
||||
|
@ -601,10 +610,7 @@ func (n *NEO) getValidators(ic *interop.Context, _ []stackitem.Item) stackitem.I
|
|||
}
|
||||
|
||||
func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
pubs, err := n.GetCommitteeMembers(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pubs := n.GetCommitteeMembers()
|
||||
sort.Sort(pubs)
|
||||
return pubsToArray(pubs)
|
||||
}
|
||||
|
@ -621,8 +627,13 @@ func (n *NEO) modifyVoterTurnout(d dao.DAO, amount *big.Int) error {
|
|||
return d.PutStorageItem(n.ContractID, key, si)
|
||||
}
|
||||
|
||||
// GetCommitteeMembers returns public keys of nodes in committee.
|
||||
func (n *NEO) GetCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||
// GetCommitteeMembers returns public keys of nodes in committee using cached value.
|
||||
func (n *NEO) GetCommitteeMembers() keys.PublicKeys {
|
||||
return n.committee.Load().(keys.PublicKeys).Copy()
|
||||
}
|
||||
|
||||
// ComputeCommitteeMembers returns public keys of nodes in committee.
|
||||
func (n *NEO) ComputeCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||
key := []byte{prefixVotersCount}
|
||||
si := d.GetStorageItem(n.ContractID, key)
|
||||
if si == nil {
|
||||
|
@ -633,7 +644,8 @@ func (n *NEO) GetCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys
|
|||
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
|
||||
voterTurnout := votersCount.Div(votersCount, n.getTotalSupply(d))
|
||||
if voterTurnout.Sign() != 1 {
|
||||
return bc.GetStandByCommittee(), nil
|
||||
pubs := bc.GetStandByCommittee()
|
||||
return pubs, nil
|
||||
}
|
||||
cs, err := n.getCandidates(d)
|
||||
if err != nil {
|
||||
|
@ -664,34 +676,13 @@ func (n *NEO) GetCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys
|
|||
}
|
||||
|
||||
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
result, err := n.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result := n.GetNextBlockValidatorsInternal()
|
||||
return pubsToArray(result)
|
||||
}
|
||||
|
||||
// GetNextBlockValidatorsInternal returns next block validators.
|
||||
func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||
pubs, err := n.getNextBlockValidatorsInternal(bc, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pubs.Copy(), nil
|
||||
}
|
||||
|
||||
// getNextBlockValidatorsInternal returns next block validators.
|
||||
func (n *NEO) getNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||
si := d.GetStorageItem(n.ContractID, nextValidatorsKey)
|
||||
if si == nil {
|
||||
return n.GetValidatorsInternal(bc, d)
|
||||
}
|
||||
pubs := keys.PublicKeys{}
|
||||
err := pubs.DecodeBytes(si.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pubs, nil
|
||||
func (n *NEO) GetNextBlockValidatorsInternal() keys.PublicKeys {
|
||||
return n.nextValidators.Load().(keys.PublicKeys).Copy()
|
||||
}
|
||||
|
||||
func pubsToArray(pubs keys.PublicKeys) stackitem.Item {
|
||||
|
|
|
@ -68,14 +68,15 @@ func TestNEO_Vote(t *testing.T) {
|
|||
require.NoError(t, neo.VoteInternal(ic, h, candidates[i]))
|
||||
}
|
||||
|
||||
require.NoError(t, neo.OnPersist(ic))
|
||||
|
||||
// We still haven't voted enough validators in.
|
||||
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||
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)
|
||||
pubs = neo.GetNextBlockValidatorsInternal()
|
||||
require.EqualValues(t, standBySorted, pubs)
|
||||
|
||||
// Register and give some value to the last validator.
|
||||
|
@ -91,19 +92,19 @@ func TestNEO_Vote(t *testing.T) {
|
|||
require.NoError(t, neo.RegisterCandidateInternal(ic, priv.PublicKey()))
|
||||
}
|
||||
|
||||
require.NoError(t, neo.OnPersist(ic))
|
||||
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||
require.NoError(t, err)
|
||||
sortedCandidates := candidates.Copy()
|
||||
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)
|
||||
pubs = neo.GetNextBlockValidatorsInternal()
|
||||
require.EqualValues(t, sortedCandidates, pubs)
|
||||
|
||||
require.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0]))
|
||||
require.Error(t, neo.VoteInternal(ic, h, candidates[0]))
|
||||
require.NoError(t, neo.OnPersist(ic))
|
||||
|
||||
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||
require.NoError(t, err)
|
||||
|
|
Loading…
Reference in a new issue