diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b0b35e9c..0bacb05a65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Changelog for NeoFS Node ### Fixed - `neo-go` RPC connection loss handling (#1337) - Concurrent morph cache misses (#1248) +- Double voting for validators on IR startup (#2365) ### Removed - Deprecated `morph.rpc_endpoint` SN and `morph.endpoint.client` IR config sections (#2400) diff --git a/pkg/innerring/state.go b/pkg/innerring/state.go index 153a620a7e..a6df68fe5d 100644 --- a/pkg/innerring/state.go +++ b/pkg/innerring/state.go @@ -4,6 +4,7 @@ import ( "fmt" "sort" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/governance" auditClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/audit" @@ -108,13 +109,21 @@ func (s *Server) voteForSidechainValidator(prm governance.VoteValidatorPrm) erro return nil } + voted, err := s.alreadyVoted(validators) + if err != nil { + return fmt.Errorf("could not check validators state: %w", err) + } + + if voted { + return nil + } + epoch := s.EpochCounter() var ( nonce uint32 = 1 vub uint32 vubP *uint32 - err error ) if prm.Hash != nil { @@ -138,6 +147,30 @@ func (s *Server) voteForSidechainValidator(prm governance.VoteValidatorPrm) erro return nil } +func (s *Server) alreadyVoted(validatorsToVote keys.PublicKeys) (bool, error) { + currentValidators := make(map[keys.PublicKey]struct{}, len(s.contracts.alphabet)) + for letter, contract := range s.contracts.alphabet { + validator, err := s.morphClient.AccountVote(contract) + if err != nil { + return false, fmt.Errorf("receiving %s's vote: %w", letter, err) + } + + if validator == nil { + continue + } + + currentValidators[*validator] = struct{}{} + } + + for _, v := range validatorsToVote { + if _, voted := currentValidators[*v]; !voted { + return false, nil + } + } + + return true, nil +} + // VoteForSidechainValidator calls vote method on alphabet contracts with // the provided list of keys. func (s *Server) VoteForSidechainValidator(prm governance.VoteValidatorPrm) error { diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index cf7351c996..3f019cf9b4 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -421,7 +421,8 @@ func (c *Client) IsValidScript(script []byte, signers []transaction.Signer) (res } // AccountVote returns a key the provided account has voted with its NEO -// tokens for. Nil key with no error is returned if the account has no NEO. +// tokens for. Nil key with no error is returned if the account has no NEO +// or if the account hasn't voted for anyone. func (c *Client) AccountVote(addr util.Uint160) (*keys.PublicKey, error) { c.switchLock.RLock() defer c.switchLock.RUnlock()