github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/eventcheck/heavycheck/heavy_check.go (about) 1 package heavycheck 2 3 import ( 4 "bytes" 5 "errors" 6 "runtime" 7 "sync" 8 9 "github.com/unicornultrafoundation/go-helios/hash" 10 "github.com/unicornultrafoundation/go-helios/native/idx" 11 "github.com/unicornultrafoundation/go-u2u/core/types" 12 "github.com/unicornultrafoundation/go-u2u/crypto" 13 14 "github.com/unicornultrafoundation/go-u2u/eventcheck/basiccheck" 15 "github.com/unicornultrafoundation/go-u2u/eventcheck/epochcheck" 16 "github.com/unicornultrafoundation/go-u2u/native" 17 "github.com/unicornultrafoundation/go-u2u/native/validatorpk" 18 ) 19 20 var ( 21 ErrWrongEventSig = errors.New("event has wrong signature") 22 ErrMalformedTxSig = errors.New("tx has wrong signature") 23 ErrWrongPayloadHash = errors.New("event has wrong payload hash") 24 ErrPubkeyChanged = errors.New("validator pubkey has changed, cannot create BVs/EV for older epochs") 25 ErrUnknownEpochEventLocator = errors.New("event locator has unknown epoch") 26 ErrImpossibleBVsEpoch = errors.New("BVs have an impossible epoch") 27 ErrUnknownEpochBVs = errors.New("BVs are unprocessable yet") 28 ErrUnknownEpochEV = errors.New("EV is unprocessable yet") 29 30 errTerminated = errors.New("terminated") // internal err 31 ) 32 33 const ( 34 // MaxBlocksPerEpoch is chosen so that even if validator chooses the latest non-liable epoch for BVs, 35 // he still cannot vote for latest blocks (latest = from last 128 epochs), as an epoch has at least one block 36 // The value is larger than a maximum possible number of blocks 37 // in an epoch where a single validator doesn't have 2/3W+1 weight 38 MaxBlocksPerEpoch = idx.Block(basiccheck.MaxLiableEpochs - 128) 39 ) 40 41 // Reader is accessed by the validator to get the current state. 42 type Reader interface { 43 GetEpochPubKeys() (map[idx.ValidatorID]validatorpk.PubKey, idx.Epoch) 44 GetEpochPubKeysOf(idx.Epoch) map[idx.ValidatorID]validatorpk.PubKey 45 GetEpochBlockStart(idx.Epoch) idx.Block 46 } 47 48 // Checker which requires only parents list + current epoch info 49 type Checker struct { 50 config Config 51 txSigner types.Signer 52 reader Reader 53 54 tasksQ chan *taskData 55 quit chan struct{} 56 wg sync.WaitGroup 57 } 58 59 type taskData struct { 60 event native.EventPayloadI 61 bvs *native.LlrSignedBlockVotes 62 ev *native.LlrSignedEpochVote 63 64 onValidated func(error) 65 } 66 67 // New validator which performs heavy checks, related to signatures validation and Merkle tree validation 68 func New(config Config, reader Reader, txSigner types.Signer) *Checker { 69 if config.Threads == 0 { 70 config.Threads = runtime.NumCPU() 71 if config.Threads > 1 { 72 config.Threads-- 73 } 74 if config.Threads < 1 { 75 config.Threads = 1 76 } 77 } 78 return &Checker{ 79 config: config, 80 txSigner: txSigner, 81 reader: reader, 82 tasksQ: make(chan *taskData, config.MaxQueuedTasks), 83 quit: make(chan struct{}), 84 } 85 } 86 87 func (v *Checker) Start() { 88 for i := 0; i < v.config.Threads; i++ { 89 v.wg.Add(1) 90 go v.loop() 91 } 92 } 93 94 func (v *Checker) Stop() { 95 close(v.quit) 96 v.wg.Wait() 97 } 98 99 func (v *Checker) Overloaded() bool { 100 return len(v.tasksQ) > v.config.MaxQueuedTasks/2 101 } 102 103 func (v *Checker) EnqueueEvent(e native.EventPayloadI, onValidated func(error)) error { 104 op := &taskData{ 105 event: e, 106 onValidated: onValidated, 107 } 108 select { 109 case v.tasksQ <- op: 110 return nil 111 case <-v.quit: 112 return errTerminated 113 } 114 } 115 116 func (v *Checker) EnqueueBVs(bvs native.LlrSignedBlockVotes, onValidated func(error)) error { 117 op := &taskData{ 118 bvs: &bvs, 119 onValidated: onValidated, 120 } 121 select { 122 case v.tasksQ <- op: 123 return nil 124 case <-v.quit: 125 return errTerminated 126 } 127 } 128 129 func (v *Checker) EnqueueEV(ev native.LlrSignedEpochVote, onValidated func(error)) error { 130 op := &taskData{ 131 ev: &ev, 132 onValidated: onValidated, 133 } 134 select { 135 case v.tasksQ <- op: 136 return nil 137 case <-v.quit: 138 return errTerminated 139 } 140 } 141 142 // verifySignature checks the signature against e.Creator. 143 func verifySignature(signedHash hash.Hash, sig native.Signature, pubkey validatorpk.PubKey) bool { 144 if pubkey.Type != validatorpk.Types.Secp256k1 { 145 return false 146 } 147 return crypto.VerifySignature(pubkey.Raw, signedHash.Bytes(), sig.Bytes()) 148 } 149 150 func (v *Checker) ValidateEventLocator(e native.SignedEventLocator, authEpoch idx.Epoch, authErr error, checkPayload func() bool) error { 151 pubkeys := v.reader.GetEpochPubKeysOf(authEpoch) 152 if len(pubkeys) == 0 { 153 return authErr 154 } 155 pubkey, ok := pubkeys[e.Locator.Creator] 156 if !ok { 157 return epochcheck.ErrAuth 158 } 159 if checkPayload != nil && !checkPayload() { 160 return ErrWrongPayloadHash 161 } 162 if !verifySignature(e.Locator.HashToSign(), e.Sig, pubkey) { 163 return ErrWrongEventSig 164 } 165 return nil 166 } 167 168 func (v *Checker) matchPubkey(creator idx.ValidatorID, epoch idx.Epoch, want []byte, authErr error) error { 169 pubkeys := v.reader.GetEpochPubKeysOf(epoch) 170 if len(pubkeys) == 0 { 171 return authErr 172 } 173 pubkey, ok := pubkeys[creator] 174 if !ok { 175 return epochcheck.ErrAuth 176 } 177 if bytes.Compare(pubkey.Bytes(), want) != 0 { 178 return ErrPubkeyChanged 179 } 180 return nil 181 } 182 183 func (v *Checker) validateBVsEpoch(bvs native.LlrBlockVotes) error { 184 actualEpochStart := v.reader.GetEpochBlockStart(bvs.Epoch) 185 if actualEpochStart == 0 { 186 return ErrUnknownEpochBVs 187 } 188 if bvs.Start < actualEpochStart || bvs.LastBlock() >= actualEpochStart+MaxBlocksPerEpoch { 189 return ErrImpossibleBVsEpoch 190 } 191 return nil 192 } 193 194 func (v *Checker) ValidateBVs(bvs native.LlrSignedBlockVotes) error { 195 if err := v.validateBVsEpoch(bvs.Val); err != nil { 196 return err 197 } 198 return v.ValidateEventLocator(bvs.Signed, bvs.Val.Epoch, ErrUnknownEpochBVs, func() bool { 199 return bvs.CalcPayloadHash() == bvs.Signed.Locator.PayloadHash 200 }) 201 } 202 203 func (v *Checker) ValidateEV(ev native.LlrSignedEpochVote) error { 204 return v.ValidateEventLocator(ev.Signed, ev.Val.Epoch-1, ErrUnknownEpochEV, func() bool { 205 return ev.CalcPayloadHash() == ev.Signed.Locator.PayloadHash 206 }) 207 } 208 209 // ValidateEvent runs heavy checks for event 210 func (v *Checker) ValidateEvent(e native.EventPayloadI) error { 211 pubkeys, epoch := v.reader.GetEpochPubKeys() 212 if e.Epoch() != epoch { 213 return epochcheck.ErrNotRelevant 214 } 215 // validatorID 216 pubkey, ok := pubkeys[e.Creator()] 217 if !ok { 218 return epochcheck.ErrAuth 219 } 220 // event sig 221 if !verifySignature(e.HashToSign(), e.Sig(), pubkey) { 222 return ErrWrongEventSig 223 } 224 // MPs 225 for _, mp := range e.MisbehaviourProofs() { 226 if proof := mp.EventsDoublesign; proof != nil { 227 for _, vote := range proof.Pair { 228 if err := v.ValidateEventLocator(vote, vote.Locator.Epoch, ErrUnknownEpochEventLocator, nil); err != nil { 229 return err 230 } 231 } 232 } 233 if proof := mp.BlockVoteDoublesign; proof != nil { 234 for _, vote := range proof.Pair { 235 if err := v.ValidateBVs(vote); err != nil { 236 return err 237 } 238 } 239 } 240 if proof := mp.WrongBlockVote; proof != nil { 241 for _, pal := range proof.Pals { 242 if err := v.ValidateBVs(pal); err != nil { 243 return err 244 } 245 } 246 } 247 if proof := mp.EpochVoteDoublesign; proof != nil { 248 for _, vote := range proof.Pair { 249 if err := v.ValidateEV(vote); err != nil { 250 return err 251 } 252 } 253 } 254 if proof := mp.WrongEpochVote; proof != nil { 255 for _, pal := range proof.Pals { 256 if err := v.ValidateEV(pal); err != nil { 257 return err 258 } 259 } 260 } 261 } 262 // pre-cache tx sig 263 for _, tx := range e.Txs() { 264 _, err := types.Sender(v.txSigner, tx) 265 if err != nil { 266 return ErrMalformedTxSig 267 } 268 } 269 // Payload hash 270 if e.PayloadHash() != native.CalcPayloadHash(e) { 271 return ErrWrongPayloadHash 272 } 273 // Epochs of BVs and EV 274 if e.EpochVote().Epoch != 0 { 275 // ensure that validator's pubkey is the same in both current and vote epochs 276 if err := v.matchPubkey(e.Creator(), e.EpochVote().Epoch-1, pubkey.Bytes(), ErrUnknownEpochEV); err != nil { 277 return err 278 } 279 } 280 if e.BlockVotes().Epoch != 0 { 281 // ensure that validator's BVs epoch passes the check 282 if err := v.validateBVsEpoch(e.BlockVotes()); err != nil { 283 return err 284 } 285 // ensure that validator's pubkey is the same in both current and vote epochs 286 if e.BlockVotes().Epoch != e.Epoch() { 287 if err := v.matchPubkey(e.Creator(), e.BlockVotes().Epoch, pubkey.Bytes(), ErrUnknownEpochBVs); err != nil { 288 return err 289 } 290 } 291 } 292 293 return nil 294 } 295 296 func (v *Checker) loop() { 297 defer v.wg.Done() 298 for { 299 select { 300 case op := <-v.tasksQ: 301 if op.event != nil { 302 op.onValidated(v.ValidateEvent(op.event)) 303 } else if op.bvs != nil { 304 op.onValidated(v.ValidateBVs(*op.bvs)) 305 } else { 306 op.onValidated(v.ValidateEV(*op.ev)) 307 } 308 309 case <-v.quit: 310 return 311 } 312 } 313 }