forked from TrueCloudLab/neoneo-go
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 {
|
for i := range tx.Attributes {
|
||||||
switch tx.Attributes[i].Type {
|
switch tx.Attributes[i].Type {
|
||||||
case transaction.HighPriority:
|
case transaction.HighPriority:
|
||||||
pubs, err := bc.contracts.NEO.GetCommitteeMembers(bc, bc.dao)
|
pubs := bc.contracts.NEO.GetCommitteeMembers()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
s, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1376,10 +1373,7 @@ func (bc *Blockchain) GetStandByCommittee() keys.PublicKeys {
|
||||||
|
|
||||||
// GetCommittee returns the sorted list of public keys of nodes in committee.
|
// GetCommittee returns the sorted list of public keys of nodes in committee.
|
||||||
func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) {
|
func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) {
|
||||||
pubs, err := bc.contracts.NEO.GetCommitteeMembers(bc, bc.dao)
|
pubs := bc.contracts.NEO.GetCommitteeMembers()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Sort(pubs)
|
sort.Sort(pubs)
|
||||||
return pubs, nil
|
return pubs, nil
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1385,7 @@ func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) {
|
||||||
|
|
||||||
// GetNextBlockValidators returns next block validators.
|
// GetNextBlockValidators returns next block validators.
|
||||||
func (bc *Blockchain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
|
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.
|
// GetEnrollments returns all registered validators.
|
||||||
|
|
|
@ -2,7 +2,6 @@ package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"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)
|
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee)
|
||||||
g.burn(ic, tx.Sender(), absAmount)
|
g.burn(ic, tx.Sender(), absAmount)
|
||||||
}
|
}
|
||||||
validators, err := g.NEO.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
validators := g.NEO.GetNextBlockValidatorsInternal()
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't get block validators: %w", err)
|
|
||||||
}
|
|
||||||
primary := validators[ic.Block.ConsensusData.PrimaryIndex].GetScriptHash()
|
primary := validators[ic.Block.ConsensusData.PrimaryIndex].GetScriptHash()
|
||||||
var netFee int64
|
var netFee int64
|
||||||
for _, tx := range ic.Block.Transactions {
|
for _, tx := range ic.Block.Transactions {
|
||||||
|
|
|
@ -30,6 +30,10 @@ type NEO struct {
|
||||||
votesChanged atomic.Value
|
votesChanged atomic.Value
|
||||||
nextValidators atomic.Value
|
nextValidators atomic.Value
|
||||||
validators 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
|
// keyWithVotes is a serialized key with votes balance. It's not deserialized
|
||||||
|
@ -63,9 +67,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// nextValidatorsKey is a key used to store validators for the
|
// prefixCommittee is a key used to store committee.
|
||||||
// next block.
|
prefixCommittee = []byte{14}
|
||||||
nextValidatorsKey = []byte{14}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// makeValidatorKey creates a key from account script hash.
|
// makeValidatorKey creates a key from account script hash.
|
||||||
|
@ -93,6 +96,7 @@ func NewNEO() *NEO {
|
||||||
n.votesChanged.Store(true)
|
n.votesChanged.Store(true)
|
||||||
n.nextValidators.Store(keys.PublicKeys(nil))
|
n.nextValidators.Store(keys.PublicKeys(nil))
|
||||||
n.validators.Store(keys.PublicKeys(nil))
|
n.validators.Store(keys.PublicKeys(nil))
|
||||||
|
n.committee.Store(keys.PublicKeys(nil))
|
||||||
|
|
||||||
onp := n.Methods["onPersist"]
|
onp := n.Methods["onPersist"]
|
||||||
onp.Func = getOnPersistWrapper(n.onPersist)
|
onp.Func = getOnPersistWrapper(n.onPersist)
|
||||||
|
@ -158,6 +162,15 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
return errors.New("already initialized")
|
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)
|
h, err := getStandbyValidatorsHash(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -177,45 +190,47 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
return nil
|
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.
|
// OnPersist implements Contract interface.
|
||||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
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)
|
gas, err := n.GetGASPerBlock(ic, ic.Block.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pubs, err := n.GetCommitteeMembers(ic.Chain, ic.DAO)
|
pubs := n.GetCommitteeMembers()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
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)))
|
||||||
|
return nil
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -304,10 +319,7 @@ func (n *NEO) GetGASPerBlock(ic *interop.Context, index uint32) (*big.Int, error
|
||||||
|
|
||||||
// GetCommitteeAddress returns address of the committee.
|
// GetCommitteeAddress returns address of the committee.
|
||||||
func (n *NEO) GetCommitteeAddress(bc blockchainer.Blockchainer, d dao.DAO) (util.Uint160, error) {
|
func (n *NEO) GetCommitteeAddress(bc blockchainer.Blockchainer, d dao.DAO) (util.Uint160, error) {
|
||||||
pubs, err := n.GetCommitteeMembers(bc, d)
|
pubs := n.GetCommitteeMembers()
|
||||||
if err != nil {
|
|
||||||
return util.Uint160{}, err
|
|
||||||
}
|
|
||||||
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint160{}, err
|
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 {
|
if vals := n.validators.Load().(keys.PublicKeys); vals != nil {
|
||||||
return vals.Copy(), nil
|
return vals.Copy(), nil
|
||||||
}
|
}
|
||||||
result, err := n.GetCommitteeMembers(bc, d)
|
result := n.GetCommitteeMembers()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
count := bc.GetConfig().ValidatorsCount
|
count := bc.GetConfig().ValidatorsCount
|
||||||
if len(result) < count {
|
if len(result) < count {
|
||||||
count = len(result)
|
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 {
|
func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
pubs, err := n.GetCommitteeMembers(ic.Chain, ic.DAO)
|
pubs := n.GetCommitteeMembers()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
sort.Sort(pubs)
|
sort.Sort(pubs)
|
||||||
return pubsToArray(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)
|
return d.PutStorageItem(n.ContractID, key, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommitteeMembers returns public keys of nodes in committee.
|
// GetCommitteeMembers returns public keys of nodes in committee using cached value.
|
||||||
func (n *NEO) GetCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
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}
|
key := []byte{prefixVotersCount}
|
||||||
si := d.GetStorageItem(n.ContractID, key)
|
si := d.GetStorageItem(n.ContractID, key)
|
||||||
if si == nil {
|
if si == nil {
|
||||||
|
@ -633,7 +644,8 @@ func (n *NEO) GetCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys
|
||||||
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
|
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
|
||||||
voterTurnout := votersCount.Div(votersCount, n.getTotalSupply(d))
|
voterTurnout := votersCount.Div(votersCount, n.getTotalSupply(d))
|
||||||
if voterTurnout.Sign() != 1 {
|
if voterTurnout.Sign() != 1 {
|
||||||
return bc.GetStandByCommittee(), nil
|
pubs := bc.GetStandByCommittee()
|
||||||
|
return pubs, nil
|
||||||
}
|
}
|
||||||
cs, err := n.getCandidates(d)
|
cs, err := n.getCandidates(d)
|
||||||
if err != nil {
|
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 {
|
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
result, err := n.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
result := n.GetNextBlockValidatorsInternal()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return pubsToArray(result)
|
return pubsToArray(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNextBlockValidatorsInternal returns next block validators.
|
// GetNextBlockValidatorsInternal returns next block validators.
|
||||||
func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
func (n *NEO) GetNextBlockValidatorsInternal() keys.PublicKeys {
|
||||||
pubs, err := n.getNextBlockValidatorsInternal(bc, d)
|
return n.nextValidators.Load().(keys.PublicKeys).Copy()
|
||||||
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 pubsToArray(pubs keys.PublicKeys) stackitem.Item {
|
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.VoteInternal(ic, h, candidates[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require.NoError(t, neo.OnPersist(ic))
|
||||||
|
|
||||||
// We still haven't voted enough validators in.
|
// We still haven't voted enough validators in.
|
||||||
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, standBySorted, pubs)
|
require.Equal(t, standBySorted, pubs)
|
||||||
|
|
||||||
require.NoError(t, neo.OnPersist(ic))
|
require.NoError(t, neo.OnPersist(ic))
|
||||||
pubs, err = neo.GetNextBlockValidatorsInternal(bc, ic.DAO)
|
pubs = neo.GetNextBlockValidatorsInternal()
|
||||||
require.NoError(t, err)
|
|
||||||
require.EqualValues(t, standBySorted, pubs)
|
require.EqualValues(t, standBySorted, pubs)
|
||||||
|
|
||||||
// Register and give some value to the last validator.
|
// 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.RegisterCandidateInternal(ic, priv.PublicKey()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require.NoError(t, neo.OnPersist(ic))
|
||||||
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
sortedCandidates := candidates.Copy()
|
sortedCandidates := candidates.Copy()
|
||||||
sort.Sort(sortedCandidates)
|
sort.Sort(sortedCandidates)
|
||||||
require.EqualValues(t, sortedCandidates, pubs)
|
require.EqualValues(t, sortedCandidates, pubs)
|
||||||
|
|
||||||
require.NoError(t, neo.OnPersist(ic))
|
pubs = neo.GetNextBlockValidatorsInternal()
|
||||||
pubs, err = neo.GetNextBlockValidatorsInternal(bc, ic.DAO)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.EqualValues(t, sortedCandidates, pubs)
|
require.EqualValues(t, sortedCandidates, pubs)
|
||||||
|
|
||||||
require.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0]))
|
require.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0]))
|
||||||
require.Error(t, neo.VoteInternal(ic, h, candidates[0]))
|
require.Error(t, neo.VoteInternal(ic, h, candidates[0]))
|
||||||
|
require.NoError(t, neo.OnPersist(ic))
|
||||||
|
|
||||||
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
Loading…
Reference in a new issue