diff --git a/cmd/frostfs-adm/internal/modules/morph/config.go b/cmd/frostfs-adm/internal/modules/morph/config.go index 0d042d94..2d0f12af 100644 --- a/cmd/frostfs-adm/internal/modules/morph/config.go +++ b/cmd/frostfs-adm/internal/modules/morph/config.go @@ -85,7 +85,7 @@ func setConfigCmd(cmd *cobra.Command, args []string) error { return errors.New("empty config pairs") } - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("can't initialize context: %w", err) } @@ -119,12 +119,12 @@ func setConfigCmd(cmd *cobra.Command, args []string) error { } } - err = wCtx.sendConsensusTx(bw.Bytes()) + err = wCtx.SendConsensusTx(bw.Bytes()) if err != nil { return err } - return wCtx.awaitTx() + return wCtx.AwaitTx() } func parseConfigPair(kvStr string, force bool) (key string, val any, err error) { diff --git a/cmd/frostfs-adm/internal/modules/morph/container.go b/cmd/frostfs-adm/internal/modules/morph/container.go index 5400a51c..ab32dcab 100644 --- a/cmd/frostfs-adm/internal/modules/morph/container.go +++ b/cmd/frostfs-adm/internal/modules/morph/container.go @@ -193,11 +193,11 @@ func restoreContainers(cmd *cobra.Command, _ []string) error { return fmt.Errorf("invalid filename: %w", err) } - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return err } - defer wCtx.close() + defer wCtx.Close() containers, err := parseContainers(filename) if err != nil { @@ -219,10 +219,10 @@ func restoreContainers(cmd *cobra.Command, _ []string) error { return err } - return wCtx.awaitTx() + return wCtx.AwaitTx() } -func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd *cobra.Command, wCtx *initializeContext, ch util.Uint160) error { +func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd *cobra.Command, wCtx *InitializeContext, ch util.Uint160) error { bw := io.NewBufBinWriter() for _, cnt := range containers { hv := hash.Sha256(cnt.Value) @@ -246,7 +246,7 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd panic(bw.Err) } - if err := wCtx.sendConsensusTx(bw.Bytes()); err != nil { + if err := wCtx.SendConsensusTx(bw.Bytes()); err != nil { return err } } @@ -262,7 +262,7 @@ func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) { } } -func isContainerRestored(cmd *cobra.Command, wCtx *initializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) { +func isContainerRestored(cmd *cobra.Command, wCtx *InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) { emit.AppCall(bw.BinWriter, containerHash, "get", callflag.All, hashValue.BytesBE()) res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil) if err != nil { @@ -300,7 +300,7 @@ func parseContainers(filename string) ([]Container, error) { return containers, nil } -func fetchContainerContractHash(wCtx *initializeContext) (util.Uint160, error) { +func fetchContainerContractHash(wCtx *InitializeContext) (util.Uint160, error) { r := management.NewReader(wCtx.ReadOnlyInvoker) nnsCs, err := r.GetContractByID(1) if err != nil { diff --git a/cmd/frostfs-adm/internal/modules/morph/deploy.go b/cmd/frostfs-adm/internal/modules/morph/deploy.go index 85c207d7..7f11362e 100644 --- a/cmd/frostfs-adm/internal/modules/morph/deploy.go +++ b/cmd/frostfs-adm/internal/modules/morph/deploy.go @@ -60,11 +60,11 @@ func init() { func deployContractCmd(cmd *cobra.Command, args []string) error { v := viper.GetViper() - c, err := newInitializeContext(cmd, v) + c, err := NewInitializeContext(cmd, v) if err != nil { return fmt.Errorf("initialization error: %w", err) } - defer c.close() + defer c.Close() ctrPath, _ := cmd.Flags().GetString(contractPathFlag) ctrName, err := probeContractName(ctrPath) @@ -123,13 +123,13 @@ func deployContractCmd(cmd *cobra.Command, args []string) error { panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err)) } - if err := c.sendCommitteeTx(writer.Bytes(), false); err != nil { + if err := c.SendCommitteeTx(writer.Bytes(), false); err != nil { return err } - return c.awaitTx() + return c.AwaitTx() } -func registerNNS(nnsCs *state.Contract, c *initializeContext, zone string, domain string, cs *contractState, writer *io.BufBinWriter) error { +func registerNNS(nnsCs *state.Contract, c *InitializeContext, zone string, domain string, cs *util.ContractState, writer *io.BufBinWriter) error { bw := io.NewBufBinWriter() emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1}) emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All) @@ -139,7 +139,7 @@ func registerNNS(nnsCs *state.Contract, c *initializeContext, zone string, domai start := bw.Len() needRecord := false - ok, err := c.nnsRootRegistered(nnsCs.Hash, zone) + ok, err := c.NNSRootRegistered(nnsCs.Hash, zone) if err != nil { return err } else if !ok { @@ -155,7 +155,7 @@ func registerNNS(nnsCs *state.Contract, c *initializeContext, zone string, domai frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600)) emit.Opcodes(bw.BinWriter, opcode.ASSERT) } else { - s, ok, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain) + s, ok, err := c.NNSRegisterDomainScript(nnsCs.Hash, cs.Hash, domain) if err != nil { return err } diff --git a/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go b/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go index ce3585c6..79b82ee4 100644 --- a/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go +++ b/cmd/frostfs-adm/internal/modules/morph/dump_hashes.go @@ -54,7 +54,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error { irSize := 0 for ; irSize < lastGlagoliticLetter; irSize++ { - ok, err := nnsIsAvailable(c, cs.Hash, getAlphabetNNSDomain(irSize)) + ok, err := morphUtil.NNSIsAvailable(c, cs.Hash, getAlphabetNNSDomain(irSize)) if err != nil { return err } else if ok { diff --git a/cmd/frostfs-adm/internal/modules/morph/epoch.go b/cmd/frostfs-adm/internal/modules/morph/epoch.go index 7cb73e26..0fc25d01 100644 --- a/cmd/frostfs-adm/internal/modules/morph/epoch.go +++ b/cmd/frostfs-adm/internal/modules/morph/epoch.go @@ -17,7 +17,7 @@ import ( ) func forceNewEpochCmd(cmd *cobra.Command, _ []string) error { - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("can't to initialize context: %w", err) } @@ -38,8 +38,8 @@ func forceNewEpochCmd(cmd *cobra.Command, _ []string) error { return err } - if err = wCtx.sendConsensusTx(bw.Bytes()); err == nil { - err = wCtx.awaitTx() + if err = wCtx.SendConsensusTx(bw.Bytes()); err == nil { + err = wCtx.AwaitTx() } if err != nil && strings.Contains(err.Error(), "invalid epoch") { cmd.Println("Epoch has already ticked.") @@ -48,7 +48,7 @@ func forceNewEpochCmd(cmd *cobra.Command, _ []string) error { return err } -func emitNewEpochCall(bw *io.BufBinWriter, wCtx *initializeContext, nmHash util.Uint160) error { +func emitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160) error { curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch")) if err != nil { return errors.New("can't fetch current epoch from the netmap contract") diff --git a/cmd/frostfs-adm/internal/modules/morph/frostfsid.go b/cmd/frostfs-adm/internal/modules/morph/frostfsid.go index 0ad8fe76..4adde815 100644 --- a/cmd/frostfs-adm/internal/modules/morph/frostfsid.go +++ b/cmd/frostfs-adm/internal/modules/morph/frostfsid.go @@ -416,11 +416,11 @@ type frostfsidClient struct { bw *io.BufBinWriter contractHash util.Uint160 roCli *frostfsidclient.Client // client can be used only for waiting tx, parsing and forming method params - wCtx *initializeContext + wCtx *InitializeContext } func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) { - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return nil, fmt.Errorf("can't to initialize context: %w", err) } @@ -449,16 +449,16 @@ func (f *frostfsidClient) addCall(method string, args []any) { } func (f *frostfsidClient) sendWait() error { - if err := f.wCtx.sendConsensusTx(f.bw.Bytes()); err != nil { + if err := f.wCtx.SendConsensusTx(f.bw.Bytes()); err != nil { return err } f.bw.Reset() - return f.wCtx.awaitTx() + return f.wCtx.AwaitTx() } func (f *frostfsidClient) sendWaitRes() (*state.AppExecResult, error) { - if err := f.wCtx.sendConsensusTx(f.bw.Bytes()); err != nil { + if err := f.wCtx.SendConsensusTx(f.bw.Bytes()); err != nil { return nil, err } f.bw.Reset() diff --git a/cmd/frostfs-adm/internal/modules/morph/generate.go b/cmd/frostfs-adm/internal/modules/morph/generate.go index a7561b26..f8e155f5 100644 --- a/cmd/frostfs-adm/internal/modules/morph/generate.go +++ b/cmd/frostfs-adm/internal/modules/morph/generate.go @@ -195,7 +195,7 @@ func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error return err } - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return err } @@ -208,11 +208,11 @@ func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err) } - if err := wCtx.sendCommitteeTx(bw.Bytes(), false); err != nil { + if err := wCtx.SendCommitteeTx(bw.Bytes(), false); err != nil { return err } - return wCtx.awaitTx() + return wCtx.AwaitTx() } func parseGASAmount(s string) (fixedn.Fixed8, error) { diff --git a/cmd/frostfs-adm/internal/modules/morph/group.go b/cmd/frostfs-adm/internal/modules/morph/group.go index 3fdffd4e..0e6c511d 100644 --- a/cmd/frostfs-adm/internal/modules/morph/group.go +++ b/cmd/frostfs-adm/internal/modules/morph/group.go @@ -7,6 +7,7 @@ import ( "path/filepath" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config" + util2 "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" @@ -75,8 +76,8 @@ func openContractWallet(v *viper.Viper, cmd *cobra.Command, walletDir string) (* return w, nil } -func (c *initializeContext) addManifestGroup(h util.Uint160, cs *contractState) error { - priv := c.ContractWallet.Accounts[0].PrivateKey() +func addManifestGroup(cw *wallet.Wallet, h util.Uint160, cs *util2.ContractState) error { + priv := cw.Accounts[0].PrivateKey() pub := priv.PublicKey() sig := priv.Sign(h.BytesBE()) diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize.go b/cmd/frostfs-adm/internal/modules/morph/initialize.go index a432fbc7..2057b4a2 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize.go @@ -20,14 +20,14 @@ import ( "github.com/spf13/viper" ) -type cache struct { - nnsCs *state.Contract - groupKey *keys.PublicKey +type Cache struct { + NNSCs *state.Contract + GroupKey *keys.PublicKey } -type initializeContext struct { +type InitializeContext struct { morphUtil.ClientContext - cache + Cache // CommitteeAcc is used for retrieving the committee address and the verification script. CommitteeAcc *wallet.Account // ConsensusAcc is used for retrieving the committee address and the verification script. @@ -37,62 +37,62 @@ type initializeContext struct { ContractWallet *wallet.Wallet // Accounts contains simple signature accounts in the same order as in Wallets. Accounts []*wallet.Account - Contracts map[string]*contractState + Contracts map[string]*morphUtil.ContractState Command *cobra.Command ContractPath string ContractURL string } func initializeSideChainCmd(cmd *cobra.Command, _ []string) error { - initCtx, err := newInitializeContext(cmd, viper.GetViper()) + initCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("initialization error: %w", err) } - defer initCtx.close() + defer initCtx.Close() // 1. Transfer funds to committee accounts. cmd.Println("Stage 1: transfer GAS to alphabet nodes.") - if err := initCtx.transferFunds(); err != nil { + if err := transferFunds(initCtx); err != nil { return err } cmd.Println("Stage 2: set notary and alphabet nodes in designate contract.") - if err := initCtx.setNotaryAndAlphabetNodes(); err != nil { + if err := setNotaryAndAlphabetNodes(initCtx); err != nil { return err } // 3. Deploy NNS contract. cmd.Println("Stage 3: deploy NNS contract.") - if err := initCtx.deployNNS(deployMethodName); err != nil { + if err := deployNNS(initCtx, deployMethodName); err != nil { return err } // 4. Deploy NeoFS contracts. cmd.Println("Stage 4: deploy NeoFS contracts.") - if err := initCtx.deployContracts(); err != nil { + if err := deployContracts(initCtx); err != nil { return err } cmd.Println("Stage 4.1: Transfer GAS to proxy contract.") - if err := initCtx.transferGASToProxy(); err != nil { + if err := transferGASToProxy(initCtx); err != nil { return err } cmd.Println("Stage 5: register candidates.") - if err := initCtx.registerCandidates(); err != nil { + if err := registerCandidates(initCtx); err != nil { return err } cmd.Println("Stage 6: transfer NEO to alphabet contracts.") - if err := initCtx.transferNEOToAlphabetContracts(); err != nil { + if err := transferNEOToAlphabetContracts(initCtx); err != nil { return err } cmd.Println("Stage 7: set addresses in NNS.") - return initCtx.setNNS() + return setNNS(initCtx) } -func (c *initializeContext) close() { +func (c *InitializeContext) Close() { if local, ok := c.Client.(*morphUtil.LocalClient); ok { err := local.Dump() if err != nil { @@ -102,7 +102,7 @@ func (c *initializeContext) close() { } } -func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContext, error) { +func NewInitializeContext(cmd *cobra.Command, v *viper.Viper) (*InitializeContext, error) { walletDir := config.ResolveHomePath(viper.GetString(morphUtil.AlphabetWalletsFlag)) wallets, err := morphUtil.GetAlphabetWallets(v, walletDir) if err != nil { @@ -160,7 +160,7 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex return nil, fmt.Errorf("client context: %w", err) } - initCtx := &initializeContext{ + initCtx := &InitializeContext{ ClientContext: *cliCtx, ConsensusAcc: consensusAcc, CommitteeAcc: committeeAcc, @@ -168,7 +168,7 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex Wallets: wallets, Accounts: accounts, Command: cmd, - Contracts: make(map[string]*contractState), + Contracts: make(map[string]*morphUtil.ContractState), ContractPath: ctrPath, ContractURL: ctrURL, } @@ -246,13 +246,13 @@ func createWalletAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) { return accounts, nil } -func (c *initializeContext) awaitTx() error { +func (c *InitializeContext) AwaitTx() error { return c.ClientContext.AwaitTx(c.Command) } -func (c *initializeContext) nnsContractState() (*state.Contract, error) { - if c.nnsCs != nil { - return c.nnsCs, nil +func (c *InitializeContext) NNSContractState() (*state.Contract, error) { + if c.NNSCs != nil { + return c.NNSCs, nil } r := management.NewReader(c.ReadOnlyInvoker) @@ -261,16 +261,16 @@ func (c *initializeContext) nnsContractState() (*state.Contract, error) { return nil, err } - c.nnsCs = cs + c.NNSCs = cs return cs, nil } -func (c *initializeContext) getSigner(tryGroup bool, acc *wallet.Account) transaction.Signer { - if tryGroup && c.groupKey != nil { +func (c *InitializeContext) GetSigner(tryGroup bool, acc *wallet.Account) transaction.Signer { + if tryGroup && c.GroupKey != nil { return transaction.Signer{ Account: acc.Contract.ScriptHash(), Scopes: transaction.CustomGroups, - AllowedGroups: keys.PublicKeys{c.groupKey}, + AllowedGroups: keys.PublicKeys{c.GroupKey}, } } @@ -283,14 +283,14 @@ func (c *initializeContext) getSigner(tryGroup bool, acc *wallet.Account) transa return signer } - nnsCs, err := c.nnsContractState() + nnsCs, err := c.NNSContractState() if err != nil { return signer } - groupKey, err := nnsResolveKey(c.ReadOnlyInvoker, nnsCs.Hash, morphClient.NNSGroupKeyName) + groupKey, err := morphUtil.NNSResolveKey(c.ReadOnlyInvoker, nnsCs.Hash, morphClient.NNSGroupKeyName) if err == nil { - c.groupKey = groupKey + c.GroupKey = groupKey signer.Scopes = transaction.CustomGroups signer.AllowedGroups = keys.PublicKeys{groupKey} @@ -298,21 +298,21 @@ func (c *initializeContext) getSigner(tryGroup bool, acc *wallet.Account) transa return signer } -// sendCommitteeTx creates transaction from script, signs it by committee nodes and sends it to RPC. +// SendCommitteeTx creates transaction from script, signs it by committee nodes and sends it to RPC. // If tryGroup is false, global scope is used for the signer (useful when // working with native contracts). -func (c *initializeContext) sendCommitteeTx(script []byte, tryGroup bool) error { +func (c *InitializeContext) SendCommitteeTx(script []byte, tryGroup bool) error { return c.sendMultiTx(script, tryGroup, false) } -// sendConsensusTx creates transaction from script, signs it by alphabet nodes and sends it to RPC. +// SendConsensusTx creates transaction from script, signs it by alphabet nodes and sends it to RPC. // Not that because this is used only after the contracts were initialized and deployed, // we always try to have a group scope. -func (c *initializeContext) sendConsensusTx(script []byte) error { +func (c *InitializeContext) SendConsensusTx(script []byte) error { return c.sendMultiTx(script, true, true) } -func (c *initializeContext) sendMultiTx(script []byte, tryGroup bool, withConsensus bool) error { +func (c *InitializeContext) sendMultiTx(script []byte, tryGroup bool, withConsensus bool) error { var act *actor.Actor var err error @@ -321,12 +321,12 @@ func (c *initializeContext) sendMultiTx(script []byte, tryGroup bool, withConsen // Even for consensus signatures we need the committee to pay. signers := make([]actor.SignerAccount, 1, 2) signers[0] = actor.SignerAccount{ - Signer: c.getSigner(tryGroup, c.CommitteeAcc), + Signer: c.GetSigner(tryGroup, c.CommitteeAcc), Account: c.CommitteeAcc, } if withConsensus { signers = append(signers, actor.SignerAccount{ - Signer: c.getSigner(tryGroup, c.ConsensusAcc), + Signer: c.GetSigner(tryGroup, c.ConsensusAcc), Account: c.ConsensusAcc, }) } @@ -346,11 +346,11 @@ func (c *initializeContext) sendMultiTx(script []byte, tryGroup bool, withConsen return fmt.Errorf("could not perform test invocation: %w", err) } - if err := c.multiSign(tx, morphUtil.CommitteeAccountName); err != nil { + if err := c.MultiSign(tx, morphUtil.CommitteeAccountName); err != nil { return err } if withConsensus { - if err := c.multiSign(tx, morphUtil.ConsensusAccountName); err != nil { + if err := c.MultiSign(tx, morphUtil.ConsensusAccountName); err != nil { return err } } diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go b/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go index e76a9f0d..fd27eda6 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_deploy.go @@ -4,7 +4,6 @@ import ( "archive/tar" "compress/gzip" "encoding/hex" - "encoding/json" "errors" "fmt" "io" @@ -22,12 +21,11 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/address" io2 "github.com/nspcc-dev/neo-go/pkg/io" "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/management" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" - "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/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -66,24 +64,16 @@ var ( } ) -type contractState struct { - NEF *nef.File - RawNEF []byte - Manifest *manifest.Manifest - RawManifest []byte - Hash util.Uint160 -} - const ( updateMethodName = "update" deployMethodName = "deploy" ) -func (c *initializeContext) deployNNS(method string) error { - cs := c.getContract(morphUtil.NNSContract) +func deployNNS(c *InitializeContext, method string) error { + cs := c.GetContract(morphUtil.NNSContract) h := cs.Hash - nnsCs, err := c.nnsContractState() + nnsCs, err := c.NNSContractState() if err != nil { return err } @@ -99,7 +89,7 @@ func (c *initializeContext) deployNNS(method string) error { h = nnsCs.Hash } - err = c.addManifestGroup(h, cs) + err = addManifestGroup(c.ContractWallet, h, cs) if err != nil { return fmt.Errorf("can't sign manifest group: %v", err) } @@ -116,17 +106,17 @@ func (c *initializeContext) deployNNS(method string) error { return fmt.Errorf("failed to create deploy tx for %s: %w", morphUtil.NNSContract, err) } - if err := c.multiSignAndSend(tx, morphUtil.CommitteeAccountName); err != nil { + if err := c.MultiSignAndSend(tx, morphUtil.CommitteeAccountName); err != nil { return fmt.Errorf("can't send deploy transaction: %w", err) } - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) updateContracts() error { - alphaCs := c.getContract(morphUtil.AlphabetContract) +func updateContractsInternal(c *InitializeContext) error { + alphaCs := c.GetContract(morphUtil.AlphabetContract) - nnsCs, err := c.nnsContractState() + nnsCs, err := c.NNSContractState() if err != nil { return err } @@ -147,19 +137,19 @@ func (c *initializeContext) updateContracts() error { emit.Bytes(w.BinWriter, alphaCs.RawNEF) emit.Opcodes(w.BinWriter, opcode.STSFLD0) - keysParam, err := c.deployAlphabetAccounts(nnsHash, w, alphaCs) + keysParam, err := deployAlphabetAccounts(c, nnsHash, w, alphaCs) if err != nil { return err } w.Reset() - if err = c.deployOrUpdateContracts(w, nnsHash, keysParam); err != nil { + if err = deployOrUpdateContracts(c, w, nnsHash, keysParam); err != nil { return err } groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey() - _, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey) + _, _, err = c.EmitUpdateNNSGroupScript(w, nnsHash, groupKey) if err != nil { return err } @@ -170,20 +160,20 @@ func (c *initializeContext) updateContracts() error { emit.Opcodes(w.BinWriter, opcode.PACK) emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All) - if err := c.sendCommitteeTx(w.Bytes(), false); err != nil { + if err := c.SendCommitteeTx(w.Bytes(), false); err != nil { return err } - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error { +func deployOrUpdateContracts(c *InitializeContext, w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error { emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1}) emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All) emit.Opcodes(w.BinWriter, opcode.STSFLD0) emit.AppCall(w.BinWriter, nnsHash, "setPrice", callflag.All, 1) for _, ctrName := range contractList { - cs := c.getContract(ctrName) + cs := c.GetContract(ctrName) method := updateMethodName ctrHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, nnsHash, morphUtil.DomainOf(ctrName)) @@ -196,7 +186,7 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash } } - err = c.addManifestGroup(ctrHash, cs) + err = addManifestGroup(c.ContractWallet, ctrHash, cs) if err != nil { return fmt.Errorf("can't sign manifest group: %v", err) } @@ -206,7 +196,7 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash invokeHash = ctrHash } - args, err := c.getContractDeployData(ctrName, keysParam, updateMethodName) + args, err := getContractDeployData(c, ctrName, keysParam, updateMethodName) if err != nil { return fmt.Errorf("%s: getting update params: %v", ctrName, err) } @@ -223,9 +213,9 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash w.WriteBytes(res.Script) if method == deployMethodName { - // same actions are done in initializeContext.setNNS, can be unified + // same actions are done in InitializeContext.setNNS, can be unified domain := ctrName + ".frostfs" - script, ok, err := c.nnsRegisterDomainScript(nnsHash, cs.Hash, domain) + script, ok, err := c.NNSRegisterDomainScript(nnsHash, cs.Hash, domain) if err != nil { return err } @@ -243,7 +233,7 @@ func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash return nil } -func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *contractState) ([]any, error) { +func deployAlphabetAccounts(c *InitializeContext, nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *morphUtil.ContractState) ([]any, error) { var keysParam []any baseGroups := alphaCs.Manifest.Groups @@ -257,11 +247,11 @@ func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2. keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes()) - params := c.getAlphabetDeployItems(i, len(c.Wallets)) + params := c.GetAlphabetDeployItems(i, len(c.Wallets)) emit.Array(w.BinWriter, params...) alphaCs.Manifest.Groups = baseGroups - err = c.addManifestGroup(ctrHash, alphaCs) + err = addManifestGroup(c.ContractWallet, ctrHash, alphaCs) if err != nil { return nil, fmt.Errorf("can't sign manifest group: %v", err) } @@ -272,7 +262,7 @@ func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2. emit.Opcodes(w.BinWriter, opcode.PACK) emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All) } - if err := c.sendCommitteeTx(w.Bytes(), false); err != nil { + if err := c.SendCommitteeTx(w.Bytes(), false); err != nil { if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) { return nil, err } @@ -282,8 +272,8 @@ func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2. return keysParam, nil } -func (c *initializeContext) deployContracts() error { - alphaCs := c.getContract(morphUtil.AlphabetContract) +func deployContracts(c *InitializeContext) error { + alphaCs := c.GetContract(morphUtil.AlphabetContract) var keysParam []any @@ -292,19 +282,19 @@ func (c *initializeContext) deployContracts() error { // alphabet contracts should be deployed by individual nodes to get different hashes. for i, acc := range c.Accounts { ctrHash := state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name) - if c.isUpdated(ctrHash, alphaCs) { + if c.IsUpdated(ctrHash, alphaCs) { c.Command.Printf("Alphabet contract #%d is already deployed.\n", i) continue } alphaCs.Manifest.Groups = baseGroups - err := c.addManifestGroup(ctrHash, alphaCs) + err := addManifestGroup(c.ContractWallet, ctrHash, alphaCs) if err != nil { return fmt.Errorf("can't sign manifest group: %v", err) } keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes()) - params := getContractDeployParameters(alphaCs, c.getAlphabetDeployItems(i, len(c.Wallets))) + params := getContractDeployParameters(alphaCs, c.GetAlphabetDeployItems(i, len(c.Wallets))) act, err := actor.NewSimple(c.Client, acc) if err != nil { @@ -320,20 +310,20 @@ func (c *initializeContext) deployContracts() error { } for _, ctrName := range contractList { - cs := c.getContract(ctrName) + cs := c.GetContract(ctrName) ctrHash := cs.Hash - if c.isUpdated(ctrHash, cs) { + if c.IsUpdated(ctrHash, cs) { c.Command.Printf("%s contract is already deployed.\n", ctrName) continue } - err := c.addManifestGroup(ctrHash, cs) + err := addManifestGroup(c.ContractWallet, ctrHash, cs) if err != nil { return fmt.Errorf("can't sign manifest group: %v", err) } - args, err := c.getContractDeployData(ctrName, keysParam, deployMethodName) + args, err := getContractDeployData(c, ctrName, keysParam, deployMethodName) if err != nil { return fmt.Errorf("%s: getting deploy params: %v", ctrName, err) } @@ -343,25 +333,25 @@ func (c *initializeContext) deployContracts() error { return fmt.Errorf("can't deploy %s contract: %w", ctrName, err) } - if err := c.sendCommitteeTx(res.Script, false); err != nil { + if err := c.SendCommitteeTx(res.Script, false); err != nil { return err } } - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) isUpdated(ctrHash util.Uint160, cs *contractState) bool { +func (c *InitializeContext) IsUpdated(ctrHash util.Uint160, cs *morphUtil.ContractState) bool { r := management.NewReader(c.ReadOnlyInvoker) realCs, err := r.GetContract(ctrHash) return err == nil && realCs != nil && realCs.NEF.Checksum == cs.NEF.Checksum } -func (c *initializeContext) getContract(ctrName string) *contractState { +func (c *InitializeContext) GetContract(ctrName string) *morphUtil.ContractState { return c.Contracts[ctrName] } -func (c *initializeContext) readContracts(names []string) error { +func (c *InitializeContext) readContracts(names []string) error { var ( fi os.FileInfo err error @@ -401,7 +391,7 @@ func (c *initializeContext) readContracts(names []string) error { return err } for _, name := range names { - if err := m[name].parse(); err != nil { + if err := m[name].Parse(); err != nil { return err } c.Contracts[name] = m[name] @@ -418,7 +408,7 @@ func (c *initializeContext) readContracts(names []string) error { return nil } -func readContract(ctrPath, ctrName string) (*contractState, error) { +func readContract(ctrPath, ctrName string) (*morphUtil.ContractState, error) { rawNef, err := os.ReadFile(filepath.Join(ctrPath, ctrName+"_contract.nef")) if err != nil { return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err) @@ -428,34 +418,18 @@ func readContract(ctrPath, ctrName string) (*contractState, error) { return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err) } - cs := &contractState{ + cs := &morphUtil.ContractState{ RawNEF: rawNef, RawManifest: rawManif, } - return cs, cs.parse() + return cs, cs.Parse() } -func (cs *contractState) parse() error { - nf, err := nef.FileFromBytes(cs.RawNEF) - if err != nil { - return fmt.Errorf("can't parse NEF file: %w", err) - } - - m := new(manifest.Manifest) - if err := json.Unmarshal(cs.RawManifest, m); err != nil { - return fmt.Errorf("can't parse manifest file: %w", err) - } - - cs.NEF = &nf - cs.Manifest = m - return nil -} - -func readContractsFromArchive(file io.Reader, names []string) (map[string]*contractState, error) { - m := make(map[string]*contractState, len(names)) +func readContractsFromArchive(file io.Reader, names []string) (map[string]*morphUtil.ContractState, error) { + m := make(map[string]*morphUtil.ContractState, len(names)) for i := range names { - m[names[i]] = new(contractState) + m[names[i]] = new(morphUtil.ContractState) } gr, err := gzip.NewReader(file) @@ -503,11 +477,11 @@ func readContractsFromArchive(file io.Reader, names []string) (map[string]*contr return m, nil } -func getContractDeployParameters(cs *contractState, deployData []any) []any { +func getContractDeployParameters(cs *morphUtil.ContractState, deployData []any) []any { return []any{cs.RawNEF, cs.RawManifest, deployData} } -func (c *initializeContext) getContractDeployData(ctrName string, keysParam []any, method string) ([]any, error) { +func getContractDeployData(c *InitializeContext, ctrName string, keysParam []any, method string) ([]any, error) { items := make([]any, 0, 6) switch ctrName { @@ -544,7 +518,7 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an err error ) if method == updateMethodName { - h, found, err = c.getFrostfsIDAdminFromContract() + h, found, err = getFrostfsIDAdminFromContract(c.ReadOnlyInvoker) } if method != updateMethodName || err == nil && !found { h, found, err = getFrostfsIDAdmin(viper.GetViper()) @@ -561,7 +535,7 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an case morphUtil.NetmapContract: md := getDefaultNetmapContractConfigMap() if method == updateMethodName { - if err := c.mergeNetmapConfig(md); err != nil { + if err := mergeNetmapConfig(c.ReadOnlyInvoker, md); err != nil { return nil, err } } @@ -586,17 +560,17 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an return items, nil } -func (c *initializeContext) getFrostfsIDAdminFromContract() (util.Uint160, bool, error) { - r := management.NewReader(c.ReadOnlyInvoker) +func getFrostfsIDAdminFromContract(roInvoker *invoker.Invoker) (util.Uint160, bool, error) { + r := management.NewReader(roInvoker) cs, err := r.GetContractByID(1) if err != nil { return util.Uint160{}, false, fmt.Errorf("get nns contract: %w", err) } - fidHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, cs.Hash, morphUtil.DomainOf(morphUtil.FrostfsIDContract)) + fidHash, err := morphUtil.NNSResolveHash(roInvoker, cs.Hash, morphUtil.DomainOf(morphUtil.FrostfsIDContract)) if err != nil { return util.Uint160{}, false, fmt.Errorf("resolve frostfsid contract hash: %w", err) } - item, err := unwrap.Item(c.ReadOnlyInvoker.Call(fidHash, "getAdmin")) + item, err := unwrap.Item(roInvoker.Call(fidHash, "getAdmin")) if err != nil { return util.Uint160{}, false, fmt.Errorf("getAdmin: %w", err) } @@ -615,25 +589,25 @@ func (c *initializeContext) getFrostfsIDAdminFromContract() (util.Uint160, bool, return h, true, nil } -func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item, error) { - r := management.NewReader(c.ReadOnlyInvoker) +func getNetConfigFromNetmapContract(roInvoker *invoker.Invoker) ([]stackitem.Item, error) { + r := management.NewReader(roInvoker) cs, err := r.GetContractByID(1) if err != nil { return nil, fmt.Errorf("get nns contract: %w", err) } - nmHash, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, cs.Hash, morphUtil.DomainOf(morphUtil.NetmapContract)) + nmHash, err := morphUtil.NNSResolveHash(roInvoker, cs.Hash, morphUtil.DomainOf(morphUtil.NetmapContract)) if err != nil { return nil, fmt.Errorf("can't get netmap contract hash: %w", err) } - arr, err := unwrap.Array(c.ReadOnlyInvoker.Call(nmHash, "listConfig")) + arr, err := unwrap.Array(roInvoker.Call(nmHash, "listConfig")) if err != nil { return nil, fmt.Errorf("can't fetch list of network config keys from the netmap contract") } return arr, err } -func (c *initializeContext) mergeNetmapConfig(md map[string]any) error { - arr, err := c.getNetConfigFromNetmapContract() +func mergeNetmapConfig(roInvoker *invoker.Invoker, md map[string]any) error { + arr, err := getNetConfigFromNetmapContract(roInvoker) if err != nil { return err } @@ -652,7 +626,7 @@ func (c *initializeContext) mergeNetmapConfig(md map[string]any) error { return nil } -func (c *initializeContext) getAlphabetDeployItems(i, n int) []any { +func (c *InitializeContext) GetAlphabetDeployItems(i, n int) []any { items := make([]any, 5) items[0] = c.Contracts[morphUtil.NetmapContract].Hash items[1] = c.Contracts[morphUtil.ProxyContract].Hash diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go b/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go index 7b0bc819..2a80077c 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_nns.go @@ -14,16 +14,11 @@ import ( "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/io" - "github.com/nspcc-dev/neo-go/pkg/rpcclient" - "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" - nnsClient "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns" - "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" ) @@ -31,14 +26,14 @@ const defaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second const frostfsOpsEmail = "ops@frostfs.info" -func (c *initializeContext) setNNS() error { +func setNNS(c *InitializeContext) error { r := management.NewReader(c.ReadOnlyInvoker) nnsCs, err := r.GetContractByID(1) if err != nil { return err } - ok, err := c.nnsRootRegistered(nnsCs.Hash, "frostfs") + ok, err := c.NNSRootRegistered(nnsCs.Hash, "frostfs") if err != nil { return err } else if !ok { @@ -47,48 +42,48 @@ func (c *initializeContext) setNNS() error { "frostfs", c.CommitteeAcc.Contract.ScriptHash(), frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600)) emit.Opcodes(bw.BinWriter, opcode.ASSERT) - if err := c.sendCommitteeTx(bw.Bytes(), true); err != nil { + if err := c.SendCommitteeTx(bw.Bytes(), true); err != nil { return fmt.Errorf("can't add domain root to NNS: %w", err) } - if err := c.awaitTx(); err != nil { + if err := c.AwaitTx(); err != nil { return err } } - alphaCs := c.getContract(morphUtil.AlphabetContract) + alphaCs := c.GetContract(morphUtil.AlphabetContract) for i, acc := range c.Accounts { alphaCs.Hash = state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name) domain := getAlphabetNNSDomain(i) - if err := c.nnsRegisterDomain(nnsCs.Hash, alphaCs.Hash, domain); err != nil { + if err := nnsRegisterDomain(c, nnsCs.Hash, alphaCs.Hash, domain); err != nil { return err } c.Command.Printf("NNS: Set %s -> %s\n", domain, alphaCs.Hash.StringLE()) } for _, ctrName := range contractList { - cs := c.getContract(ctrName) + cs := c.GetContract(ctrName) domain := ctrName + ".frostfs" - if err := c.nnsRegisterDomain(nnsCs.Hash, cs.Hash, domain); err != nil { + if err := nnsRegisterDomain(c, nnsCs.Hash, cs.Hash, domain); err != nil { return err } c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE()) } groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey() - err = c.updateNNSGroup(nnsCs.Hash, groupKey) + err = updateNNSGroup(c, nnsCs.Hash, groupKey) if err != nil { return err } c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes())) - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) updateNNSGroup(nnsHash util.Uint160, pub *keys.PublicKey) error { +func updateNNSGroup(c *InitializeContext, nnsHash util.Uint160, pub *keys.PublicKey) error { bw := io.NewBufBinWriter() - keyAlreadyAdded, domainRegCodeEmitted, err := c.emitUpdateNNSGroupScript(bw, nnsHash, pub) + keyAlreadyAdded, domainRegCodeEmitted, err := c.EmitUpdateNNSGroupScript(bw, nnsHash, pub) if keyAlreadyAdded || err != nil { return err } @@ -101,20 +96,20 @@ func (c *initializeContext) updateNNSGroup(nnsHash util.Uint160, pub *keys.Publi script = w.Bytes() } - return c.sendCommitteeTx(script, true) + return c.SendCommitteeTx(script, true) } -// emitUpdateNNSGroupScript emits script for updating group key stored in NNS. +// EmitUpdateNNSGroupScript emits script for updating group key stored in NNS. // First return value is true iff the key is already there and nothing should be done. // Second return value is true iff a domain registration code was emitted. -func (c *initializeContext) emitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHash util.Uint160, pub *keys.PublicKey) (bool, bool, error) { - isAvail, err := nnsIsAvailable(c.Client, nnsHash, morphClient.NNSGroupKeyName) +func (c *InitializeContext) EmitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHash util.Uint160, pub *keys.PublicKey) (bool, bool, error) { + isAvail, err := morphUtil.NNSIsAvailable(c.Client, nnsHash, morphClient.NNSGroupKeyName) if err != nil { return false, false, err } if !isAvail { - currentPub, err := nnsResolveKey(c.ReadOnlyInvoker, nnsHash, morphClient.NNSGroupKeyName) + currentPub, err := morphUtil.NNSResolveKey(c.ReadOnlyInvoker, nnsHash, morphClient.NNSGroupKeyName) if err != nil { return false, false, err } @@ -165,8 +160,8 @@ func wrapRegisterScriptWithPrice(w *io.BufBinWriter, nnsHash util.Uint160, s []b } } -func (c *initializeContext) nnsRegisterDomainScript(nnsHash, expectedHash util.Uint160, domain string) ([]byte, bool, error) { - ok, err := nnsIsAvailable(c.Client, nnsHash, domain) +func (c *InitializeContext) NNSRegisterDomainScript(nnsHash, expectedHash util.Uint160, domain string) ([]byte, bool, error) { + ok, err := morphUtil.NNSIsAvailable(c.Client, nnsHash, domain) if err != nil { return nil, false, err } @@ -191,8 +186,8 @@ func (c *initializeContext) nnsRegisterDomainScript(nnsHash, expectedHash util.U return nil, s == expectedHash, nil } -func (c *initializeContext) nnsRegisterDomain(nnsHash, expectedHash util.Uint160, domain string) error { - script, ok, err := c.nnsRegisterDomainScript(nnsHash, expectedHash, domain) +func nnsRegisterDomain(c *InitializeContext, nnsHash, expectedHash util.Uint160, domain string) error { + script, ok, err := c.NNSRegisterDomainScript(nnsHash, expectedHash, domain) if ok || err != nil { return err } @@ -206,10 +201,10 @@ func (c *initializeContext) nnsRegisterDomain(nnsHash, expectedHash util.Uint160 domain, int64(nns.TXT), expectedHash.StringLE()) emit.AppCall(w.BinWriter, nnsHash, "addRecord", callflag.All, domain, int64(nns.TXT), address.Uint160ToString(expectedHash)) - return c.sendCommitteeTx(w.Bytes(), true) + return c.SendCommitteeTx(w.Bytes(), true) } -func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160, zone string) (bool, error) { +func (c *InitializeContext) NNSRootRegistered(nnsHash util.Uint160, zone string) (bool, error) { res, err := c.CommitteeAct.Call(nnsHash, "isAvailable", "name."+zone) if err != nil { return false, err @@ -219,43 +214,3 @@ func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160, zone string) } var errMissingNNSRecord = errors.New("missing NNS record") - -func nnsResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) { - res, err := morphUtil.NNSResolve(inv, nnsHash, domain) - if err != nil { - return nil, err - } - if _, ok := res.Value().(stackitem.Null); ok { - return nil, errors.New("NNS record is missing") - } - arr, ok := res.Value().([]stackitem.Item) - if !ok { - return nil, errors.New("API of the NNS contract method `resolve` has changed") - } - for i := range arr { - var bs []byte - bs, err = arr[i].TryBytes() - if err != nil { - continue - } - - return keys.NewPublicKeyFromString(string(bs)) - } - return nil, errors.New("no valid keys are found") -} - -func nnsIsAvailable(c morphUtil.Client, nnsHash util.Uint160, name string) (bool, error) { - switch c.(type) { - case *rpcclient.Client: - inv := invoker.New(c, nil) - reader := nnsClient.NewReader(inv, nnsHash) - return reader.IsAvailable(name) - default: - b, err := unwrap.Bool(morphUtil.InvokeFunction(c, nnsHash, "isAvailable", []any{name}, nil)) - if err != nil { - return false, fmt.Errorf("`isAvailable`: invalid response: %w", err) - } - - return b, nil - } -} diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_register.go b/cmd/frostfs-adm/internal/modules/morph/initialize_register.go index 04afd7c7..9d3bcff0 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_register.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_register.go @@ -28,8 +28,8 @@ const ( registerBatchSize = transaction.MaxAttributes - 1 ) -func (c *initializeContext) registerCandidateRange(start, end int) error { - regPrice, err := c.getCandidateRegisterPrice() +func registerCandidateRange(c *InitializeContext, start, end int) error { + regPrice, err := getCandidateRegisterPrice(c) if err != nil { return fmt.Errorf("can't fetch registration price: %w", err) } @@ -46,7 +46,7 @@ func (c *initializeContext) registerCandidateRange(start, end int) error { } signers := []actor.SignerAccount{{ - Signer: c.getSigner(false, c.CommitteeAcc), + Signer: c.GetSigner(false, c.CommitteeAcc), Account: c.CommitteeAcc, }} for _, acc := range c.Accounts[start:end] { @@ -68,7 +68,7 @@ func (c *initializeContext) registerCandidateRange(start, end int) error { if err != nil { return fmt.Errorf("can't create tx: %w", err) } - if err := c.multiSign(tx, morphUtil.CommitteeAccountName); err != nil { + if err := c.MultiSign(tx, morphUtil.CommitteeAccountName); err != nil { return fmt.Errorf("can't sign a transaction: %w", err) } @@ -82,7 +82,7 @@ func (c *initializeContext) registerCandidateRange(start, end int) error { return c.SendTx(tx, c.Command, true) } -func (c *initializeContext) registerCandidates() error { +func registerCandidates(c *InitializeContext) error { cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neo.Hash, "getCandidates")) if err != nil { return fmt.Errorf("`getCandidates`: %w", err) @@ -107,7 +107,7 @@ func (c *initializeContext) registerCandidates() error { if have >= end { continue } - if err := c.registerCandidateRange(start, end); err != nil { + if err := registerCandidateRange(c, start, end); err != nil { return fmt.Errorf("registering candidates %d..%d: %q", start, end-1, err) } } @@ -115,15 +115,15 @@ func (c *initializeContext) registerCandidates() error { return nil } -func (c *initializeContext) transferNEOToAlphabetContracts() error { +func transferNEOToAlphabetContracts(c *InitializeContext) error { neoHash := neo.Hash - ok, err := c.transferNEOFinished(neoHash) + ok, err := transferNEOFinished(c, neoHash) if ok || err != nil { return err } - cs := c.getContract(morphUtil.AlphabetContract) + cs := c.GetContract(morphUtil.AlphabetContract) amount := initialAlphabetNEOAmount / len(c.Wallets) bw := io.NewBufBinWriter() @@ -134,14 +134,14 @@ func (c *initializeContext) transferNEOToAlphabetContracts() error { emit.Opcodes(bw.BinWriter, opcode.ASSERT) } - if err := c.sendCommitteeTx(bw.Bytes(), false); err != nil { + if err := c.SendCommitteeTx(bw.Bytes(), false); err != nil { return err } - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, error) { +func transferNEOFinished(c *InitializeContext, neoHash util.Uint160) (bool, error) { r := nep17.NewReader(c.ReadOnlyInvoker, neoHash) bal, err := r.BalanceOf(c.CommitteeAcc.Contract.ScriptHash()) return bal.Cmp(big.NewInt(native.NEOTotalSupply)) == -1, err @@ -149,7 +149,7 @@ func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, err var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response") -func (c *initializeContext) getCandidateRegisterPrice() (int64, error) { +func getCandidateRegisterPrice(c *InitializeContext) (int64, error) { switch c.Client.(type) { case *rpcclient.Client: inv := invoker.New(c.Client, nil) diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_roles.go b/cmd/frostfs-adm/internal/modules/morph/initialize_roles.go index aed35e23..35750d60 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_roles.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_roles.go @@ -9,8 +9,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) -func (c *initializeContext) setNotaryAndAlphabetNodes() error { - if ok, err := c.setRolesFinished(); ok || err != nil { +func setNotaryAndAlphabetNodes(c *InitializeContext) error { + if ok, err := setRolesFinished(c); ok || err != nil { if err == nil { c.Command.Println("Stage 2: already performed.") } @@ -28,14 +28,14 @@ func (c *initializeContext) setNotaryAndAlphabetNodes() error { emit.AppCall(w.BinWriter, rolemgmt.Hash, "designateAsRole", callflag.States|callflag.AllowNotify, int64(noderoles.NeoFSAlphabet), pubs) - if err := c.sendCommitteeTx(w.Bytes(), false); err != nil { + if err := c.SendCommitteeTx(w.Bytes(), false); err != nil { return err } - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) setRolesFinished() (bool, error) { +func setRolesFinished(c *InitializeContext) (bool, error) { height, err := c.Client.GetBlockCount() if err != nil { return false, err diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_transfer.go b/cmd/frostfs-adm/internal/modules/morph/initialize_transfer.go index a24d3a6f..634b4789 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_transfer.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_transfer.go @@ -28,8 +28,8 @@ const ( initialProxyGASAmount = 50_000 * native.GASFactor ) -func (c *initializeContext) transferFunds() error { - ok, err := c.transferFundsFinished() +func transferFunds(c *InitializeContext) error { + ok, err := transferFundsFinished(c) if ok || err != nil { if err == nil { c.Command.Println("Stage 1: already performed.") @@ -68,14 +68,14 @@ func (c *initializeContext) transferFunds() error { return fmt.Errorf("can't create transfer transaction: %w", err) } - if err := c.multiSignAndSend(tx, morphUtil.ConsensusAccountName); err != nil { + if err := c.MultiSignAndSend(tx, morphUtil.ConsensusAccountName); err != nil { return fmt.Errorf("can't send transfer transaction: %w", err) } - return c.awaitTx() + return c.AwaitTx() } -func (c *initializeContext) transferFundsFinished() (bool, error) { +func transferFundsFinished(c *InitializeContext) (bool, error) { acc := c.Accounts[0] r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash) @@ -83,15 +83,15 @@ func (c *initializeContext) transferFundsFinished() (bool, error) { return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err } -func (c *initializeContext) multiSignAndSend(tx *transaction.Transaction, accType string) error { - if err := c.multiSign(tx, accType); err != nil { +func (c *InitializeContext) MultiSignAndSend(tx *transaction.Transaction, accType string) error { + if err := c.MultiSign(tx, accType); err != nil { return err } return c.SendTx(tx, c.Command, false) } -func (c *initializeContext) multiSign(tx *transaction.Transaction, accType string) error { +func (c *InitializeContext) MultiSign(tx *transaction.Transaction, accType string) error { version, err := c.Client.GetVersion() if err != nil { // error appears only if client @@ -143,8 +143,8 @@ func (c *initializeContext) multiSign(tx *transaction.Transaction, accType strin return fmt.Errorf("%s account was not found among transaction signers", accType) } -func (c *initializeContext) transferGASToProxy() error { - proxyCs := c.getContract(morphUtil.ProxyContract) +func transferGASToProxy(c *InitializeContext) error { + proxyCs := c.GetContract(morphUtil.ProxyContract) r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash) bal, err := r.BalanceOf(proxyCs.Hash) @@ -161,11 +161,11 @@ func (c *initializeContext) transferGASToProxy() error { return err } - if err := c.multiSignAndSend(tx, morphUtil.CommitteeAccountName); err != nil { + if err := c.MultiSignAndSend(tx, morphUtil.CommitteeAccountName); err != nil { return err } - return c.awaitTx() + return c.AwaitTx() } type transferTarget struct { diff --git a/cmd/frostfs-adm/internal/modules/morph/policy.go b/cmd/frostfs-adm/internal/modules/morph/policy.go index ba73d132..97cc76ab 100644 --- a/cmd/frostfs-adm/internal/modules/morph/policy.go +++ b/cmd/frostfs-adm/internal/modules/morph/policy.go @@ -25,7 +25,7 @@ const ( ) func setPolicyCmd(cmd *cobra.Command, args []string) error { - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("can't to initialize context: %w", err) } @@ -51,11 +51,11 @@ func setPolicyCmd(cmd *cobra.Command, args []string) error { emit.AppCall(bw.BinWriter, policy.Hash, "set"+k, callflag.All, int64(value)) } - if err := wCtx.sendCommitteeTx(bw.Bytes(), false); err != nil { + if err := wCtx.SendCommitteeTx(bw.Bytes(), false); err != nil { return err } - return wCtx.awaitTx() + return wCtx.AwaitTx() } func dumpPolicyCmd(cmd *cobra.Command, _ []string) error { diff --git a/cmd/frostfs-adm/internal/modules/morph/proxy.go b/cmd/frostfs-adm/internal/modules/morph/proxy.go index 9e973b0d..4bd461b9 100644 --- a/cmd/frostfs-adm/internal/modules/morph/proxy.go +++ b/cmd/frostfs-adm/internal/modules/morph/proxy.go @@ -36,7 +36,7 @@ func removeProxyAccount(cmd *cobra.Command, _ []string) { } func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error { - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("can't to initialize context: %w", err) } @@ -55,11 +55,11 @@ func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error bw := io.NewBufBinWriter() emit.AppCall(bw.BinWriter, proxyHash, method, callflag.All, addr) - if err := wCtx.sendConsensusTx(bw.Bytes()); err != nil { + if err := wCtx.SendConsensusTx(bw.Bytes()); err != nil { return err } - if err = wCtx.awaitTx(); err != nil { + if err = wCtx.AwaitTx(); err != nil { return err } diff --git a/cmd/frostfs-adm/internal/modules/morph/remove_node.go b/cmd/frostfs-adm/internal/modules/morph/remove_node.go index 678bcc7e..42464e73 100644 --- a/cmd/frostfs-adm/internal/modules/morph/remove_node.go +++ b/cmd/frostfs-adm/internal/modules/morph/remove_node.go @@ -29,11 +29,11 @@ func removeNodesCmd(cmd *cobra.Command, args []string) error { } } - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("can't initialize context: %w", err) } - defer wCtx.close() + defer wCtx.Close() r := management.NewReader(wCtx.ReadOnlyInvoker) cs, err := r.GetContractByID(1) @@ -56,9 +56,9 @@ func removeNodesCmd(cmd *cobra.Command, args []string) error { return err } - if err := wCtx.sendConsensusTx(bw.Bytes()); err != nil { + if err := wCtx.SendConsensusTx(bw.Bytes()); err != nil { return err } - return wCtx.awaitTx() + return wCtx.AwaitTx() } diff --git a/cmd/frostfs-adm/internal/modules/morph/update.go b/cmd/frostfs-adm/internal/modules/morph/update.go index 90b6d655..b6fcbb5b 100644 --- a/cmd/frostfs-adm/internal/modules/morph/update.go +++ b/cmd/frostfs-adm/internal/modules/morph/update.go @@ -8,14 +8,14 @@ import ( ) func updateContracts(cmd *cobra.Command, _ []string) error { - wCtx, err := newInitializeContext(cmd, viper.GetViper()) + wCtx, err := NewInitializeContext(cmd, viper.GetViper()) if err != nil { return fmt.Errorf("initialization error: %w", err) } - if err := wCtx.deployNNS(updateMethodName); err != nil { + if err := deployNNS(wCtx, updateMethodName); err != nil { return err } - return wCtx.updateContracts() + return updateContractsInternal(wCtx) } diff --git a/cmd/frostfs-adm/internal/modules/morph/util/initialize.go b/cmd/frostfs-adm/internal/modules/morph/util/initialize.go index 923061a0..725181ec 100644 --- a/cmd/frostfs-adm/internal/modules/morph/util/initialize.go +++ b/cmd/frostfs-adm/internal/modules/morph/util/initialize.go @@ -7,8 +7,11 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-contract/nns" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" + "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/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + nns2 "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" @@ -144,3 +147,43 @@ func NNSResolveHash(inv *invoker.Invoker, nnsHash util.Uint160, domain string) ( func DomainOf(contract string) string { return contract + ".frostfs" } + +func NNSResolveKey(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (*keys.PublicKey, error) { + res, err := NNSResolve(inv, nnsHash, domain) + if err != nil { + return nil, err + } + if _, ok := res.Value().(stackitem.Null); ok { + return nil, errors.New("NNS record is missing") + } + arr, ok := res.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("API of the NNS contract method `resolve` has changed") + } + for i := range arr { + var bs []byte + bs, err = arr[i].TryBytes() + if err != nil { + continue + } + + return keys.NewPublicKeyFromString(string(bs)) + } + return nil, errors.New("no valid keys are found") +} + +func NNSIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) { + switch c.(type) { + case *rpcclient.Client: + inv := invoker.New(c, nil) + reader := nns2.NewReader(inv, nnsHash) + return reader.IsAvailable(name) + default: + b, err := unwrap.Bool(InvokeFunction(c, nnsHash, "isAvailable", []any{name}, nil)) + if err != nil { + return false, fmt.Errorf("`isAvailable`: invalid response: %w", err) + } + + return b, nil + } +} diff --git a/cmd/frostfs-adm/internal/modules/morph/util/initialize_ctx.go b/cmd/frostfs-adm/internal/modules/morph/util/initialize_ctx.go new file mode 100644 index 00000000..be5dcf2d --- /dev/null +++ b/cmd/frostfs-adm/internal/modules/morph/util/initialize_ctx.go @@ -0,0 +1,34 @@ +package util + +import ( + "encoding/json" + "fmt" + + "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" +) + +func (cs *ContractState) Parse() error { + nf, err := nef.FileFromBytes(cs.RawNEF) + if err != nil { + return fmt.Errorf("can't parse NEF file: %w", err) + } + + m := new(manifest.Manifest) + if err := json.Unmarshal(cs.RawManifest, m); err != nil { + return fmt.Errorf("can't parse manifest file: %w", err) + } + + cs.NEF = &nf + cs.Manifest = m + return nil +} + +type ContractState struct { + NEF *nef.File + RawNEF []byte + Manifest *manifest.Manifest + RawManifest []byte + Hash util.Uint160 +}