diff --git a/internal/fakechain/fakechain.go b/internal/fakechain/fakechain.go index 584861d70..5255d6adb 100644 --- a/internal/fakechain/fakechain.go +++ b/internal/fakechain/fakechain.go @@ -300,16 +300,6 @@ func (chain *FakeChain) GetValidators() ([]*keys.PublicKey, error) { panic("TODO") } -// GetStandByCommittee implements Blockchainer interface. -func (chain *FakeChain) GetStandByCommittee() keys.PublicKeys { - panic("TODO") -} - -// GetStandByValidators implements Blockchainer interface. -func (chain *FakeChain) GetStandByValidators() keys.PublicKeys { - panic("TODO") -} - // GetEnrollments implements Blockchainer interface. func (chain *FakeChain) GetEnrollments() ([]state.Validator, error) { panic("TODO") diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index f697f5770..7518b4c96 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -152,8 +152,6 @@ type Blockchain struct { // Block's transactions are passed via mempool. postBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block) - sbCommittee keys.PublicKeys - log *zap.Logger lastBatch *storage.MemBatch @@ -247,10 +245,6 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L zap.Int("StateSyncInterval", cfg.StateSyncInterval)) } } - committee, err := committeeFromConfig(cfg) - if err != nil { - return nil, err - } if len(cfg.NativeUpdateHistories) == 0 { cfg.NativeUpdateHistories = map[string][]uint32{} log.Info("NativeActivations are not set, using default values") @@ -262,7 +256,6 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L stopCh: make(chan struct{}), runToExitCh: make(chan struct{}), memPool: mempool.New(cfg.MemPoolSize, 0, false), - sbCommittee: committee, log: log, events: make(chan bcEvent), subCh: make(chan interface{}), @@ -2089,16 +2082,6 @@ func (bc *Blockchain) PoolTxWithData(t *transaction.Transaction, data interface{ return bc.verifyAndPoolTx(t, mp, feer, data) } -// GetStandByValidators returns validators from the configuration. -func (bc *Blockchain) GetStandByValidators() keys.PublicKeys { - return bc.sbCommittee[:bc.config.GetNumOfCNs(bc.BlockHeight())].Copy() -} - -// GetStandByCommittee returns standby committee from the configuration. -func (bc *Blockchain) GetStandByCommittee() keys.PublicKeys { - return bc.sbCommittee.Copy() -} - // GetCommittee returns the sorted list of public keys of nodes in committee. func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) { pubs := bc.contracts.NEO.GetCommitteeMembers() diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index d2320730a..307e79990 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -56,8 +56,6 @@ type Blockchainer interface { GetNotaryContractScriptHash() util.Uint160 GetNotaryBalance(acc util.Uint160) *big.Int GetValidators() ([]*keys.PublicKey, error) - GetStandByCommittee() keys.PublicKeys - GetStandByValidators() keys.PublicKeys GetStateModule() StateRoot GetStorageItem(id int32, key []byte) state.StorageItem GetStorageItems(id int32) ([]state.StorageItemWithKey, error) diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 93417ecc7..26a0e723c 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -15,7 +15,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" @@ -42,8 +41,6 @@ type Ledger interface { GetBlock(hash util.Uint256) (*block.Block, error) GetConfig() config.ProtocolConfiguration GetHeaderHash(int) util.Uint256 - GetStandByCommittee() keys.PublicKeys - GetStandByValidators() keys.PublicKeys GetStoragePrice() int64 } diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 1a7784fd1..5be67bb66 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -10,6 +10,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" ) @@ -135,7 +136,12 @@ func (g *GAS) BalanceOf(d dao.DAO, acc util.Uint160) *big.Int { } func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) { - s, err := smartcontract.CreateDefaultMultiSigRedeemScript(ic.Chain.GetStandByValidators()) + cfg := ic.Chain.GetConfig() + committee, err := keys.NewPublicKeysFromStrings(cfg.StandbyCommittee) + if err != nil { + return util.Uint160{}, err + } + s, err := smartcontract.CreateDefaultMultiSigRedeemScript(committee[:cfg.GetNumOfCNs(0)]) if err != nil { return util.Uint160{}, err } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index a1e597a03..b5499b2f1 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -10,6 +10,7 @@ import ( "strings" "sync/atomic" + "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" @@ -54,6 +55,10 @@ type NEO struct { // It is set in state-modifying methods only and read in `PostPersist` thus is not protected // by any mutex. gasPerVoteCache map[string]big.Int + // Configuration and standby keys are set during initialization and then + // only read from. + cfg config.ProtocolConfiguration + standbyKeys keys.PublicKeys } const ( @@ -190,6 +195,10 @@ func newNEO() *NEO { // Initialize initializes NEO contract. func (n *NEO) Initialize(ic *interop.Context) error { + err := n.initConfigCache(ic.Chain) + if err != nil { + return nil + } if err := n.nep17TokenNative.Initialize(ic); err != nil { return err } @@ -199,9 +208,9 @@ func (n *NEO) Initialize(ic *interop.Context) error { return errors.New("already initialized") } - committee := ic.Chain.GetStandByCommittee() - cvs := toKeysWithVotes(committee) - err := n.updateCache(cvs, ic.Chain) + committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)] + cvs := toKeysWithVotes(committee0) + err = n.updateCache(cvs, ic.Chain) if err != nil { return err } @@ -244,6 +253,10 @@ func (n *NEO) Initialize(ic *interop.Context) error { // Cache initialisation should be done apart from Initialize because Initialize is // called only when deploying native contracts. func (n *NEO) InitializeCache(bc interop.Ledger, d dao.DAO) error { + err := n.initConfigCache(bc) + if err != nil { + return nil + } var committee = keysWithVotes{} si := d.GetStorageItem(n.ID, prefixCommittee) if err := committee.DecodeBytes(si); err != nil { @@ -263,6 +276,14 @@ func (n *NEO) InitializeCache(bc interop.Ledger, d dao.DAO) error { return nil } +func (n *NEO) initConfigCache(bc interop.Ledger) error { + var err error + + n.cfg = bc.GetConfig() + n.standbyKeys, err = keys.NewPublicKeysFromStrings(n.cfg.StandbyCommittee) + return err +} + func (n *NEO) updateCache(cvs keysWithVotes, bc interop.Ledger) error { n.committee.Store(cvs) @@ -273,8 +294,7 @@ func (n *NEO) updateCache(cvs keysWithVotes, bc interop.Ledger) error { } n.committeeHash.Store(hash.Hash160(script)) - cfg := bc.GetConfig() - nextVals := committee[:cfg.GetNumOfCNs(bc.BlockHeight()+1)].Copy() + nextVals := committee[:n.cfg.GetNumOfCNs(bc.BlockHeight()+1)].Copy() sort.Sort(nextVals) n.nextValidators.Store(nextVals) return nil @@ -301,12 +321,11 @@ func (n *NEO) updateCommittee(ic *interop.Context) error { // OnPersist implements Contract interface. func (n *NEO) OnPersist(ic *interop.Context) error { - cfg := ic.Chain.GetConfig() - if cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { + if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { oldKeys := n.nextValidators.Load().(keys.PublicKeys) oldCom := n.committee.Load().(keysWithVotes) - if cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) || - cfg.GetCommitteeSize(ic.Block.Index) != len(oldCom) { + if n.cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) || + n.cfg.GetCommitteeSize(ic.Block.Index) != len(oldCom) { n.votesChanged.Store(true) } if err := n.updateCommittee(ic); err != nil { @@ -320,17 +339,16 @@ func (n *NEO) OnPersist(ic *interop.Context) error { func (n *NEO) PostPersist(ic *interop.Context) error { gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index) pubs := n.GetCommitteeMembers() - cfg := ic.Chain.GetConfig() - committeeSize := cfg.GetCommitteeSize(ic.Block.Index) + committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index) index := int(ic.Block.Index) % committeeSize committeeReward := new(big.Int).Mul(gas, bigCommitteeRewardRatio) n.GAS.mint(ic, pubs[index].GetScriptHash(), committeeReward.Div(committeeReward, big100), false) - if cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { + if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { var voterReward = new(big.Int).Set(bigVoterRewardRatio) voterReward.Mul(voterReward, gas) voterReward.Mul(voterReward, big.NewInt(voterRewardFactor*int64(committeeSize))) - var validatorsCount = cfg.GetNumOfCNs(ic.Block.Index) + var validatorsCount = n.cfg.GetNumOfCNs(ic.Block.Index) voterReward.Div(voterReward, big.NewInt(int64(committeeSize+validatorsCount))) voterReward.Div(voterReward, big100) @@ -940,8 +958,7 @@ func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stacki // ComputeNextBlockValidators returns an actual list of current validators. func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d dao.DAO) (keys.PublicKeys, error) { - cfg := bc.GetConfig() - numOfCNs := cfg.GetNumOfCNs(bc.BlockHeight() + 1) + numOfCNs := n.cfg.GetNumOfCNs(bc.BlockHeight() + 1) if vals := n.validators.Load().(keys.PublicKeys); vals != nil && numOfCNs == len(vals) { return vals.Copy(), nil } @@ -1010,10 +1027,9 @@ func (n *NEO) computeCommitteeMembers(bc interop.Ledger, d dao.DAO) (keys.Public _, totalSupply := n.getTotalSupply(d) voterTurnout := votersCount.Div(votersCount, totalSupply) - sbVals := bc.GetStandByCommittee() - cfg := bc.GetConfig() - count := cfg.GetCommitteeSize(bc.BlockHeight() + 1) - sbVals = sbVals[:count] + count := n.cfg.GetCommitteeSize(bc.BlockHeight() + 1) + // Can be sorted and/or returned to outside users, thus needs to be copied. + sbVals := keys.PublicKeys(n.standbyKeys[:count]).Copy() cs, err := n.getCandidates(d, false) if err != nil { return nil, nil, err diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go index 6e308c592..6da70d52e 100644 --- a/pkg/core/native/native_test/neo_test.go +++ b/pkg/core/native/native_test/neo_test.go @@ -50,8 +50,9 @@ func TestNEO_Vote(t *testing.T) { neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) e := neoCommitteeInvoker.Executor - committeeSize := len(neoValidatorsInvoker.Chain.GetConfig().StandbyCommittee) - validatorsCount := neoCommitteeInvoker.Chain.GetConfig().ValidatorsCount + cfg := e.Chain.GetConfig() + committeeSize := cfg.GetCommitteeSize(0) + validatorsCount := cfg.GetNumOfCNs(0) freq := validatorsCount + committeeSize advanceChain := func(t *testing.T) { for i := 0; i < freq; i++ { @@ -59,7 +60,9 @@ func TestNEO_Vote(t *testing.T) { } } - standBySorted := e.Chain.GetStandByValidators() + standBySorted, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee) + require.NoError(t, err) + standBySorted = standBySorted[:validatorsCount] sort.Sort(standBySorted) pubs, err := e.Chain.GetValidators() require.NoError(t, err) @@ -250,7 +253,8 @@ func TestNEO_CommitteeBountyOnPersist(t *testing.T) { neoCommitteeInvoker := newNeoCommitteeClient(t, 0) e := neoCommitteeInvoker.Executor - hs := e.Chain.GetStandByCommittee() + hs, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee) + require.NoError(t, err) committeeSize := len(hs) const singleBounty = 50000000 diff --git a/pkg/core/util.go b/pkg/core/util.go index e66243d60..308a75151 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -49,25 +49,13 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) } func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) { - vs, err := committeeFromConfig(cfg) + vs, err := keys.NewPublicKeysFromStrings(cfg.StandbyCommittee) if err != nil { return nil, err } return vs[:cfg.GetNumOfCNs(0)], nil } -func committeeFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) { - validators := make([]*keys.PublicKey, len(cfg.StandbyCommittee)) - for i := range validators { - pubKey, err := keys.NewPublicKeyFromString(cfg.StandbyCommittee[i]) - if err != nil { - return nil, err - } - validators[i] = pubKey - } - return validators, nil -} - func getNextConsensusAddress(validators []*keys.PublicKey) (val util.Uint160, err error) { raw, err := smartcontract.CreateDefaultMultiSigRedeemScript(validators) if err != nil { diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index ecbeee259..56c3f4a48 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -32,6 +32,20 @@ type PublicKeys []*PublicKey var big0 = big.NewInt(0) var big3 = big.NewInt(3) +// NewPublicKeysFromStrings converts an array of string-encoded P256 public keys +// into an array of PublicKeys. +func NewPublicKeysFromStrings(ss []string) (PublicKeys, error) { + arr := make([]*PublicKey, len(ss)) + for i := range ss { + pubKey, err := NewPublicKeyFromString(ss[i]) + if err != nil { + return nil, err + } + arr[i] = pubKey + } + return PublicKeys(arr), nil +} + func (keys PublicKeys) Len() int { return len(keys) } func (keys PublicKeys) Swap(i, j int) { keys[i], keys[j] = keys[j], keys[i] } func (keys PublicKeys) Less(i, j int) bool { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index cc54c2259..cebad1bb7 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -699,8 +699,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { params: "[]", result: func(e *executor) interface{} { - // it's a test chain, so committee is a sorted standby committee - expected := e.chain.GetStandByCommittee() + expected, _ := e.chain.GetCommittee() sort.Sort(expected) return &expected }, diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index 15712d532..55f3ff5da 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -64,7 +64,9 @@ func main() { bc, err := newChain() handleError("can't initialize blockchain", err) - valScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(bc.GetStandByValidators()) + nbVals, err := bc.GetNextBlockValidators() + handleError("can't get next block validators", err) + valScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(nbVals) handleError("can't create verification script", err) lastBlock, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight()))) handleError("can't fetch last block", err)