From d5c7a40db97fb4fda23e1d3736f9cb4d226d1ee7 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 6 May 2021 23:29:43 +0300 Subject: [PATCH] stateroot: try finalizing stateroot when adding our signature And don't add/resend it multiple times. 1. We can be in a setup with one SV only and no AddSignature() called at all. 2. AddSignature() might add M-1 signatures and our signature should be the last one to complete MPTRoot, but we'll never do that. --- pkg/services/stateroot/network.go | 43 ++++++++++++++++------------ pkg/services/stateroot/signature.go | 10 ++++--- pkg/services/stateroot/validators.go | 1 + 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/pkg/services/stateroot/network.go b/pkg/services/stateroot/network.go index 185ec2951..58d065581 100644 --- a/pkg/services/stateroot/network.go +++ b/pkg/services/stateroot/network.go @@ -27,36 +27,27 @@ func (s *service) AddSignature(height uint32, validatorIndex int32, sig []byte) return nil } - pubs := s.GetStateValidators(height) - if validatorIndex < 0 || int(validatorIndex) >= len(pubs) { - return errors.New("invalid validator index") - } - pub := pubs[validatorIndex] - incRoot := s.getIncompleteRoot(height) if incRoot == nil { return nil } incRoot.Lock() + defer incRoot.Unlock() + + if validatorIndex < 0 || int(validatorIndex) >= len(incRoot.svList) { + return errors.New("invalid validator index") + } + + pub := incRoot.svList[validatorIndex] if incRoot.root != nil { ok := pub.VerifyHashable(sig, uint32(s.Network), incRoot.root) if !ok { - incRoot.Unlock() return fmt.Errorf("invalid state root signature for %d", validatorIndex) } } incRoot.addSignature(pub, sig) - sr, ready := incRoot.finalize(pubs) - incRoot.Unlock() - - if ready { - err := s.AddStateRoot(sr) - if err != nil { - s.log.Error("can't add validated state root", zap.Error(err)) - } - s.sendValidatedRoot(sr, acc) - } + s.trySendRoot(incRoot, acc) return nil } @@ -71,11 +62,27 @@ func (s *service) getIncompleteRoot(height uint32) *incompleteRoot { if incRoot, ok := s.incompleteRoots[height]; ok { return incRoot } - incRoot := &incompleteRoot{sigs: make(map[string]*rootSig)} + incRoot := &incompleteRoot{svList: s.GetStateValidators(height), sigs: make(map[string]*rootSig)} s.incompleteRoots[height] = incRoot return incRoot } +// trySendRoot attempts to finalize and send MPTRoot, it must be called with ir locked. +func (s *service) trySendRoot(ir *incompleteRoot, acc *wallet.Account) { + if ir.isSent { + return + } + sr, ready := ir.finalize() + if ready { + err := s.AddStateRoot(sr) + if err != nil { + s.log.Error("can't add validated state root", zap.Error(err)) + } + s.sendValidatedRoot(sr, acc) + ir.isSent = true + } +} + func (s *service) sendValidatedRoot(r *state.MPTRoot, acc *wallet.Account) { priv := acc.PrivateKey() w := io.NewBufBinWriter() diff --git a/pkg/services/stateroot/signature.go b/pkg/services/stateroot/signature.go index 5262473f0..980389bc9 100644 --- a/pkg/services/stateroot/signature.go +++ b/pkg/services/stateroot/signature.go @@ -15,6 +15,8 @@ import ( type ( incompleteRoot struct { sync.RWMutex + // svList is a list of state validator keys for this stateroot. + svList keys.PublicKeys // isSent is true state root was already broadcasted. isSent bool // request is oracle request. @@ -51,14 +53,14 @@ func (r *incompleteRoot) addSignature(pub *keys.PublicKey, sig []byte) { // finalize checks is either main or backup tx has sufficient number of signatures and returns // tx and bool value indicating if it is ready to be broadcasted. -func (r *incompleteRoot) finalize(stateValidators keys.PublicKeys) (*state.MPTRoot, bool) { +func (r *incompleteRoot) finalize() (*state.MPTRoot, bool) { if r.root == nil { return nil, false } - m := smartcontract.GetDefaultHonestNodeCount(len(stateValidators)) + m := smartcontract.GetDefaultHonestNodeCount(len(r.svList)) sigs := make([][]byte, 0, m) - for _, pub := range stateValidators { + for _, pub := range r.svList { sig, ok := r.sigs[string(pub.Bytes())] if ok && sig.ok { sigs = append(sigs, sig.sig) @@ -71,7 +73,7 @@ func (r *incompleteRoot) finalize(stateValidators keys.PublicKeys) (*state.MPTRo return nil, false } - verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(stateValidators) + verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(r.svList) if err != nil { return nil, false } diff --git a/pkg/services/stateroot/validators.go b/pkg/services/stateroot/validators.go index d33d0f5fc..bcfd78176 100644 --- a/pkg/services/stateroot/validators.go +++ b/pkg/services/stateroot/validators.go @@ -64,6 +64,7 @@ func (s *service) signAndSend(r *state.MPTRoot) error { incRoot.root = r incRoot.addSignature(acc.PrivateKey().PublicKey(), sig) incRoot.reverify(s.Network) + s.trySendRoot(incRoot, acc) incRoot.Unlock() msg := NewMessage(VoteT, &Vote{