From 3d49f7d99a37e5af8b2a8f44ad0b44b9c12313da Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 7 May 2021 13:19:40 +0300 Subject: [PATCH] stateroot: only send stateroot message once per validators count Actually fix #1922. --- pkg/core/stateroot_test.go | 9 +++++++++ pkg/services/stateroot/network.go | 14 +++++++++----- pkg/services/stateroot/service.go | 8 ++++++++ pkg/services/stateroot/signature.go | 17 +++++++++++++++++ pkg/services/stateroot/validators.go | 2 +- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/pkg/core/stateroot_test.go b/pkg/core/stateroot_test.go index 8d70ed45e..fafdc0c82 100644 --- a/pkg/core/stateroot_test.go +++ b/pkg/core/stateroot_test.go @@ -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) diff --git a/pkg/services/stateroot/network.go b/pkg/services/stateroot/network.go index dba8cb216..a6c914c16 100644 --- a/pkg/services/stateroot/network.go +++ b/pkg/services/stateroot/network.go @@ -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() diff --git a/pkg/services/stateroot/service.go b/pkg/services/stateroot/service.go index e569f16b1..3b01a560e 100644 --- a/pkg/services/stateroot/service.go +++ b/pkg/services/stateroot/service.go @@ -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) diff --git a/pkg/services/stateroot/signature.go b/pkg/services/stateroot/signature.go index 627193dfd..fb989fb00 100644 --- a/pkg/services/stateroot/signature.go +++ b/pkg/services/stateroot/signature.go @@ -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) { diff --git a/pkg/services/stateroot/validators.go b/pkg/services/stateroot/validators.go index 821e618c3..54150270f 100644 --- a/pkg/services/stateroot/validators.go +++ b/pkg/services/stateroot/validators.go @@ -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