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.
This commit is contained in:
Roman Khimov 2021-05-06 23:29:43 +03:00
parent 847927af74
commit d5c7a40db9
3 changed files with 32 additions and 22 deletions

View file

@ -27,36 +27,27 @@ func (s *service) AddSignature(height uint32, validatorIndex int32, sig []byte)
return nil 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) incRoot := s.getIncompleteRoot(height)
if incRoot == nil { if incRoot == nil {
return nil return nil
} }
incRoot.Lock() 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 { if incRoot.root != nil {
ok := pub.VerifyHashable(sig, uint32(s.Network), incRoot.root) ok := pub.VerifyHashable(sig, uint32(s.Network), incRoot.root)
if !ok { if !ok {
incRoot.Unlock()
return fmt.Errorf("invalid state root signature for %d", validatorIndex) return fmt.Errorf("invalid state root signature for %d", validatorIndex)
} }
} }
incRoot.addSignature(pub, sig) incRoot.addSignature(pub, sig)
sr, ready := incRoot.finalize(pubs) s.trySendRoot(incRoot, acc)
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)
}
return nil return nil
} }
@ -71,11 +62,27 @@ func (s *service) getIncompleteRoot(height uint32) *incompleteRoot {
if incRoot, ok := s.incompleteRoots[height]; ok { if incRoot, ok := s.incompleteRoots[height]; ok {
return incRoot return incRoot
} }
incRoot := &incompleteRoot{sigs: make(map[string]*rootSig)} incRoot := &incompleteRoot{svList: s.GetStateValidators(height), sigs: make(map[string]*rootSig)}
s.incompleteRoots[height] = incRoot s.incompleteRoots[height] = incRoot
return 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) { func (s *service) sendValidatedRoot(r *state.MPTRoot, acc *wallet.Account) {
priv := acc.PrivateKey() priv := acc.PrivateKey()
w := io.NewBufBinWriter() w := io.NewBufBinWriter()

View file

@ -15,6 +15,8 @@ import (
type ( type (
incompleteRoot struct { incompleteRoot struct {
sync.RWMutex 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 is true state root was already broadcasted.
isSent bool isSent bool
// request is oracle request. // 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 // 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. // 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 { if r.root == nil {
return nil, false return nil, false
} }
m := smartcontract.GetDefaultHonestNodeCount(len(stateValidators)) m := smartcontract.GetDefaultHonestNodeCount(len(r.svList))
sigs := make([][]byte, 0, m) sigs := make([][]byte, 0, m)
for _, pub := range stateValidators { for _, pub := range r.svList {
sig, ok := r.sigs[string(pub.Bytes())] sig, ok := r.sigs[string(pub.Bytes())]
if ok && sig.ok { if ok && sig.ok {
sigs = append(sigs, sig.sig) sigs = append(sigs, sig.sig)
@ -71,7 +73,7 @@ func (r *incompleteRoot) finalize(stateValidators keys.PublicKeys) (*state.MPTRo
return nil, false return nil, false
} }
verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(stateValidators) verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(r.svList)
if err != nil { if err != nil {
return nil, false return nil, false
} }

View file

@ -64,6 +64,7 @@ func (s *service) signAndSend(r *state.MPTRoot) error {
incRoot.root = r incRoot.root = r
incRoot.addSignature(acc.PrivateKey().PublicKey(), sig) incRoot.addSignature(acc.PrivateKey().PublicKey(), sig)
incRoot.reverify(s.Network) incRoot.reverify(s.Network)
s.trySendRoot(incRoot, acc)
incRoot.Unlock() incRoot.Unlock()
msg := NewMessage(VoteT, &Vote{ msg := NewMessage(VoteT, &Vote{