stateroot: only send stateroot message once per validators count

Actually fix #1922.
This commit is contained in:
Roman Khimov 2021-05-07 13:19:40 +03:00
parent 4a45abe3e0
commit 3d49f7d99a
5 changed files with 44 additions and 6 deletions

View file

@ -224,6 +224,15 @@ func TestStateRootFull(t *testing.T) {
require.NotNil(t, lastValidated.Load().(*payload.Extensible))
msg := new(stateroot.Message)
require.NoError(t, testserdes.DecodeBinary(lastValidated.Load().(*payload.Extensible).Data, msg))
require.NotEqual(t, stateroot.RootT, msg.Type) // not a sender for this root
r, err = srv.GetStateRoot(3)
require.NoError(t, err)
require.Error(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
require.NoError(t, srv.AddSignature(3, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
require.NotNil(t, lastValidated.Load().(*payload.Extensible))
require.NoError(t, testserdes.DecodeBinary(lastValidated.Load().(*payload.Extensible).Data, msg))
require.Equal(t, stateroot.RootT, msg.Type)

View file

@ -24,12 +24,12 @@ func (s *service) AddSignature(height uint32, validatorIndex int32, sig []byte)
if !s.MainCfg.Enabled {
return nil
}
_, acc := s.getAccount()
myIndex, acc := s.getAccount()
if acc == nil {
return nil
}
incRoot := s.getIncompleteRoot(height)
incRoot := s.getIncompleteRoot(height, myIndex)
if incRoot == nil {
return nil
}
@ -58,20 +58,24 @@ func (s *service) GetConfig() config.StateRoot {
return s.MainCfg
}
func (s *service) getIncompleteRoot(height uint32) *incompleteRoot {
func (s *service) getIncompleteRoot(height uint32, myIndex byte) *incompleteRoot {
s.srMtx.Lock()
defer s.srMtx.Unlock()
if incRoot, ok := s.incompleteRoots[height]; ok {
return incRoot
}
incRoot := &incompleteRoot{svList: s.GetStateValidators(height), sigs: make(map[string]*rootSig)}
incRoot := &incompleteRoot{
myIndex: int(myIndex),
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 {
if !ir.isSenderNow() {
return
}
sr, ready := ir.finalize()

View file

@ -118,6 +118,14 @@ func (s *service) OnPayload(ep *payload.Extensible) error {
s.log.Error("can't add SV-signed state root", zap.Error(err))
return nil
}
s.srMtx.Lock()
ir, ok := s.incompleteRoots[sr.Index]
s.srMtx.Unlock()
if ok {
ir.Lock()
ir.isSent = true
ir.Unlock()
}
return err
case VoteT:
v := m.Payload.(*Vote)

View file

@ -24,6 +24,8 @@ type (
root *state.MPTRoot
// sigs contains signature from every oracle node.
sigs map[string]*rootSig
// myIndex is the index of validator for this root.
myIndex int
// myVote is an extensible message containing node's vote.
myVote *payload.Extensible
// retries is a counter of send attempts.
@ -56,6 +58,21 @@ func (r *incompleteRoot) addSignature(pub *keys.PublicKey, sig []byte) {
}
}
func (r *incompleteRoot) isSenderNow() bool {
if r.root == nil || r.isSent || len(r.svList) == 0 {
return false
}
retries := r.retries
if retries < 0 {
retries = 0
}
ind := (int(r.root.Index) - retries) % len(r.svList)
if ind < 0 {
ind += len(r.svList)
}
return ind == r.myIndex
}
// 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() (*state.MPTRoot, bool) {

View file

@ -66,7 +66,7 @@ func (s *service) signAndSend(r *state.MPTRoot) error {
}
sig := acc.PrivateKey().SignHashable(uint32(s.Network), r)
incRoot := s.getIncompleteRoot(r.Index)
incRoot := s.getIncompleteRoot(r.Index, myIndex)
incRoot.Lock()
defer incRoot.Unlock()
incRoot.root = r