From 33c3f18b4fe9412a170990f3886d25fcea035b28 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 6 Aug 2021 14:28:07 +0300 Subject: [PATCH] [#748] neofs-adm: allow to update contracts Signed-off-by: Evgenii Stratonikov --- .../internal/modules/morph/initialize.go | 5 +- .../modules/morph/initialize_deploy.go | 32 ++++++-- .../internal/modules/morph/initialize_nns.go | 78 +++++++++++-------- cmd/neofs-adm/internal/modules/morph/root.go | 15 ++++ .../internal/modules/morph/update.go | 21 +++++ 5 files changed, 112 insertions(+), 39 deletions(-) create mode 100644 cmd/neofs-adm/internal/modules/morph/update.go diff --git a/cmd/neofs-adm/internal/modules/morph/initialize.go b/cmd/neofs-adm/internal/modules/morph/initialize.go index 34cd6971c..cbf98a630 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize.go @@ -62,7 +62,7 @@ func initializeSideChainCmd(cmd *cobra.Command, args []string) error { // 4. Deploy NeoFS contracts. cmd.Println("Stage 4: deploy NeoFS contracts.") - if err := initCtx.deployContracts(); err != nil { + if err := initCtx.deployContracts("deploy"); err != nil { return err } @@ -120,7 +120,8 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex if viper.GetInt64(maxObjectSizeInitFlag) <= 0 { return nil, fmt.Errorf("max object size must be positive") } - + } + if cmd.Name() == "update-contracts" || cmd.Name() == "init" { ctrPath, err = cmd.Flags().GetString(contractsInitFlag) if err != nil { return nil, fmt.Errorf("missing contracts path: %w", err) diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go index 3b6988cc8..d3f7ae73c 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go @@ -103,7 +103,7 @@ func (c *initializeContext) deployNNS() error { return c.awaitTx() } -func (c *initializeContext) deployContracts() error { +func (c *initializeContext) deployContracts(method string) error { mgmtHash := c.nativeHash(nativenames.Management) sender := c.CommitteeAcc.Contract.ScriptHash() for _, ctrName := range contractList { @@ -119,16 +119,30 @@ func (c *initializeContext) deployContracts() error { return err } + nnsCs, err := c.Client.GetContractStateByID(1) + if err != nil { + return err + } + nnsHash := nnsCs.Hash + var keysParam []smartcontract.Parameter // 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 _, err := c.Client.GetContractStateByHash(ctrHash); err == nil { - c.Command.Printf("Stage 4: alphabet contract #%d is already deployed.\n", i) + c.Command.Printf("Alphabet contract #%d is already deployed.\n", i) continue } + invokeHash := mgmtHash + if method == "migrate" { + invokeHash, err = nnsResolveHash(c.Client, nnsHash, getAlphabetNNSDomain(i)) + if err != nil { + return fmt.Errorf("can't resolve hash for contract update: %w", err) + } + } + keysParam = append(keysParam, smartcontract.Parameter{ Type: smartcontract.PublicKeyType, Value: acc.PrivateKey().PublicKey().Bytes(), @@ -140,7 +154,7 @@ func (c *initializeContext) deployContracts() error { Account: acc.Contract.ScriptHash(), Scopes: transaction.CalledByEntry, } - res, err := c.Client.InvokeFunction(mgmtHash, "deploy", params, []transaction.Signer{signer}) + res, err := c.Client.InvokeFunction(invokeHash, method, params, []transaction.Signer{signer}) if err != nil { return fmt.Errorf("can't deploy alphabet #%d contract: %w", i, err) } @@ -158,10 +172,18 @@ func (c *initializeContext) deployContracts() error { for _, ctrName := range contractList { cs := c.Contracts[ctrName] if _, err := c.Client.GetContractStateByHash(cs.Hash); err == nil { - c.Command.Printf("Stage 4: %s contract is already deployed.\n", ctrName) + c.Command.Printf("%s contract is already deployed.\n", ctrName) continue } + invokeHash := mgmtHash + if method == "migrate" { + invokeHash, err = nnsResolveHash(c.Client, nnsHash, ctrName+".neofs") + if err != nil { + return fmt.Errorf("can't resolve hash for contract update: %w", err) + } + } + params := getContractDeployParameters(cs.RawNEF, cs.RawManifest, c.getContractDeployData(ctrName, keysParam)) signer := transaction.Signer{ @@ -169,7 +191,7 @@ func (c *initializeContext) deployContracts() error { Scopes: transaction.CalledByEntry, } - res, err := c.Client.InvokeFunction(mgmtHash, "deploy", params, []transaction.Signer{signer}) + res, err := c.Client.InvokeFunction(invokeHash, method, params, []transaction.Signer{signer}) if err != nil { return fmt.Errorf("can't deploy contract: %w", err) } diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_nns.go b/cmd/neofs-adm/internal/modules/morph/initialize_nns.go index 56d03ac95..b4d436985 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_nns.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_nns.go @@ -8,6 +8,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" @@ -49,21 +50,7 @@ func (c *initializeContext) setNNS() error { alphaCs.Hash = state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name) domain := getAlphabetNNSDomain(i) - if ok, err := c.Client.NNSIsAvailable(h, domain); err != nil { - return err - } else if !ok { - continue - } - - bw := io.NewBufBinWriter() - emit.AppCall(bw.BinWriter, h, "register", callflag.All, - domain, c.CommitteeAcc.Contract.ScriptHash()) - emit.Opcodes(bw.BinWriter, opcode.ASSERT) - emit.AppCall(bw.BinWriter, h, "setRecord", callflag.All, - domain, int64(nns.TXT), alphaCs.Hash.StringLE()) - - sysFee := int64(defaultRegisterSysfee + native.GASFactor) - if err := c.sendCommitteeTx(bw.Bytes(), sysFee); err != nil { + if err := c.nnsRegisterDomain(h, alphaCs.Hash, domain); err != nil { return err } } @@ -76,21 +63,7 @@ func (c *initializeContext) setNNS() error { cs.Hash = state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) domain := ctrName + ".neofs" - if ok, err := c.Client.NNSIsAvailable(h, domain); err != nil { - return err - } else if !ok { - continue - } - - bw := io.NewBufBinWriter() - emit.AppCall(bw.BinWriter, h, "register", callflag.All, - domain, c.CommitteeAcc.Contract.ScriptHash()) - emit.Opcodes(bw.BinWriter, opcode.ASSERT) - emit.AppCall(bw.BinWriter, h, "setRecord", callflag.All, - domain, int64(nns.TXT), cs.Hash.StringLE()) - - sysFee := int64(defaultRegisterSysfee + native.GASFactor) - if err := c.sendCommitteeTx(bw.Bytes(), sysFee); err != nil { + if err := c.nnsRegisterDomain(h, cs.Hash, domain); err != nil { return err } } @@ -98,6 +71,42 @@ func (c *initializeContext) setNNS() error { return c.awaitTx() } +func getAlphabetNNSDomain(i int) string { + return alphabetContract + strconv.FormatUint(uint64(i), 10) + ".neofs" +} + +func (c *initializeContext) nnsRegisterDomain(nnsHash, expectedHash util.Uint160, domain string) error { + ok, err := c.Client.NNSIsAvailable(nnsHash, domain) + if err != nil { + return err + } + + bw := io.NewBufBinWriter() + if ok { + emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All, + domain, c.CommitteeAcc.Contract.ScriptHash()) + emit.Opcodes(bw.BinWriter, opcode.ASSERT) + } else { + s, err := c.Client.NNSResolve(nnsHash, domain, nns.TXT) + if err != nil { + return err + } + if s == expectedHash.StringLE() { + return nil + } + } + + emit.AppCall(bw.BinWriter, nnsHash, "setRecord", callflag.All, + domain, int64(nns.TXT), expectedHash.StringLE()) + + if bw.Err != nil { + panic(bw.Err) + } + + sysFee := int64(defaultRegisterSysfee + native.GASFactor) + return c.sendCommitteeTx(bw.Bytes(), sysFee) +} + func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160) (bool, error) { params := []smartcontract.Parameter{{Type: smartcontract.StringType, Value: "name.neofs"}} res, err := c.Client.InvokeFunction(nnsHash, "isAvailable", params, nil) @@ -107,6 +116,11 @@ func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160) (bool, error return res.State == vm.HaltState.String(), nil } -func getAlphabetNNSDomain(i int) string { - return alphabetContract + strconv.FormatUint(uint64(i), 10) + ".neofs" +func nnsResolveHash(c *client.Client, nnsHash util.Uint160, domain string) (util.Uint160, error) { + s, err := c.NNSResolve(nnsHash, domain, nns.TXT) + if err != nil { + return util.Uint160{}, err + } + + return util.Uint160DecodeStringLE(s) } diff --git a/cmd/neofs-adm/internal/modules/morph/root.go b/cmd/neofs-adm/internal/modules/morph/root.go index 669b98dc8..42e5b3fa9 100644 --- a/cmd/neofs-adm/internal/modules/morph/root.go +++ b/cmd/neofs-adm/internal/modules/morph/root.go @@ -101,6 +101,16 @@ var ( }, RunE: dumpNetworkConfig, } + + updateContractsCmd = &cobra.Command{ + Use: "update-contracts", + Short: "Update NeoFS contracts.", + PreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag)) + _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) + }, + RunE: updateContracts, + } ) func init() { @@ -130,4 +140,9 @@ func init() { RootCmd.AddCommand(dumpNetworkConfigCmd) dumpNetworkConfigCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + + RootCmd.AddCommand(updateContractsCmd) + updateContractsCmd.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir") + updateContractsCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + updateContractsCmd.Flags().String(contractsInitFlag, "", "path to archive with compiled NeoFS contracts (default fetched from latest github release)") } diff --git a/cmd/neofs-adm/internal/modules/morph/update.go b/cmd/neofs-adm/internal/modules/morph/update.go new file mode 100644 index 000000000..ad17544cf --- /dev/null +++ b/cmd/neofs-adm/internal/modules/morph/update.go @@ -0,0 +1,21 @@ +package morph + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func updateContracts(cmd *cobra.Command, _ []string) error { + wCtx, err := newInitializeContext(cmd, viper.GetViper()) + if err != nil { + return fmt.Errorf("initialization error: %w", err) + } + + if err := wCtx.deployContracts("migrate"); err != nil { + return err + } + + return wCtx.setNNS() +}