From 9095b3e7f98abd9f99d34a866db18625f134f4ec Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Thu, 20 Jun 2024 15:47:28 +0300 Subject: [PATCH 1/2] deploy: transfer GAS in the first place if notaries are set It is possible to start blockchain with required roles already set. In this case, there is no need to wait for NNS to be deployed for GAS transfers, since notary service (requires Notary roles to be set correctly) is ready. It allows all multi-signed operations to be applied at an earlier stage. Closes #409. Signed-off-by: Pavel Karpy --- deploy/deploy.go | 191 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 138 insertions(+), 53 deletions(-) diff --git a/deploy/deploy.go b/deploy/deploy.go index f51e5eb4..a326b900 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -10,12 +10,15 @@ import ( "strconv" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "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/keys" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" @@ -205,11 +208,50 @@ func Deploy(ctx context.Context, prm Prm) error { chNewBlock := make(chan struct{}, 1) + err = listenCommitteeNotaryRequests(ctx, listenCommitteeNotaryRequestsPrm{ + logger: prm.Logger, + blockchain: prm.Blockchain, + localAcc: prm.LocalAccount, + committee: committee, + validatorMultiSigAcc: prm.ValidatorMultiSigAccount, + }) + if err != nil { + return fmt.Errorf("start listener of committee notary requests: %w", err) + } + go autoReplenishNotaryBalance(ctx, prm.Logger, prm.Blockchain, prm.LocalAccount, chNewBlock) + monitor, err := newBlockchainMonitor(prm.Logger, prm.Blockchain, chNewBlock) if err != nil { return fmt.Errorf("init blockchain monitor: %w", err) } + rolesAreSet, err := checkCommitteeRoles(prm.Logger, prm.Blockchain, monitor, committee) + if err != nil { + return fmt.Errorf("pre-check committee roles: %w", err) + } + transfersDone := rolesAreSet // currently the same + if rolesAreSet { + // if it is possible to transfer GAS before any network settings, + // it should be done as it speeds up expensive operations + + prm.Logger.Info("making initial transfer of funds to the committee...") + + err = makeInitialTransferToCommittee(ctx, makeInitialGASTransferToCommitteePrm{ + logger: prm.Logger, + blockchain: prm.Blockchain, + monitor: monitor, + committee: committee, + localAcc: prm.LocalAccount, + validatorMultiSigAcc: prm.ValidatorMultiSigAccount, + tryTransfer: localAccCommitteeIndex == 0, + }) + if err != nil { + return fmt.Errorf("initial transfer funds to the committee: %w", err) + } + + prm.Logger.Info("initial transfer to the committee successfully done") + } + deployNNSPrm := deployNNSContractPrm{ logger: prm.Logger, blockchain: prm.Blockchain, @@ -235,69 +277,66 @@ func Deploy(ctx context.Context, prm Prm) error { prm.Logger.Info("NNS contract successfully initialized on the chain", zap.Stringer("address", nnsOnChainAddress)) - prm.Logger.Info("enable Notary service for the committee...") - - err = enableNotary(ctx, enableNotaryPrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - monitor: monitor, - nnsOnChainAddress: nnsOnChainAddress, - systemEmail: prm.NNS.SystemEmail, - committee: committee, - localAcc: prm.LocalAccount, - localAccCommitteeIndex: localAccCommitteeIndex, - }) - if err != nil { - return fmt.Errorf("enable Notary service for the committee: %w", err) - } - - prm.Logger.Info("Notary service successfully enabled for the committee") - - go autoReplenishNotaryBalance(ctx, prm.Logger, prm.Blockchain, prm.LocalAccount, chNewBlock) + if !rolesAreSet { + prm.Logger.Info("enable Notary service for the committee...") + + err = enableNotary(ctx, enableNotaryPrm{ + logger: prm.Logger, + blockchain: prm.Blockchain, + monitor: monitor, + nnsOnChainAddress: nnsOnChainAddress, + systemEmail: prm.NNS.SystemEmail, + committee: committee, + localAcc: prm.LocalAccount, + localAccCommitteeIndex: localAccCommitteeIndex, + }) + if err != nil { + return fmt.Errorf("enable Notary service for the committee: %w", err) + } - err = listenCommitteeNotaryRequests(ctx, listenCommitteeNotaryRequestsPrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - localAcc: prm.LocalAccount, - committee: committee, - validatorMultiSigAcc: prm.ValidatorMultiSigAccount, - }) - if err != nil { - return fmt.Errorf("start listener of committee notary requests: %w", err) + prm.Logger.Info("Notary service successfully enabled for the committee") + } else { + prm.Logger.Debug("notary roles are already set") } - prm.Logger.Info("making initial transfer of funds to the committee...") + if !transfersDone { + prm.Logger.Info("making initial transfer of funds to the committee...") + + err = makeInitialTransferToCommittee(ctx, makeInitialGASTransferToCommitteePrm{ + logger: prm.Logger, + blockchain: prm.Blockchain, + monitor: monitor, + committee: committee, + localAcc: prm.LocalAccount, + validatorMultiSigAcc: prm.ValidatorMultiSigAccount, + tryTransfer: localAccCommitteeIndex == 0, + }) + if err != nil { + return fmt.Errorf("initial transfer funds to the committee: %w", err) + } - err = makeInitialTransferToCommittee(ctx, makeInitialGASTransferToCommitteePrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - monitor: monitor, - committee: committee, - localAcc: prm.LocalAccount, - validatorMultiSigAcc: prm.ValidatorMultiSigAccount, - tryTransfer: localAccCommitteeIndex == 0, - }) - if err != nil { - return fmt.Errorf("initial transfer funds to the committee: %w", err) + prm.Logger.Info("initial transfer to the committee successfully done") } - prm.Logger.Info("initial transfer to the committee successfully done") + if !rolesAreSet { + prm.Logger.Info("initializing NeoFS Alphabet...") - prm.Logger.Info("initializing NeoFS Alphabet...") + err = designateNeoFSAlphabet(ctx, initAlphabetPrm{ + logger: prm.Logger, + blockchain: prm.Blockchain, + monitor: monitor, + committee: committee, + localAcc: prm.LocalAccount, + }) + if err != nil { + return fmt.Errorf("init NeoFS Alphabet: %w", err) + } - err = designateNeoFSAlphabet(ctx, initAlphabetPrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - monitor: monitor, - committee: committee, - localAcc: prm.LocalAccount, - }) - if err != nil { - return fmt.Errorf("init NeoFS Alphabet: %w", err) + prm.Logger.Info("NeoFS Alphabet successfully initialized") + } else { + prm.Logger.Debug("alphabet roles are already set") } - prm.Logger.Info("NeoFS Alphabet successfully initialized") - syncPrm := syncNeoFSContractPrm{ logger: prm.Logger, blockchain: prm.Blockchain, @@ -645,3 +684,49 @@ func neoFSRuntimeTransactionModifier(getBlockchainHeight func() uint32) actor.Tr return nil } } + +func checkCommitteeRoles(logger *zap.Logger, b Blockchain, m *blockchainMonitor, committee keys.PublicKeys) (bool, error) { + currHeight := m.currentHeight() + + roleContract := rolemgmt.NewReader(invoker.New(b, nil)) + + currNotaries, err := roleContract.GetDesignatedByRole(noderoles.P2PNotary, currHeight) + if err != nil { + return false, fmt.Errorf("reading notary role: %w", err) + } + sort.Sort(currNotaries) + if !keysAreEqual(currNotaries, committee) { + logger.Debug("notaries and committee mismatch", + zap.Stringers("notaries", currNotaries), zap.Stringers("committee", committee)) + + return false, nil + } + + currAlphas, err := roleContract.GetDesignatedByRole(noderoles.NeoFSAlphabet, currHeight) + if err != nil { + return false, fmt.Errorf("reading alphabet role: %w", err) + } + sort.Sort(currAlphas) + if !keysAreEqual(currAlphas, committee) { + logger.Debug("alphabet and committee mismatch", + zap.Stringers("alphabet", currAlphas), zap.Stringers("committee", committee)) + + return false, nil + } + + return true, nil +} + +func keysAreEqual(a, b keys.PublicKeys) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if !a[i].Equal(b[i]) { + return false + } + } + + return true +} From 13e96843b566617c52df7b2c808a6daa699bebfe Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Thu, 27 Jun 2024 01:18:26 +0300 Subject: [PATCH 2/2] deploy: share roles check code Signed-off-by: Pavel Karpy --- deploy/alphabet.go | 15 +++---------- deploy/deploy.go | 56 +++++++++++++++++++--------------------------- deploy/notary.go | 15 +++---------- 3 files changed, 29 insertions(+), 57 deletions(-) diff --git a/deploy/alphabet.go b/deploy/alphabet.go index 154eb512..8b429740 100644 --- a/deploy/alphabet.go +++ b/deploy/alphabet.go @@ -55,22 +55,13 @@ func designateNeoFSAlphabet(ctx context.Context, prm initAlphabetPrm) error { prm.logger.Info("checking NeoFS Alphabet role of the committee members...") - accsWithAlphabetRole, err := roleContract.GetDesignatedByRole(noderoles.NeoFSAlphabet, prm.monitor.currentHeight()) + ok, err := checkRole(noderoles.NeoFSAlphabet, &roleContract.ContractReader, prm.monitor, prm.committee) if err != nil { - prm.logger.Error("failed to check role of the committee, will try again later", zap.Error(err)) + prm.logger.Error("failed to check NeoFS Alphabet role of the committee, will try again later", zap.Error(err)) continue } - someoneWithoutRole := len(accsWithAlphabetRole) < len(prm.committee) - if !someoneWithoutRole { - for i := range prm.committee { - if !accsWithAlphabetRole.Contains(prm.committee[i]) { - someoneWithoutRole = true - break - } - } - } - if !someoneWithoutRole { + if ok { prm.logger.Info("all committee members have a NeoFS Alphabet role") return nil } diff --git a/deploy/deploy.go b/deploy/deploy.go index a326b900..2408fa01 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -225,12 +225,12 @@ func Deploy(ctx context.Context, prm Prm) error { return fmt.Errorf("init blockchain monitor: %w", err) } - rolesAreSet, err := checkCommitteeRoles(prm.Logger, prm.Blockchain, monitor, committee) + notaryRolesSet, alphaRolesSet, err := checkCommitteeRoles(prm.Blockchain, monitor, committee) if err != nil { return fmt.Errorf("pre-check committee roles: %w", err) } - transfersDone := rolesAreSet // currently the same - if rolesAreSet { + transfersDone := notaryRolesSet // currently the same + if notaryRolesSet { // if it is possible to transfer GAS before any network settings, // it should be done as it speeds up expensive operations @@ -277,7 +277,7 @@ func Deploy(ctx context.Context, prm Prm) error { prm.Logger.Info("NNS contract successfully initialized on the chain", zap.Stringer("address", nnsOnChainAddress)) - if !rolesAreSet { + if !notaryRolesSet { prm.Logger.Info("enable Notary service for the committee...") err = enableNotary(ctx, enableNotaryPrm{ @@ -318,7 +318,7 @@ func Deploy(ctx context.Context, prm Prm) error { prm.Logger.Info("initial transfer to the committee successfully done") } - if !rolesAreSet { + if !alphaRolesSet { prm.Logger.Info("initializing NeoFS Alphabet...") err = designateNeoFSAlphabet(ctx, initAlphabetPrm{ @@ -685,48 +685,38 @@ func neoFSRuntimeTransactionModifier(getBlockchainHeight func() uint32) actor.Tr } } -func checkCommitteeRoles(logger *zap.Logger, b Blockchain, m *blockchainMonitor, committee keys.PublicKeys) (bool, error) { - currHeight := m.currentHeight() - +// first is for notary, second is for alphabet. +func checkCommitteeRoles(b Blockchain, m *blockchainMonitor, committee keys.PublicKeys) (bool, bool, error) { roleContract := rolemgmt.NewReader(invoker.New(b, nil)) - currNotaries, err := roleContract.GetDesignatedByRole(noderoles.P2PNotary, currHeight) + notaryRole, err := checkRole(noderoles.P2PNotary, roleContract, m, committee) if err != nil { - return false, fmt.Errorf("reading notary role: %w", err) + return false, false, fmt.Errorf("%s role check: %w", noderoles.P2PNotary, err) } - sort.Sort(currNotaries) - if !keysAreEqual(currNotaries, committee) { - logger.Debug("notaries and committee mismatch", - zap.Stringers("notaries", currNotaries), zap.Stringers("committee", committee)) - return false, nil + alphaRole, err := checkRole(noderoles.NeoFSAlphabet, roleContract, m, committee) + if err != nil { + return false, false, fmt.Errorf("%s role check: %w", noderoles.NeoFSAlphabet, err) } - currAlphas, err := roleContract.GetDesignatedByRole(noderoles.NeoFSAlphabet, currHeight) + return notaryRole, alphaRole, nil +} + +func checkRole(role noderoles.Role, roleContract *rolemgmt.ContractReader, m *blockchainMonitor, committee keys.PublicKeys) (bool, error) { + currentRoles, err := roleContract.GetDesignatedByRole(role, m.currentHeight()) if err != nil { - return false, fmt.Errorf("reading alphabet role: %w", err) + return false, fmt.Errorf("reading role: %w", err) } - sort.Sort(currAlphas) - if !keysAreEqual(currAlphas, committee) { - logger.Debug("alphabet and committee mismatch", - zap.Stringers("alphabet", currAlphas), zap.Stringers("committee", committee)) + if len(currentRoles) < len(committee) { return false, nil } - return true, nil -} - -func keysAreEqual(a, b keys.PublicKeys) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if !a[i].Equal(b[i]) { - return false + for i := range committee { + if !currentRoles.Contains(committee[i]) { + return false, nil } } - return true + return true, nil } diff --git a/deploy/notary.go b/deploy/notary.go index 815522f3..515a569c 100644 --- a/deploy/notary.go +++ b/deploy/notary.go @@ -93,22 +93,13 @@ func enableNotary(ctx context.Context, prm enableNotaryPrm) error { prm.logger.Info("checking Notary role of the committee members...") - accsWithNotaryRole, err := roleContract.GetDesignatedByRole(noderoles.P2PNotary, prm.monitor.currentHeight()) + ok, err := checkRole(noderoles.P2PNotary, roleContract, prm.monitor, prm.committee) if err != nil { - prm.logger.Error("failed to check role of the committee, will try again later", zap.Error(err)) + prm.logger.Error("failed to check Notary role of the committee, will try again later", zap.Error(err)) continue } - someoneWithoutNotaryRole := len(accsWithNotaryRole) < len(prm.committee) - if !someoneWithoutNotaryRole { - for i := range prm.committee { - if !accsWithNotaryRole.Contains(prm.committee[i]) { - someoneWithoutNotaryRole = true - break - } - } - } - if !someoneWithoutNotaryRole { + if ok { prm.logger.Info("all committee members have a Notary role") return nil }