github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/stateroot/validators.go (about) 1 package stateroot 2 3 import ( 4 "time" 5 6 "github.com/nspcc-dev/neo-go/pkg/core/state" 7 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 8 "github.com/nspcc-dev/neo-go/pkg/io" 9 "github.com/nspcc-dev/neo-go/pkg/network/payload" 10 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 11 "github.com/nspcc-dev/neo-go/pkg/wallet" 12 "go.uber.org/zap" 13 ) 14 15 const ( 16 voteValidEndInc = 10 17 firstVoteResendDelay = 3 * time.Second 18 ) 19 20 // Name returns service name. 21 func (s *service) Name() string { 22 return "stateroot" 23 } 24 25 // Start runs service instance in a separate goroutine. 26 // The service only starts once, subsequent calls to Start are no-op. 27 func (s *service) Start() { 28 if !s.started.CompareAndSwap(false, true) { 29 return 30 } 31 s.log.Info("starting state validation service") 32 go s.run() 33 } 34 35 func (s *service) run() { 36 s.chain.SubscribeForBlocks(s.blockCh) 37 runloop: 38 for { 39 select { 40 case b := <-s.blockCh: 41 r, err := s.GetStateRoot(b.Index) 42 if err != nil { 43 s.log.Error("can't get state root for new block", zap.Error(err)) 44 } else if err := s.signAndSend(r); err != nil { 45 s.log.Error("can't sign or send state root", zap.Error(err)) 46 } 47 s.srMtx.Lock() 48 delete(s.incompleteRoots, b.Index-voteValidEndInc) 49 s.srMtx.Unlock() 50 case <-s.stopCh: 51 break runloop 52 } 53 } 54 s.chain.UnsubscribeFromBlocks(s.blockCh) 55 drainloop: 56 for { 57 select { 58 case <-s.blockCh: 59 default: 60 break drainloop 61 } 62 } 63 close(s.blockCh) 64 close(s.done) 65 } 66 67 // Shutdown stops the service. It can only be called once, subsequent calls 68 // to Shutdown on the same instance are no-op. The instance that was stopped can 69 // not be started again by calling Start (use a new instance if needed). 70 func (s *service) Shutdown() { 71 if !s.started.CompareAndSwap(true, false) { 72 return 73 } 74 s.log.Info("stopping state validation service") 75 close(s.stopCh) 76 <-s.done 77 if s.wallet != nil { 78 s.wallet.Close() 79 } 80 _ = s.log.Sync() 81 } 82 83 func (s *service) signAndSend(r *state.MPTRoot) error { 84 if !s.MainCfg.Enabled { 85 return nil 86 } 87 88 myIndex, acc := s.getAccount() 89 if acc == nil { 90 return nil 91 } 92 93 sig := acc.SignHashable(s.Network, r) 94 incRoot := s.getIncompleteRoot(r.Index, myIndex) 95 incRoot.Lock() 96 defer incRoot.Unlock() 97 incRoot.root = r 98 incRoot.addSignature(acc.PublicKey(), sig) 99 incRoot.reverify(s.Network) 100 s.trySendRoot(incRoot, acc) 101 102 msg := NewMessage(VoteT, &Vote{ 103 ValidatorIndex: int32(myIndex), 104 Height: r.Index, 105 Signature: sig, 106 }) 107 108 w := io.NewBufBinWriter() 109 msg.EncodeBinary(w.BinWriter) 110 if w.Err != nil { 111 return w.Err 112 } 113 e := &payload.Extensible{ 114 Category: Category, 115 ValidBlockStart: r.Index, 116 ValidBlockEnd: r.Index + voteValidEndInc, 117 Sender: acc.ScriptHash(), 118 Data: w.Bytes(), 119 Witness: transaction.Witness{ 120 VerificationScript: acc.GetVerificationScript(), 121 }, 122 } 123 sig = acc.SignHashable(s.Network, e) 124 buf := io.NewBufBinWriter() 125 emit.Bytes(buf.BinWriter, sig) 126 e.Witness.InvocationScript = buf.Bytes() 127 incRoot.myVote = e 128 incRoot.retries = -1 129 s.sendVote(incRoot) 130 return nil 131 } 132 133 // sendVote attempts to send a vote if it's still valid and if stateroot message 134 // has not been sent yet. It must be called with the ir locked. 135 func (s *service) sendVote(ir *incompleteRoot) { 136 if ir.isSent || ir.retries >= s.maxRetries || 137 s.chain.HeaderHeight() >= ir.myVote.ValidBlockEnd { 138 return 139 } 140 s.relayExtensible(ir.myVote) 141 delay := firstVoteResendDelay 142 if ir.retries > 0 { 143 delay = s.timePerBlock << ir.retries 144 } 145 _ = time.AfterFunc(delay, func() { 146 ir.Lock() 147 s.sendVote(ir) 148 ir.Unlock() 149 }) 150 ir.retries++ 151 } 152 153 // getAccount returns the current index and account for the node running this service. 154 func (s *service) getAccount() (byte, *wallet.Account) { 155 s.accMtx.RLock() 156 defer s.accMtx.RUnlock() 157 return s.myIndex, s.acc 158 }