From d803c225ab1b1b721d63b519d1006ad97509717a Mon Sep 17 00:00:00 2001 From: Alejandro Lopez Date: Mon, 15 May 2023 14:46:17 +0300 Subject: [PATCH] [#341] Register candidates in separate transactions Signed-off-by: Alejandro Lopez --- .../modules/morph/initialize_register.go | 72 ++++++++++++------- .../internal/modules/morph/initialize_test.go | 6 ++ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_register.go b/cmd/frostfs-adm/internal/modules/morph/initialize_register.go index b1542cc92..469b269de 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_register.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_register.go @@ -19,33 +19,24 @@ import ( ) // initialAlphabetNEOAmount represents the total amount of GAS distributed between alphabet nodes. -const initialAlphabetNEOAmount = native.NEOTotalSupply - -func (c *initializeContext) registerCandidates() error { - neoHash := neo.Hash - - cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neoHash, "getCandidates")) - if err != nil { - return fmt.Errorf("`getCandidates`: %w", err) - } - - if len(cc) > 0 { - c.Command.Println("Candidates are already registered.") - return nil - } +const ( + initialAlphabetNEOAmount = native.NEOTotalSupply + registerBatchSize = transaction.MaxAttributes - 1 +) +func (c *initializeContext) registerCandidateRange(start, end int) error { regPrice, err := c.getCandidateRegisterPrice() if err != nil { return fmt.Errorf("can't fetch registration price: %w", err) } w := io.NewBufBinWriter() - emit.AppCall(w.BinWriter, neoHash, "setRegisterPrice", callflag.States, 1) - for _, acc := range c.Accounts { - emit.AppCall(w.BinWriter, neoHash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes()) + emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, 1) + for _, acc := range c.Accounts[start:end] { + emit.AppCall(w.BinWriter, neo.Hash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes()) emit.Opcodes(w.BinWriter, opcode.ASSERT) } - emit.AppCall(w.BinWriter, neoHash, "setRegisterPrice", callflag.States, regPrice) + emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, regPrice) if w.Err != nil { panic(fmt.Sprintf("BUG: %v", w.Err)) } @@ -54,14 +45,14 @@ func (c *initializeContext) registerCandidates() error { Signer: c.getSigner(false, c.CommitteeAcc), Account: c.CommitteeAcc, }} - for i := range c.Accounts { + for _, acc := range c.Accounts[start:end] { signers = append(signers, rpcclient.SignerAccount{ Signer: transaction.Signer{ - Account: c.Accounts[i].Contract.ScriptHash(), + Account: acc.Contract.ScriptHash(), Scopes: transaction.CustomContracts, - AllowedContracts: []util.Uint160{neoHash}, + AllowedContracts: []util.Uint160{neo.Hash}, }, - Account: c.Accounts[i], + Account: acc, }) } @@ -74,8 +65,8 @@ func (c *initializeContext) registerCandidates() error { } network := c.CommitteeAct.GetNetwork() - for i := range c.Accounts { - if err := c.Accounts[i].SignTx(network, tx); err != nil { + for _, acc := range c.Accounts[start:end] { + if err := acc.SignTx(network, tx); err != nil { return fmt.Errorf("can't sign a transaction: %w", err) } } @@ -83,6 +74,39 @@ func (c *initializeContext) registerCandidates() error { return c.sendTx(tx, c.Command, true) } +func (c *initializeContext) registerCandidates() error { + cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neo.Hash, "getCandidates")) + if err != nil { + return fmt.Errorf("`getCandidates`: %w", err) + } + + need := len(c.Accounts) + have := len(cc) + + if need == have { + c.Command.Println("Candidates are already registered.") + return nil + } + + // Register candidates in batches in order to overcome the signers amount limit. + // See: https://github.com/nspcc-dev/neo-go/blob/master/pkg/core/transaction/transaction.go#L27 + for i := 0; i < need; i += registerBatchSize { + start, end := i, i+registerBatchSize + if end > need { + end = need + } + // This check is sound because transactions are accepted/rejected atomically. + if have >= end { + continue + } + if err := c.registerCandidateRange(start, end); err != nil { + return fmt.Errorf("registering candidates %d..%d: %q", start, end-1, err) + } + } + + return nil +} + func (c *initializeContext) transferNEOToAlphabetContracts() error { neoHash := neo.Hash diff --git a/cmd/frostfs-adm/internal/modules/morph/initialize_test.go b/cmd/frostfs-adm/internal/modules/morph/initialize_test.go index fb2dc3e3f..e2e5aa0ad 100644 --- a/cmd/frostfs-adm/internal/modules/morph/initialize_test.go +++ b/cmd/frostfs-adm/internal/modules/morph/initialize_test.go @@ -37,6 +37,12 @@ func TestInitialize(t *testing.T) { t.Run("7 nodes", func(t *testing.T) { testInitialize(t, 7) }) + t.Run("16 nodes", func(t *testing.T) { + testInitialize(t, 16) + }) + t.Run("22 nodes", func(t *testing.T) { + testInitialize(t, 22) + }) } func testInitialize(t *testing.T, committeeSize int) {