diff --git a/cmd/neofs-adm/internal/modules/morph/initialize.go b/cmd/neofs-adm/internal/modules/morph/initialize.go index 045d1d4be6..1d3575375b 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize.go @@ -68,8 +68,15 @@ func initializeSideChainCmd(cmd *cobra.Command, args []string) error { return err } - // TODO 5. Setup NeoFS contracts addresses in NNS. - // TODO 6. Register candidates and call alphabet.Vote. + cmd.Println("Stage 5: register candidates.") + if err := initCtx.registerCandidates(); err != nil { + return err + } + + cmd.Println("Stage 6: transfer NEO to alphabet contracts.") + if err := initCtx.transferNEOToAlphabetContracts(); err != nil { + return err + } cmd.Println("endpoint:", viper.GetString(endpointFlag)) cmd.Println("alphabet-wallets:", viper.GetString(alphabetWalletsFlag)) diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_register.go b/cmd/neofs-adm/internal/modules/morph/initialize_register.go new file mode 100644 index 0000000000..1a53c38122 --- /dev/null +++ b/cmd/neofs-adm/internal/modules/morph/initialize_register.go @@ -0,0 +1,119 @@ +package morph + +import ( + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/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" + "github.com/nspcc-dev/neo-go/pkg/vm" + "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" +) + +// initialAlphabetNEOAmount represents total amount of GAS distributed between alphabet nodes. +const initialAlphabetNEOAmount = native.NEOTotalSupply + +func (c *initializeContext) registerCandidates() error { + neoHash, err := c.Client.GetNativeContractHash(nativenames.Neo) + if err != nil { + return err + } + + res, err := c.Client.InvokeFunction(neoHash, "getCandidates", []smartcontract.Parameter{}, nil) + if err != nil { + return err + } + if res.State == vm.HaltState.String() && len(res.Stack) > 0 { + arr, ok := res.Stack[0].Value().([]stackitem.Item) + if ok && len(arr) > 0 { + return nil + } + } + + regPrice, err := c.Client.GetCandidateRegisterPrice() + if err != nil { + return fmt.Errorf("can't fetch registration price: %w", err) + } + + sysGas := regPrice + native.GASFactor // + 1 GAS + for _, w := range c.Wallets { + acc, err := getWalletAccount(w, singleAccountName) + if err != nil { + return err + } + + w := io.NewBufBinWriter() + emit.AppCall(w.BinWriter, neoHash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes()) + emit.Opcodes(w.BinWriter, opcode.ASSERT) + + h, err := c.Client.SignAndPushInvocationTx(w.Bytes(), acc, sysGas, 0, []client.SignerAccount{{ + Signer: transaction.Signer{ + Account: acc.Contract.ScriptHash(), + Scopes: transaction.CalledByEntry, + }, + Account: acc, + }}) + if err != nil { + return err + } + + c.Hashes = append(c.Hashes, h) + } + + return c.awaitTx() +} + +func (c *initializeContext) transferNEOToAlphabetContracts() error { + neoHash, err := c.Client.GetNativeContractHash(nativenames.Neo) + if err != nil { + return err + } + + ok, err := c.transferNEOFinished(neoHash) + if ok || err != nil { + return err + } + + ctrPath, err := c.Command.Flags().GetString(contractsInitFlag) + if err != nil { + return fmt.Errorf("missing contracts path: %w", err) + } + + cs, err := c.readContract(ctrPath, alphabetContract) + if err != nil { + return fmt.Errorf("can't read alphabet contract: %w", err) + } + + amount := initialAlphabetNEOAmount / len(c.Wallets) + + bw := io.NewBufBinWriter() + for _, w := range c.Wallets { + acc, err := getWalletAccount(w, singleAccountName) + if err != nil { + return err + } + + h := state.CreateContractHash(acc.Contract.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) + emit.AppCall(bw.BinWriter, neoHash, "transfer", callflag.All, + c.CommitteeAcc.Contract.ScriptHash(), h, int64(amount), nil) + } + + if err := c.sendCommitteeTx(bw.Bytes(), -1); err != nil { + return err + } + + return c.awaitTx() +} + +func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, error) { + bal, err := c.Client.NEP17BalanceOf(neoHash, c.CommitteeAcc.Contract.ScriptHash()) + return bal < native.NEOTotalSupply, err +}