From 06dcd7e332e6d47be344e07308e5ff3f644ed2b6 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 15 Nov 2023 18:41:53 +0100 Subject: [PATCH] sidechain/deploy: Do not register/use a committee contract group Starting from c08e2b5159918a41bdabea8b3523814e8556b88b (#2625), there is no need to maintain committee group and its private key because CustomGroups witness scope is no longer used. Completely purge committee group from NeoFS Sidechain auto-deployment procedure. As a result, contract manifests are no longer modified. Closes #2642. Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 1 + pkg/innerring/deploy.go | 77 ------ pkg/innerring/deploy_test.go | 66 ----- pkg/innerring/innerring.go | 2 - pkg/morph/deploy/contracts.go | 8 +- pkg/morph/deploy/deploy.go | 98 ++------ pkg/morph/deploy/group.go | 428 --------------------------------- pkg/morph/deploy/group_test.go | 25 -- pkg/morph/deploy/nns.go | 37 +-- pkg/morph/deploy/util.go | 28 --- 10 files changed, 21 insertions(+), 749 deletions(-) delete mode 100644 pkg/innerring/deploy_test.go delete mode 100644 pkg/morph/deploy/group.go delete mode 100644 pkg/morph/deploy/group_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1339779b87..1df87c6bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Changelog for NeoFS Node - Transaction witness scope no longer uses CustomGroups relying on more strict Rules now (#2619) - New optimized SDK version is integrated (#2622) - IR uses internal LOCODE DB from Go package (#2610) +- Contract group for the committee is no longer registered/used in the Sidechain auto-deployment (#2642) ### Removed - deprecated `no-precheck` flag of `neofs-cli container set-eacl` (#2496) diff --git a/pkg/innerring/deploy.go b/pkg/innerring/deploy.go index ab4caf270d..698f230dd8 100644 --- a/pkg/innerring/deploy.go +++ b/pkg/innerring/deploy.go @@ -1,17 +1,13 @@ package innerring import ( - "encoding/json" "fmt" "math" "sync" - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neofs-node/pkg/morph/client" "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" "github.com/nspcc-dev/neofs-node/pkg/morph/deploy" - "github.com/nspcc-dev/neofs-node/pkg/util/state" ) type neoFSSidechain struct { @@ -80,76 +76,3 @@ func (x *neoFSSidechain) CurrentState() (deploy.NeoFSState, error) { return res, nil } - -type sidechainKeyStorage struct { - persistentStorage *state.PersistentStorage -} - -func newSidechainKeyStorage(persistentStorage *state.PersistentStorage) *sidechainKeyStorage { - return &sidechainKeyStorage{ - persistentStorage: persistentStorage, - } -} - -var committeeGroupKey = []byte("committeeGroupKey") - -// GetPersistedPrivateKey reads persisted private key from the underlying -// storage. If key is missing, it's randomized and saved first. -func (x *sidechainKeyStorage) GetPersistedPrivateKey() (*keys.PrivateKey, error) { - b, err := x.persistentStorage.Bytes(committeeGroupKey) - if err != nil { - return nil, fmt.Errorf("read persistent storage: %w", err) - } - - const password = "" - - if b != nil { - var wlt wallet.Wallet - - err = json.Unmarshal(b, &wlt) - if err != nil { - return nil, fmt.Errorf("decode persisted NEO wallet from JSON: %w", err) - } - - if len(wlt.Accounts) != 1 { - return nil, fmt.Errorf("unexpected number of accounts in the persisted NEO wallet: %d instead of 1", len(wlt.Accounts)) - } - - err = wlt.Accounts[0].Decrypt(password, keys.NEP2ScryptParams()) - if err != nil { - return nil, fmt.Errorf("unlock 1st NEO account of the persisted NEO wallet: %w", err) - } - - return wlt.Accounts[0].PrivateKey(), nil - } - - acc, err := wallet.NewAccount() - if err != nil { - return nil, fmt.Errorf("generate random NEO account: %w", err) - } - - scryptPrm := keys.NEP2ScryptParams() - - err = acc.Encrypt(password, scryptPrm) - if err != nil { - return nil, fmt.Errorf("protect NEO account with password: %w", err) - } - - wlt := wallet.Wallet{ - Version: "1.0", // copy-paste from wallet package - Accounts: []*wallet.Account{acc}, - Scrypt: scryptPrm, - } - - jWallet, err := json.Marshal(wlt) - if err != nil { - return nil, fmt.Errorf("encode NEO wallet with randomized NEO account into JSON: %w", err) - } - - err = x.persistentStorage.SetBytes(committeeGroupKey, jWallet) - if err != nil { - return nil, fmt.Errorf("save generated key in the persistent storage: %w", err) - } - - return acc.PrivateKey(), nil -} diff --git a/pkg/innerring/deploy_test.go b/pkg/innerring/deploy_test.go deleted file mode 100644 index a47e3f439e..0000000000 --- a/pkg/innerring/deploy_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package innerring - -import ( - "encoding/json" - "path/filepath" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/wallet" - "github.com/nspcc-dev/neofs-node/pkg/util/state" - "github.com/stretchr/testify/require" -) - -func newTestPersistentStorage(tb testing.TB) *state.PersistentStorage { - ps, err := state.NewPersistentStorage(filepath.Join(tb.TempDir(), "storage")) - require.NoError(tb, err) - - tb.Cleanup(func() { - _ = ps.Close() - }) - - return ps -} - -func TestSidechainKeyStorage_GetPersistedPrivateKey(t *testing.T) { - testPersistedKey := func(tb testing.TB, ks *sidechainKeyStorage, persistedKey *keys.PrivateKey) { - key, err := ks.GetPersistedPrivateKey() - require.NoError(tb, err) - require.Equal(tb, persistedKey, key) - } - - t.Run("fresh", func(t *testing.T) { - ks := newSidechainKeyStorage(newTestPersistentStorage(t)) - - initKey, err := ks.GetPersistedPrivateKey() - require.NoError(t, err) - require.NotNil(t, initKey) - - testPersistedKey(t, ks, initKey) - }) - - t.Run("preset", func(t *testing.T) { - ps := newTestPersistentStorage(t) - - acc, err := wallet.NewAccount() - require.NoError(t, err) - - err = acc.Encrypt("", keys.NEP2ScryptParams()) - require.NoError(t, err) - - jWallet, err := json.Marshal(wallet.Wallet{ - Version: "1.0", - Accounts: []*wallet.Account{acc}, - Scrypt: keys.ScryptParams{}, - Extra: wallet.Extra{}, - }) - require.NoError(t, err) - - err = ps.SetBytes(committeeGroupKey, jWallet) - require.NoError(t, err) - - ks := newSidechainKeyStorage(ps) - - testPersistedKey(t, ks, acc.PrivateKey()) - }) -} diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index c1703ad919..0f2d5c8ca3 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -460,14 +460,12 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- log.Info("auto-deployment configured, initializing Sidechain...") sidechain := newNeoFSSidechain(server.morphClient) - sidechainKeyStorage := newSidechainKeyStorage(server.persistate) var deployPrm deploy.Prm deployPrm.Logger = server.log deployPrm.Blockchain = wsClient deployPrm.LocalAccount = singleAcc deployPrm.ValidatorMultiSigAccount = consensusAcc - deployPrm.KeyStorage = sidechainKeyStorage deployPrm.NeoFS = sidechain nnsCfg, err := parseNNSConfig(cfg) diff --git a/pkg/morph/deploy/contracts.go b/pkg/morph/deploy/contracts.go index 80a29c4fa9..5e6ca4375b 100644 --- a/pkg/morph/deploy/contracts.go +++ b/pkg/morph/deploy/contracts.go @@ -22,8 +22,7 @@ import ( // various common methods of the NeoFS contracts. const ( - methodUpdate = "update" - methodVersion = "version" + methodUpdate = "update" ) // syncNeoFSContractPrm groups parameters of syncNeoFSContract. @@ -43,8 +42,7 @@ type syncNeoFSContractPrm struct { nnsContract util.Uint160 systemEmail string - committee keys.PublicKeys - committeeGroupKey *keys.PrivateKey + committee keys.PublicKeys // with localAcc signer only simpleLocalActor *actor.Actor @@ -129,8 +127,6 @@ func syncNeoFSContract(ctx context.Context, prm syncNeoFSContractPrm) (util.Uint ctx, cancel := context.WithCancel(ctx) defer cancel() - setGroupInManifest(&prm.localManifest, prm.localNEF, prm.committeeGroupKey, prm.localAcc.ScriptHash()) - var contractDeployer interface { Sender() util.Uint160 } diff --git a/pkg/morph/deploy/deploy.go b/pkg/morph/deploy/deploy.go index f2891c8df2..14815b9202 100644 --- a/pkg/morph/deploy/deploy.go +++ b/pkg/morph/deploy/deploy.go @@ -65,14 +65,6 @@ type Blockchain interface { Unsubscribe(id string) error } -// KeyStorage represents storage of the private keys. -type KeyStorage interface { - // GetPersistedPrivateKey returns singleton private key persisted in the - // storage. GetPersistedPrivateKey randomizes the key initially. All subsequent - // successful calls return the same key. - GetPersistedPrivateKey() (*keys.PrivateKey, error) -} - // NeoFSState groups information about NeoFS network state processed by Deploy. type NeoFSState struct { // Current NeoFS epoch. @@ -157,9 +149,6 @@ type Prm struct { // participants (must be unlocked). ValidatorMultiSigAccount *wallet.Account - // Storage for single committee group key. - KeyStorage KeyStorage - // Running NeoFS network for which deployment procedure is performed. NeoFS NeoFS @@ -187,10 +176,9 @@ type Prm struct { // 1. NNS contract deployment // 2. launch of a notary service for the committee // 3. initial GAS distribution between committee members -// 4. committee group initialization -// 5. Alphabet initialization (incl. registration as candidates to validators) -// 6. deployment/update of the NeoFS system contracts -// 7. distribution of all available NEO between the Alphabet contracts +// 4. Alphabet initialization (incl. registration as candidates to validators) +// 5. deployment/update of the NeoFS system contracts +// 6. distribution of all available NEO between the Alphabet contracts // // See project documentation for details. func Deploy(ctx context.Context, prm Prm) error { @@ -242,39 +230,20 @@ func Deploy(ctx context.Context, prm Prm) error { defer monitor.stop() deployNNSPrm := deployNNSContractPrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - monitor: monitor, - localAcc: prm.LocalAccount, - localNEF: prm.NNS.Common.NEF, - localManifest: prm.NNS.Common.Manifest, - systemEmail: prm.NNS.SystemEmail, - initCommitteeGroupKey: nil, // set below + logger: prm.Logger, + blockchain: prm.Blockchain, + monitor: monitor, + localAcc: prm.LocalAccount, + localNEF: prm.NNS.Common.NEF, + localManifest: prm.NNS.Common.Manifest, + systemEmail: prm.NNS.SystemEmail, + tryDeploy: localAccCommitteeIndex == 0, // see below } // if local node is the first committee member (Az) => deploy NNS contract, - // otherwise just wait - if localAccCommitteeIndex == 0 { - // Why such a centralized approach? There is a need to initialize committee - // contract group and share its private key between all committee members (the - // latter is done in the current procedure next). Currently, there is no - // convenient Neo service for this, and we don't want to use anything but - // blockchain, so the key is distributed through domain NNS records. However, - // then the chicken-and-egg problem pops up: committee group must be also set - // for the NNS contract. To set the group, you need to know the contract hash in - // advance, and it is a function from the sender of the deployment transaction. - // Summing up all these statements, we come to the conclusion that the one who - // deploys the contract creates the group key, and he shares it among the other - // members. Technically any committee member could deploy NNS contract, but for - // the sake of simplicity, this is a fixed node. This makes the procedure even - // more centralized, however, in practice, at the start of the network, all - // members are expected to be healthy and active. - // - // Note that manifest can't be changed w/o NEF change, so it's impossible to set - // committee group dynamically right after deployment. See - // https://github.com/nspcc-dev/neofs-contract/issues/340 - deployNNSPrm.initCommitteeGroupKey = prm.KeyStorage.GetPersistedPrivateKey - } + // otherwise just wait. This will avoid duplication of contracts. This also + // makes the procedure more centralized, however, in practice, at the start of + // the network, all members are expected to be healthy and active. prm.Logger.Info("initializing NNS contract on the chain...") @@ -333,43 +302,6 @@ func Deploy(ctx context.Context, prm Prm) error { prm.Logger.Info("initial transfer to the committee successfully done") - prm.Logger.Info("initializing committee group for contract management...") - - committeeGroupKey, err := initCommitteeGroup(ctx, initCommitteeGroupPrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - monitor: monitor, - nnsOnChainAddress: nnsOnChainAddress, - systemEmail: prm.NNS.SystemEmail, - committee: committee, - localAcc: prm.LocalAccount, - localAccCommitteeIndex: localAccCommitteeIndex, - keyStorage: prm.KeyStorage, - }) - if err != nil { - return fmt.Errorf("init committee group: %w", err) - } - - prm.Logger.Info("committee group successfully initialized", zap.Stringer("public key", committeeGroupKey.PublicKey())) - - prm.Logger.Info("registering committee group in the NNS...") - - err = registerCommitteeGroupInNNS(ctx, registerCommitteeGroupInNNSPrm{ - logger: prm.Logger, - blockchain: prm.Blockchain, - monitor: monitor, - nnsContract: nnsOnChainAddress, - systemEmail: prm.NNS.SystemEmail, - localAcc: prm.LocalAccount, - committee: committee, - committeeGroupKey: committeeGroupKey, - }) - if err != nil { - return fmt.Errorf("regsiter committee group in the NNS: %w", err) - } - - prm.Logger.Info("committee group successfully registered in the NNS") - prm.Logger.Info("initializing NeoFS Alphabet...") err = initAlphabet(ctx, initAlphabetPrm{ @@ -394,7 +326,6 @@ func Deploy(ctx context.Context, prm Prm) error { nnsContract: nnsOnChainAddress, systemEmail: prm.NNS.SystemEmail, committee: committee, - committeeGroupKey: committeeGroupKey, simpleLocalActor: simpleLocalActor, committeeLocalActor: committeeLocalActor, } @@ -509,7 +440,6 @@ func Deploy(ctx context.Context, prm Prm) error { localManifest: prm.NNS.Common.Manifest, systemEmail: prm.NNS.SystemEmail, committee: committee, - committeeGroupKey: committeeGroupKey, buildExtraUpdateArgs: noExtraUpdateArgs, proxyContract: proxyContractAddress, }) diff --git a/pkg/morph/deploy/group.go b/pkg/morph/deploy/group.go deleted file mode 100644 index 4df8522265..0000000000 --- a/pkg/morph/deploy/group.go +++ /dev/null @@ -1,428 +0,0 @@ -package deploy - -import ( - "context" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/neorpc" - "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/nns" - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/wallet" - "go.uber.org/zap" -) - -// initCommitteeGroupPrm groups parameters of committee group initialization. -type initCommitteeGroupPrm struct { - logger *zap.Logger - - blockchain Blockchain - - // based on blockchain - monitor *blockchainMonitor - - nnsOnChainAddress util.Uint160 - systemEmail string - - committee keys.PublicKeys - localAcc *wallet.Account - localAccCommitteeIndex int - - keyStorage KeyStorage -} - -// initCommitteeGroup initializes committee group and returns corresponding private key. -func initCommitteeGroup(ctx context.Context, prm initCommitteeGroupPrm) (*keys.PrivateKey, error) { - // wrap the parent context into the context of the current function so that - // transaction wait routines do not leak - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - inv := invoker.New(prm.blockchain, nil) - const leaderCommitteeIndex = 0 - var committeeGroupKey *keys.PrivateKey - var leaderTick func() - -upperLoop: - for ; ; prm.monitor.waitForNextBlock(ctx) { - select { - case <-ctx.Done(): - return nil, fmt.Errorf("wait for committee group key to be distributed: %w", ctx.Err()) - default: - } - - prm.logger.Info("checking domains with shared committee group key...") - - nShared := 0 - - for i := range prm.committee { - domain := committeeGroupDomainForMember(i) - - rec, err := lookupNNSDomainRecord(inv, prm.nnsOnChainAddress, domain) - if err != nil { - if errors.Is(err, errMissingDomain) || errors.Is(err, errMissingDomainRecord) { - prm.logger.Info("NNS record with committee group key shared with the committee member is still missing, waiting...", - zap.String("domain", domain)) - } else { - prm.logger.Error("failed to lookup NNS domain record, will try again later", - zap.String("domain", domain), zap.Error(err)) - } - - continue - } - - if committeeGroupKey == nil && i == prm.localAccCommitteeIndex { - committeeGroupKey, err = decryptSharedPrivateKey(rec, prm.committee[leaderCommitteeIndex], prm.localAcc.PrivateKey()) - if err != nil { - prm.logger.Error("failed to decrypt shared committee group key, will wait for a background fix", - zap.String("domain", domain), zap.Error(err)) - continue upperLoop - } - } - - nShared++ - } - - if nShared == len(prm.committee) { - prm.logger.Info("committee group key is distributed between all committee members") - return committeeGroupKey, nil - } - - prm.logger.Info("not all committee members received the committee group key, distribution is needed", - zap.Int("need", len(prm.committee)), zap.Int("shared", nShared)) - - if prm.localAccCommitteeIndex != leaderCommitteeIndex { - prm.logger.Info("will wait for distribution from the leader") - continue - } - - var err error - - if committeeGroupKey == nil { - committeeGroupKey, err = prm.keyStorage.GetPersistedPrivateKey() - if err != nil { - prm.logger.Error("failed to init committee group key, will try again later", zap.Error(err)) - continue - } - } - - if leaderTick == nil { - leaderTick, err = initShareCommitteeGroupKeyAsLeaderTick(ctx, prm, committeeGroupKey) - if err != nil { - prm.logger.Error("failed to construct action sharing committee group key between committee members as leader, will try again later", - zap.Error(err)) - continue - } - } - - leaderTick() - } -} - -// initShareCommitteeGroupKeyAsLeaderTick returns a function that preserves -// context of the committee group key distribution by leading committee member -// between calls. -func initShareCommitteeGroupKeyAsLeaderTick(ctx context.Context, prm initCommitteeGroupPrm, committeeGroupKey *keys.PrivateKey) (func(), error) { - localActor, err := actor.NewSimple(prm.blockchain, prm.localAcc) - if err != nil { - return nil, fmt.Errorf("init transaction sender from local account: %w", err) - } - - invkr := invoker.New(prm.blockchain, nil) - - // multi-tick context - mDomainsToRegisterTxs := make(map[string]*transactionGroupMonitor, len(prm.committee)) - mDomainsToSetRecordTxs := make(map[string]*transactionGroupMonitor, len(prm.committee)) - - return func() { - prm.logger.Info("distributing committee group key between committee members using NNS...") - - for i := range prm.committee { - domain := committeeGroupDomainForMember(i) - l := prm.logger.With(zap.String("domain", domain), zap.Stringer("member", prm.committee[i])) - - l.Info("synchronizing committee group key with NNS domain record...") - - _, err := lookupNNSDomainRecord(invkr, prm.nnsOnChainAddress, domain) - if err != nil { - if errors.Is(err, errMissingDomain) { - l.Info("NNS domain is missing, registration is needed") - - txMonitor, ok := mDomainsToRegisterTxs[domain] - if ok { - if txMonitor.isPending() { - l.Info("previously sent transaction registering NNS domain is still pending, will wait for the outcome") - continue - } - } else { - txMonitor = newTransactionGroupMonitor(localActor) - mDomainsToRegisterTxs[domain] = txMonitor - } - - l.Info("sending new transaction registering domain in the NNS...") - - txID, vub, err := localActor.SendCall(prm.nnsOnChainAddress, methodNNSRegister, - domain, localActor.Sender(), prm.systemEmail, nnsRefresh, nnsRetry, nnsExpire, nnsMinimum) - if err != nil { - if errors.Is(err, neorpc.ErrInsufficientFunds) { - l.Info("not enough GAS to register domain in the NNS, will try again later") - } else { - l.Error("failed to send transaction registering domain in the NNS, will try again later", zap.Error(err)) - } - return - } - - l.Info("transaction registering domain in the NNS has been successfully sent, will wait for the outcome", - zap.Stringer("tx", txID), zap.Uint32("vub", vub)) - - txMonitor.trackPendingTransactionsAsync(ctx, vub, txID) - - continue - } else if !errors.Is(err, errMissingDomainRecord) { - l.Error("failed to lookup NNS domain record, will try again later", zap.Error(err)) - return - } - - l.Info("missing record of the NNS domain, needed to be set") - - txMonitor, ok := mDomainsToSetRecordTxs[domain] - if ok { - if txMonitor.isPending() { - l.Info("previously sent transaction setting NNS domain record is still pending, will wait for the outcome") - continue - } - } else { - txMonitor = newTransactionGroupMonitor(localActor) - mDomainsToSetRecordTxs[domain] = txMonitor - } - - l.Info("sharing encrypted committee group key with the committee member...") - - keyCipher, err := encryptSharedPrivateKey(committeeGroupKey, prm.localAcc.PrivateKey(), prm.committee[i]) - if err != nil { - l.Error("failed to encrypt committee group key to share with the committee member, will try again later", - zap.Error(err)) - continue - } - - l.Info("sending new transaction setting domain record in the NNS...") - - txID, vub, err := localActor.SendCall(prm.nnsOnChainAddress, methodNNSAddRecord, - domain, int64(nns.TXT), keyCipher) - if err != nil { - if errors.Is(err, neorpc.ErrInsufficientFunds) { - l.Info("not enough GAS to set NNS domain record, will try again later") - } else { - l.Error("failed to send transaction setting NNS domain record, will try again later", zap.Error(err)) - } - return - } - - l.Info("transaction setting NNS domain record has been successfully sent, will wait for the outcome", - zap.Stringer("tx", txID), zap.Uint32("vub", vub)) - - txMonitor.trackPendingTransactionsAsync(ctx, vub, txID) - - continue - } - - l.Info("committee group key is shared with the committee member in NNS domain record") - } - }, nil -} - -// encryptSharedPrivateKey encrypts private key using provided coder's private -// key to be decrypted using decoder's private key. Inverse operation to -// decryptSharedPrivateKey. -func encryptSharedPrivateKey(sharedPrivKey, coderPrivKey *keys.PrivateKey, decoderPubKey *keys.PublicKey) (string, error) { - sharedSecret, err := calculateSharedSecret(coderPrivKey, decoderPubKey) - if err != nil { - return "", fmt.Errorf("calculate shared secret: %w", err) - } - - cipherBlock, err := aes.NewCipher(sharedSecret) - if err != nil { - return "", fmt.Errorf("create AES cipher block: %w", err) - } - - cipherMode, err := cipher.NewGCM(cipherBlock) - if err != nil { - return "", fmt.Errorf("wrap cipher block in GCM: %w", err) - } - - nonce := make([]byte, cipherMode.NonceSize()) - - _, err = rand.Reader.Read(nonce) - if err != nil { - return "", fmt.Errorf("generate nonce using crypto randomizer: %w", err) - } - - bKeyCipher, err := cipherMode.Seal(nonce, nonce, sharedPrivKey.Bytes(), nil), nil - if err != nil { - return "", fmt.Errorf("encrypt key binary: %w", err) - } - - return base64.StdEncoding.EncodeToString(bKeyCipher), nil -} - -// decryptSharedPrivateKey decrypts cipher of the private key encrypted by -// coder's private key. Inverse operation to encryptSharedPrivateKey. -func decryptSharedPrivateKey(sharedPrivKeyCipher string, coderPubKey *keys.PublicKey, decoderPrivKey *keys.PrivateKey) (*keys.PrivateKey, error) { - bKeyCipher, err := base64.StdEncoding.DecodeString(sharedPrivKeyCipher) - if err != nil { - return nil, fmt.Errorf("decode key cipher from base64: %w", err) - } - - sharedSecret, err := calculateSharedSecret(decoderPrivKey, coderPubKey) - if err != nil { - return nil, fmt.Errorf("calculate shared secret: %w", err) - } - - cipherBlock, err := aes.NewCipher(sharedSecret) - if err != nil { - return nil, fmt.Errorf("create AES cipher block: %w", err) - } - - cipherMode, err := cipher.NewGCM(cipherBlock) - if err != nil { - return nil, fmt.Errorf("wrap cipher block in GCM: %w", err) - } - - nonceSize := cipherMode.NonceSize() - if len(sharedPrivKeyCipher) < nonceSize { - return nil, fmt.Errorf("too short cipher %d", len(sharedPrivKeyCipher)) - } - - bSharedPrivKey, err := cipherMode.Open(nil, bKeyCipher[:nonceSize], bKeyCipher[nonceSize:], nil) - if err != nil { - return nil, fmt.Errorf("decrypt cipher: %w", err) - } - - sharedPrivKey, err := keys.NewPrivateKeyFromBytes(bSharedPrivKey) - if err != nil { - return nil, fmt.Errorf("decode key binary: %w", err) - } - - return sharedPrivKey, nil -} - -func calculateSharedSecret(localPrivKey *keys.PrivateKey, remotePubKey *keys.PublicKey) ([]byte, error) { - // this commented code will start working from go1.20 (it's fully compatible - // with current implementation) - // - // localPrivKeyECDH, err := localPrivKey.ECDH() - // if err != nil { - // return nil, fmt.Errorf("local private key to ECDH key: %w", err) - // } - // - // remotePubKeyECDH, err := (*ecdsa.PublicKey)(remotePubKey).ECDH() - // if err != nil { - // return nil, fmt.Errorf("remote public key to ECDH key: %w", err) - // } - // - // sharedSecret, err := localPrivKeyECDH.ECDH(remotePubKeyECDH) - // if err != nil { - // return nil, fmt.Errorf("ECDH exchange: %w", err) - // } - // - // return sharedSecret, nil - - x, _ := localPrivKey.ScalarMult(remotePubKey.X, remotePubKey.Y, localPrivKey.D.Bytes()) - return x.Bytes(), nil -} - -// registerCommitteeGroupInNNSPrm groups parameters of committee group's -// register in the NNS. -type registerCommitteeGroupInNNSPrm struct { - logger *zap.Logger - - blockchain Blockchain - - // based on blockchain - monitor *blockchainMonitor - - nnsContract util.Uint160 - systemEmail string - - localAcc *wallet.Account - - committee keys.PublicKeys - committeeGroupKey *keys.PrivateKey -} - -// registerCommitteeGroupInNNS registers committee group in the NNS. -func registerCommitteeGroupInNNS(ctx context.Context, prm registerCommitteeGroupInNNSPrm) error { - localActor, err := actor.NewSimple(prm.blockchain, prm.localAcc) - if err != nil { - return fmt.Errorf("init transaction sender from local account: %w", err) - } - - committeeActor, err := newCommitteeNotaryActor(prm.blockchain, prm.localAcc, prm.committee) - if err != nil { - return fmt.Errorf("create Notary service client sending transactions to be signed by the committee: %w", err) - } - - // wrap the parent context into the context of the current function so that - // transaction wait routines do not leak - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - inv := invoker.New(prm.blockchain, nil) - domain := domainCommitteeGroup + "." + domainContractAddresses - l := prm.logger.With(zap.String("domain", domain)) - committeeGroupPubKey := prm.committeeGroupKey.PublicKey() - setContractRecordPrm := setNeoFSContractDomainRecordPrm{ - logger: l, - setRecordTxMonitor: newTransactionGroupMonitor(localActor), - registerTLDTxMonitor: newTransactionGroupMonitor(localActor), - nnsContract: prm.nnsContract, - systemEmail: prm.systemEmail, - localActor: localActor, - committeeActor: committeeActor, - domain: domain, - record: hex.EncodeToString(committeeGroupPubKey.Bytes()), - } - - for ; ; prm.monitor.waitForNextBlock(ctx) { - select { - case <-ctx.Done(): - return fmt.Errorf("wait for committee group key to be registered in the NNS: %w", ctx.Err()) - default: - } - - rec, err := lookupNNSDomainRecord(inv, prm.nnsContract, domain) - if err != nil { - if !errors.Is(err, errMissingDomain) && !errors.Is(err, errMissingDomainRecord) { - l.Error("failed to lookup NNS domain record, will try again later") - continue - } - - setNeoFSContractDomainRecord(ctx, setContractRecordPrm) - - continue - } - - pubKeyInNNS, err := keys.NewPublicKeyFromString(rec) - if err != nil { - l.Error("failed to parse public key of the committee group, will wait for a background fix", - zap.Error(err)) - continue - } - - if !pubKeyInNNS.Equal(committeeGroupPubKey) { - l.Error("public key of the committee group from the NNS differs with the local one, will wait for a background fix", - zap.Stringer("nns", pubKeyInNNS), zap.Stringer("local", committeeGroupPubKey)) - continue - } - - return nil - } -} diff --git a/pkg/morph/deploy/group_test.go b/pkg/morph/deploy/group_test.go deleted file mode 100644 index 9a30d33099..0000000000 --- a/pkg/morph/deploy/group_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package deploy - -import ( - "testing" - - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/stretchr/testify/require" -) - -func TestKeySharing(t *testing.T) { - coderKey, err := keys.NewPrivateKey() - require.NoError(t, err) - decoderKey, err := keys.NewPrivateKey() - require.NoError(t, err) - sharedKey, err := keys.NewPrivateKey() - require.NoError(t, err) - - cipher, err := encryptSharedPrivateKey(sharedKey, coderKey, decoderKey.PublicKey()) - require.NoError(t, err) - - restoredKey, err := decryptSharedPrivateKey(cipher, coderKey.PublicKey(), decoderKey) - require.NoError(t, err) - - require.Equal(t, sharedKey, restoredKey) -} diff --git a/pkg/morph/deploy/nns.go b/pkg/morph/deploy/nns.go index df852bb5f2..b907ddb3a7 100644 --- a/pkg/morph/deploy/nns.go +++ b/pkg/morph/deploy/nns.go @@ -40,8 +40,6 @@ const ( domainNetmap = "netmap" domainProxy = "proxy" domainReputation = "reputation" - - domainCommitteeGroup = "group" ) func calculateAlphabetContractAddressDomain(index int) string { @@ -56,10 +54,6 @@ func designateNotarySignatureDomainForMember(memberIndex int) string { return fmt.Sprintf("%s%d.%s", domainDesignateNotaryPrefix, memberIndex, domainBootstrap) } -func committeeGroupDomainForMember(memberIndex int) string { - return fmt.Sprintf("committee-group-%d.%s", memberIndex, domainBootstrap) -} - // various methods of the NeoFS NNS contract. const ( methodNNSRegister = "register" @@ -99,9 +93,7 @@ type deployNNSContractPrm struct { localManifest manifest.Manifest systemEmail string - // optional constructor of private key for the committee group. If set, it is - // used only when contract is missing. - initCommitteeGroupKey func() (*keys.PrivateKey, error) + tryDeploy bool } // initNNSContract synchronizes NNS contract with the chain and returns the @@ -115,7 +107,7 @@ type deployNNSContractPrm struct { // previously succeeded actions will be skipped, and execution will be continued // from the last failed stage. // -// If contract is missing and deployNNSContractPrm.initCommitteeGroupKey is provided, +// If contract is missing and deployNNSContractPrm.tryDeploy is set, // initNNSContract attempts to deploy local contract. func initNNSContract(ctx context.Context, prm deployNNSContractPrm) (res util.Uint160, err error) { localActor, err := actor.NewSimple(prm.blockchain, prm.localAcc) @@ -128,7 +120,6 @@ func initNNSContract(ctx context.Context, prm deployNNSContractPrm) (res util.Ui ctx, cancel := context.WithCancel(ctx) defer cancel() - var committeeGroupKey *keys.PrivateKey txMonitor := newTransactionGroupMonitor(localActor) managementContract := management.New(localActor) @@ -158,27 +149,13 @@ func initNNSContract(ctx context.Context, prm deployNNSContractPrm) (res util.Ui return stateOnChain.Hash, nil } - if prm.initCommitteeGroupKey == nil { + if !prm.tryDeploy { prm.logger.Info("NNS contract is missing on the chain but attempts to deploy are disabled, will wait for background deployment") continue } prm.logger.Info("NNS contract is missing on the chain, contract needs to be deployed") - if committeeGroupKey == nil { - prm.logger.Info("initializing private key for the committee group...") - - committeeGroupKey, err = prm.initCommitteeGroupKey() - if err != nil { - prm.logger.Error("failed to init committee group key, will try again later", zap.Error(err)) - continue - } - - setGroupInManifest(&prm.localManifest, prm.localNEF, committeeGroupKey, prm.localAcc.ScriptHash()) - - prm.logger.Info("private key of the committee group has been initialized", zap.Stringer("public key", committeeGroupKey.PublicKey())) - } - if txMonitor.isPending() { prm.logger.Info("previously sent transaction updating NNS contract is still pending, will wait for the outcome") continue @@ -262,8 +239,7 @@ type updateNNSContractPrm struct { localManifest manifest.Manifest systemEmail string - committee keys.PublicKeys - committeeGroupKey *keys.PrivateKey + committee keys.PublicKeys // constructor of extra arguments to be passed into method updating the // contract. If returns both nil, no data is passed (noExtraUpdateArgs may be @@ -280,9 +256,6 @@ type updateNNSContractPrm struct { // on-chain version is greater or equal to the local one, nothing happens. // Otherwise, transaction calling 'update' method is sent. // -// Local manifest is extended with committee group represented by the -// parameterized private key. -// // Function behaves similar to initNNSContract in terms of context. func updateNNSContract(ctx context.Context, prm updateNNSContractPrm) error { bLocalNEF, err := prm.localNEF.Bytes() @@ -335,8 +308,6 @@ func updateNNSContract(ctx context.Context, prm updateNNSContractPrm) error { continue } - setGroupInManifest(&prm.localManifest, prm.localNEF, prm.committeeGroupKey, prm.localAcc.ScriptHash()) - // we pre-check 'already updated' case via MakeCall in order to not potentially // wait for previously sent transaction to be expired (condition below) and // immediately succeed diff --git a/pkg/morph/deploy/util.go b/pkg/morph/deploy/util.go index bd01dab17e..8fd980f245 100644 --- a/pkg/morph/deploy/util.go +++ b/pkg/morph/deploy/util.go @@ -10,12 +10,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/neorpc" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" - "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" "github.com/nspcc-dev/neofs-contract/common" "go.uber.org/zap" @@ -29,31 +26,6 @@ func isErrTLDNotFound(err error) bool { return strings.Contains(err.Error(), "TLD not found") } -func setGroupInManifest(manif *manifest.Manifest, nefFile nef.File, groupPrivKey *keys.PrivateKey, deployerAcc util.Uint160) { - contractAddress := state.CreateContractHash(deployerAcc, nefFile.Checksum, manif.Name) - sig := groupPrivKey.Sign(contractAddress.BytesBE()) - groupPubKey := groupPrivKey.PublicKey() - - ind := -1 - - for i := range manif.Groups { - if manif.Groups[i].PublicKey.Equal(groupPubKey) { - ind = i - break - } - } - - if ind >= 0 { - manif.Groups[ind].Signature = sig - return - } - - manif.Groups = append(manif.Groups, manifest.Group{ - PublicKey: groupPubKey, - Signature: sig, - }) -} - // blockchainMonitor is a thin utility around Blockchain providing state // monitoring. type blockchainMonitor struct {