[#341] Register candidates in separate transactions #349

Merged
fyrchik merged 1 commit from ale64bit/frostfs-node:fix/341-register-candidate-batching into master 2023-05-15 12:46:13 +00:00
2 changed files with 54 additions and 24 deletions

View file

@ -19,33 +19,24 @@ import (
) )
// initialAlphabetNEOAmount represents the total amount of GAS distributed between alphabet nodes. // initialAlphabetNEOAmount represents the total amount of GAS distributed between alphabet nodes.
const initialAlphabetNEOAmount = native.NEOTotalSupply const (
initialAlphabetNEOAmount = native.NEOTotalSupply
func (c *initializeContext) registerCandidates() error { registerBatchSize = transaction.MaxAttributes - 1
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
}
func (c *initializeContext) registerCandidateRange(start, end int) error {
regPrice, err := c.getCandidateRegisterPrice() regPrice, err := c.getCandidateRegisterPrice()
if err != nil { if err != nil {
return fmt.Errorf("can't fetch registration price: %w", err) return fmt.Errorf("can't fetch registration price: %w", err)
} }
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCall(w.BinWriter, neoHash, "setRegisterPrice", callflag.States, 1) emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, 1)
for _, acc := range c.Accounts { for _, acc := range c.Accounts[start:end] {
emit.AppCall(w.BinWriter, neoHash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes()) emit.AppCall(w.BinWriter, neo.Hash, "registerCandidate", callflag.States, acc.PrivateKey().PublicKey().Bytes())
emit.Opcodes(w.BinWriter, opcode.ASSERT) 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 { if w.Err != nil {
panic(fmt.Sprintf("BUG: %v", w.Err)) panic(fmt.Sprintf("BUG: %v", w.Err))
} }
@ -54,14 +45,14 @@ func (c *initializeContext) registerCandidates() error {
Signer: c.getSigner(false, c.CommitteeAcc), Signer: c.getSigner(false, c.CommitteeAcc),
Account: c.CommitteeAcc, Account: c.CommitteeAcc,
}} }}
for i := range c.Accounts { for _, acc := range c.Accounts[start:end] {
signers = append(signers, rpcclient.SignerAccount{ signers = append(signers, rpcclient.SignerAccount{
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: c.Accounts[i].Contract.ScriptHash(), Account: acc.Contract.ScriptHash(),
Scopes: transaction.CustomContracts, 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() network := c.CommitteeAct.GetNetwork()
for i := range c.Accounts { for _, acc := range c.Accounts[start:end] {
if err := c.Accounts[i].SignTx(network, tx); err != nil { if err := acc.SignTx(network, tx); err != nil {
return fmt.Errorf("can't sign a transaction: %w", err) 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) 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 { func (c *initializeContext) transferNEOToAlphabetContracts() error {
neoHash := neo.Hash neoHash := neo.Hash

View file

@ -37,6 +37,12 @@ func TestInitialize(t *testing.T) {
t.Run("7 nodes", func(t *testing.T) { t.Run("7 nodes", func(t *testing.T) {
testInitialize(t, 7) 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)
fyrchik marked this conversation as resolved
Review

So we do not have any more problems? Or it fails with 23?

So we do not have any more problems? Or it fails with 23?
Review

It does fail with >=23, due to the second failure described in #295.

It does fail with >=23, due to the second failure described in #295.
})
} }
func testInitialize(t *testing.T, committeeSize int) { func testInitialize(t *testing.T, committeeSize int) {