github.com/number571/tendermint@v0.34.11-gost/light/client.go (about) 1 package light 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "sort" 9 "sync" 10 "time" 11 12 tmsync "github.com/number571/tendermint/internal/libs/sync" 13 "github.com/number571/tendermint/libs/log" 14 tmmath "github.com/number571/tendermint/libs/math" 15 "github.com/number571/tendermint/light/provider" 16 "github.com/number571/tendermint/light/store" 17 "github.com/number571/tendermint/types" 18 ) 19 20 type mode byte 21 22 const ( 23 sequential mode = iota + 1 24 skipping 25 26 defaultPruningSize = 1000 27 28 // For verifySkipping, we need an algorithm to find what height to check 29 // next to see if it has sufficient validator set overlap. The most 30 // intuitive method is to take the halfway point i.e. if you trusted block 31 // 1 and were not able to verify block 128 then your next try would be 64. 32 // 33 // However, because this implementation caches all the prior results, instead of always taking halfpoints 34 // it is more efficient to re-check cached blocks. Take this simple example. Say 35 // you failed to verify 64 but were able to verify block 32. Following a strict half-way policy, 36 // you would start over again and try verify to block 128. If this failed 37 // then the halfway point between 32 and 128 is 80. But you already have 38 // block 64. Instead of requesting and waiting for another block it is far 39 // better to try again with block 64. This is of course not directly in the 40 // middle. In fact, no matter how the algrorithm plays out, the blocks in 41 // cache are always going to be a little less than the halfway point ( 42 // maximum 1/8 less). To account for this we add a heuristic, bumping the 43 // next height to 9/16 instead of 1/2 44 verifySkippingNumerator = 9 45 verifySkippingDenominator = 16 46 47 // 10s should cover most of the clients. 48 // References: 49 // - http://vancouver-webpages.com/time/web.html 50 // - https://blog.codinghorror.com/keeping-time-on-the-pc/ 51 defaultMaxClockDrift = 10 * time.Second 52 53 // 10s is sufficient for most networks. 54 defaultMaxBlockLag = 10 * time.Second 55 ) 56 57 // Option sets a parameter for the light client. 58 type Option func(*Client) 59 60 // SequentialVerification option configures the light client to sequentially 61 // check the blocks (every block, in ascending height order). Note this is 62 // much slower than SkippingVerification, albeit more secure. 63 func SequentialVerification() Option { 64 return func(c *Client) { 65 c.verificationMode = sequential 66 } 67 } 68 69 // SkippingVerification option configures the light client to skip blocks as 70 // long as {trustLevel} of the old validator set signed the new header. The 71 // verifySkipping algorithm from the specification is used for finding the minimal 72 // "trust path". 73 // 74 // trustLevel - fraction of the old validator set (in terms of voting power), 75 // which must sign the new header in order for us to trust it. NOTE this only 76 // applies to non-adjacent headers. For adjacent headers, sequential 77 // verification is used. 78 func SkippingVerification(trustLevel tmmath.Fraction) Option { 79 return func(c *Client) { 80 c.verificationMode = skipping 81 c.trustLevel = trustLevel 82 } 83 } 84 85 // PruningSize option sets the maximum amount of light blocks that the light 86 // client stores. When Prune() is run, all light blocks that are earlier than 87 // the h amount of light blocks will be removed from the store. 88 // Default: 1000. A pruning size of 0 will not prune the light client at all. 89 func PruningSize(h uint16) Option { 90 return func(c *Client) { 91 c.pruningSize = h 92 } 93 } 94 95 // Logger option can be used to set a logger for the client. 96 func Logger(l log.Logger) Option { 97 return func(c *Client) { 98 c.logger = l 99 } 100 } 101 102 // MaxClockDrift defines how much new header's time can drift into 103 // the future relative to the light clients local time. Default: 10s. 104 func MaxClockDrift(d time.Duration) Option { 105 return func(c *Client) { 106 c.maxClockDrift = d 107 } 108 } 109 110 // MaxBlockLag represents the maximum time difference between the realtime 111 // that a block is received and the timestamp of that block. 112 // One can approximate it to the maximum block production time 113 // 114 // As an example, say the light client received block B at a time 115 // 12:05 (this is the real time) and the time on the block 116 // was 12:00. Then the lag here is 5 minutes. 117 // Default: 10s 118 func MaxBlockLag(d time.Duration) Option { 119 return func(c *Client) { 120 c.maxBlockLag = d 121 } 122 } 123 124 // Client represents a light client, connected to a single chain, which gets 125 // light blocks from a primary provider, verifies them either sequentially or by 126 // skipping some and stores them in a trusted store (usually, a local FS). 127 // 128 // Default verification: SkippingVerification(DefaultTrustLevel) 129 type Client struct { 130 chainID string 131 trustingPeriod time.Duration // see TrustOptions.Period 132 verificationMode mode 133 trustLevel tmmath.Fraction 134 maxClockDrift time.Duration 135 maxBlockLag time.Duration 136 137 // Mutex for locking during changes of the light clients providers 138 providerMutex tmsync.Mutex 139 // Primary provider of new headers. 140 primary provider.Provider 141 // Providers used to "witness" new headers. 142 witnesses []provider.Provider 143 144 // Where trusted light blocks are stored. 145 trustedStore store.Store 146 // Highest trusted light block from the store (height=H). 147 latestTrustedBlock *types.LightBlock 148 149 // See PruningSize option 150 pruningSize uint16 151 152 logger log.Logger 153 } 154 155 // NewClient returns a new light client. It returns an error if it fails to 156 // obtain the light block from the primary or they are invalid (e.g. trust 157 // hash does not match with the one from the headers). 158 // 159 // Witnesses are providers, which will be used for cross-checking the primary 160 // provider. At least one witness must be given when skipping verification is 161 // used (default). A witness can become a primary iff the current primary is 162 // unavailable. 163 // 164 // See all Option(s) for the additional configuration. 165 func NewClient( 166 ctx context.Context, 167 chainID string, 168 trustOptions TrustOptions, 169 primary provider.Provider, 170 witnesses []provider.Provider, 171 trustedStore store.Store, 172 options ...Option) (*Client, error) { 173 174 // Check whether the trusted store already has a trusted block. If so, then create 175 // a new client from the trusted store instead of the trust options. 176 lastHeight, err := trustedStore.LastLightBlockHeight() 177 if err != nil { 178 return nil, err 179 } 180 if lastHeight > 0 { 181 return NewClientFromTrustedStore( 182 chainID, trustOptions.Period, primary, witnesses, trustedStore, options..., 183 ) 184 } 185 186 // Validate trust options 187 if err := trustOptions.ValidateBasic(); err != nil { 188 return nil, fmt.Errorf("invalid TrustOptions: %w", err) 189 } 190 191 // Validate the number of witnesses. 192 if len(witnesses) < 1 { 193 return nil, ErrNoWitnesses 194 } 195 196 c := &Client{ 197 chainID: chainID, 198 trustingPeriod: trustOptions.Period, 199 verificationMode: skipping, 200 trustLevel: DefaultTrustLevel, 201 maxClockDrift: defaultMaxClockDrift, 202 maxBlockLag: defaultMaxBlockLag, 203 primary: primary, 204 witnesses: witnesses, 205 trustedStore: trustedStore, 206 pruningSize: defaultPruningSize, 207 logger: log.NewNopLogger(), 208 } 209 210 for _, o := range options { 211 o(c) 212 } 213 214 // Validate trust level. 215 if err := ValidateTrustLevel(c.trustLevel); err != nil { 216 return nil, err 217 } 218 219 // Use the trusted hash and height to fetch the first weakly-trusted block 220 // from the primary provider. Assert that all the witnesses have the same block 221 if err := c.initializeWithTrustOptions(ctx, trustOptions); err != nil { 222 return nil, err 223 } 224 225 return c, nil 226 } 227 228 // NewClientFromTrustedStore initializes an existing client from the trusted store. 229 // It does not check that the providers have the same trusted block. 230 func NewClientFromTrustedStore( 231 chainID string, 232 trustingPeriod time.Duration, 233 primary provider.Provider, 234 witnesses []provider.Provider, 235 trustedStore store.Store, 236 options ...Option) (*Client, error) { 237 238 c := &Client{ 239 chainID: chainID, 240 trustingPeriod: trustingPeriod, 241 verificationMode: skipping, 242 trustLevel: DefaultTrustLevel, 243 maxClockDrift: defaultMaxClockDrift, 244 maxBlockLag: defaultMaxBlockLag, 245 primary: primary, 246 witnesses: witnesses, 247 trustedStore: trustedStore, 248 pruningSize: defaultPruningSize, 249 logger: log.NewNopLogger(), 250 } 251 252 for _, o := range options { 253 o(c) 254 } 255 256 // Validate the number of witnesses. 257 if len(c.witnesses) < 1 { 258 return nil, ErrNoWitnesses 259 } 260 261 // Validate trust level. 262 if err := ValidateTrustLevel(c.trustLevel); err != nil { 263 return nil, err 264 } 265 266 // Check that the trusted store has at least one block and 267 if err := c.restoreTrustedLightBlock(); err != nil { 268 return nil, err 269 } 270 271 return c, nil 272 } 273 274 // restoreTrustedLightBlock loads the latest trusted light block from the store 275 func (c *Client) restoreTrustedLightBlock() error { 276 lastHeight, err := c.trustedStore.LastLightBlockHeight() 277 if err != nil { 278 return fmt.Errorf("can't get last trusted light block height: %w", err) 279 } 280 if lastHeight <= 0 { 281 return errors.New("trusted store is empty") 282 } 283 284 trustedBlock, err := c.trustedStore.LightBlock(lastHeight) 285 if err != nil { 286 return fmt.Errorf("can't get last trusted light block: %w", err) 287 } 288 c.latestTrustedBlock = trustedBlock 289 c.logger.Info("restored trusted light block", "height", lastHeight) 290 291 return nil 292 } 293 294 // initializeWithTrustOptions fetches the weakly-trusted light block from 295 // primary provider, matches it to the trusted hash, and sets it as the 296 // lastTrustedBlock. It then asserts that all witnesses have the same light block. 297 func (c *Client) initializeWithTrustOptions(ctx context.Context, options TrustOptions) error { 298 // 1) Fetch and verify the light block. Note that we do not verify the time of the first block 299 l, err := c.lightBlockFromPrimary(ctx, options.Height) 300 if err != nil { 301 return err 302 } 303 304 // 2) Assert that the hashes match 305 if !bytes.Equal(l.Header.Hash(), options.Hash) { 306 return fmt.Errorf("expected header's hash %X, but got %X", options.Hash, l.Hash()) 307 } 308 309 // 3) Ensure that +2/3 of validators signed correctly. This also sanity checks that the 310 // chain ID is the same. 311 err = l.ValidatorSet.VerifyCommitLight(c.chainID, l.Commit.BlockID, l.Height, l.Commit) 312 if err != nil { 313 return fmt.Errorf("invalid commit: %w", err) 314 } 315 316 // 4) Cross-verify with witnesses to ensure everybody has the same state. 317 if err := c.compareFirstHeaderWithWitnesses(ctx, l.SignedHeader); err != nil { 318 return err 319 } 320 321 // 5) Persist both of them and continue. 322 return c.updateTrustedLightBlock(l) 323 } 324 325 // TrustedLightBlock returns a trusted light block at the given height (0 - the latest). 326 // 327 // It returns an error if: 328 // - there are some issues with the trusted store, although that should not 329 // happen normally; 330 // - negative height is passed; 331 // - header has not been verified yet and is therefore not in the store 332 // 333 // Safe for concurrent use by multiple goroutines. 334 func (c *Client) TrustedLightBlock(height int64) (*types.LightBlock, error) { 335 height, err := c.compareWithLatestHeight(height) 336 if err != nil { 337 return nil, err 338 } 339 return c.trustedStore.LightBlock(height) 340 } 341 342 func (c *Client) compareWithLatestHeight(height int64) (int64, error) { 343 latestHeight, err := c.LastTrustedHeight() 344 if err != nil { 345 return 0, fmt.Errorf("can't get last trusted height: %w", err) 346 } 347 if latestHeight == -1 { 348 return 0, errors.New("no headers exist") 349 } 350 351 switch { 352 case height > latestHeight: 353 return 0, fmt.Errorf("unverified header/valset requested (latest: %d)", latestHeight) 354 case height == 0: 355 return latestHeight, nil 356 case height < 0: 357 return 0, errors.New("negative height") 358 } 359 360 return height, nil 361 } 362 363 // Update attempts to advance the state by downloading the latest light 364 // block and verifying it. It returns a new light block on a successful 365 // update. Otherwise, it returns nil (plus an error, if any). 366 func (c *Client) Update(ctx context.Context, now time.Time) (*types.LightBlock, error) { 367 lastTrustedHeight, err := c.LastTrustedHeight() 368 if err != nil { 369 return nil, fmt.Errorf("can't get last trusted height: %w", err) 370 } 371 372 if lastTrustedHeight == -1 { 373 // no light blocks yet => wait 374 return nil, nil 375 } 376 377 latestBlock, err := c.lightBlockFromPrimary(ctx, 0) 378 if err != nil { 379 return nil, err 380 } 381 382 if latestBlock.Height > lastTrustedHeight { 383 err = c.verifyLightBlock(ctx, latestBlock, now) 384 if err != nil { 385 return nil, err 386 } 387 c.logger.Info("advanced to new state", "height", latestBlock.Height, "hash", latestBlock.Hash()) 388 return latestBlock, nil 389 } 390 391 return nil, nil 392 } 393 394 // VerifyLightBlockAtHeight fetches the light block at the given height 395 // and verifies it. It returns the block immediately if it exists in 396 // the trustedStore (no verification is needed). 397 // 398 // height must be > 0. 399 // 400 // It returns provider.ErrlightBlockNotFound if light block is not found by 401 // primary. 402 // 403 // It will replace the primary provider if an error from a request to the provider occurs 404 func (c *Client) VerifyLightBlockAtHeight(ctx context.Context, height int64, now time.Time) (*types.LightBlock, error) { 405 if height <= 0 { 406 return nil, errors.New("negative or zero height") 407 } 408 409 // Check if the light block is already verified. 410 h, err := c.TrustedLightBlock(height) 411 if err == nil { 412 c.logger.Debug("header has already been verified", "height", height, "hash", h.Hash()) 413 // Return already trusted light block 414 return h, nil 415 } 416 417 // Request the light block from primary 418 l, err := c.lightBlockFromPrimary(ctx, height) 419 if err != nil { 420 return nil, err 421 } 422 423 return l, c.verifyLightBlock(ctx, l, now) 424 } 425 426 // VerifyHeader verifies a new header against the trusted state. It returns 427 // immediately if newHeader exists in trustedStore (no verification is 428 // needed). Else it performs one of the two types of verification: 429 // 430 // SequentialVerification: verifies that 2/3 of the trusted validator set has 431 // signed the new header. If the headers are not adjacent, **all** intermediate 432 // headers will be requested. Intermediate headers are not saved to database. 433 // 434 // SkippingVerification(trustLevel): verifies that {trustLevel} of the trusted 435 // validator set has signed the new header. If it's not the case and the 436 // headers are not adjacent, verifySkipping is performed and necessary (not all) 437 // intermediate headers will be requested. See the specification for details. 438 // Intermediate headers are not saved to database. 439 // https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md 440 // 441 // If the header, which is older than the currently trusted header, is 442 // requested and the light client does not have it, VerifyHeader will perform: 443 // a) verifySkipping verification if nearest trusted header is found & not expired 444 // b) backwards verification in all other cases 445 // 446 // It returns ErrOldHeaderExpired if the latest trusted header expired. 447 // 448 // If the primary provides an invalid header (ErrInvalidHeader), it is rejected 449 // and replaced by another provider until all are exhausted. 450 // 451 // If, at any moment, a LightBlock is not found by the primary provider as part of 452 // verification then the provider will be replaced by another and the process will 453 // restart. 454 func (c *Client) VerifyHeader(ctx context.Context, newHeader *types.Header, now time.Time) error { 455 if newHeader == nil { 456 return errors.New("nil header") 457 } 458 if newHeader.Height <= 0 { 459 return errors.New("negative or zero height") 460 } 461 462 // Check if newHeader already verified. 463 l, err := c.TrustedLightBlock(newHeader.Height) 464 if err == nil { 465 // Make sure it's the same header. 466 if !bytes.Equal(l.Hash(), newHeader.Hash()) { 467 return fmt.Errorf("existing trusted header %X does not match newHeader %X", l.Hash(), newHeader.Hash()) 468 } 469 c.logger.Debug("header has already been verified", 470 "height", newHeader.Height, "hash", newHeader.Hash()) 471 return nil 472 } 473 474 // Request the header and the vals. 475 l, err = c.lightBlockFromPrimary(ctx, newHeader.Height) 476 if err != nil { 477 return fmt.Errorf("failed to retrieve light block from primary to verify against: %w", err) 478 } 479 480 if !bytes.Equal(l.Hash(), newHeader.Hash()) { 481 return fmt.Errorf("header from primary %X does not match newHeader %X", l.Hash(), newHeader.Hash()) 482 } 483 484 return c.verifyLightBlock(ctx, l, now) 485 } 486 487 func (c *Client) verifyLightBlock(ctx context.Context, newLightBlock *types.LightBlock, now time.Time) error { 488 c.logger.Info("verify light block", "height", newLightBlock.Height, "hash", newLightBlock.Hash()) 489 490 var ( 491 verifyFunc func(ctx context.Context, trusted *types.LightBlock, new *types.LightBlock, now time.Time) error 492 err error 493 ) 494 495 switch c.verificationMode { 496 case sequential: 497 verifyFunc = c.verifySequential 498 case skipping: 499 verifyFunc = c.verifySkippingAgainstPrimary 500 default: 501 panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode)) 502 } 503 504 firstBlockHeight, err := c.FirstTrustedHeight() 505 if err != nil { 506 return fmt.Errorf("can't get first light block height: %w", err) 507 } 508 509 switch { 510 // Verifying forwards 511 case newLightBlock.Height >= c.latestTrustedBlock.Height: 512 err = verifyFunc(ctx, c.latestTrustedBlock, newLightBlock, now) 513 514 // Verifying backwards 515 case newLightBlock.Height < firstBlockHeight: 516 var firstBlock *types.LightBlock 517 firstBlock, err = c.trustedStore.LightBlock(firstBlockHeight) 518 if err != nil { 519 return fmt.Errorf("can't get first light block: %w", err) 520 } 521 err = c.backwards(ctx, firstBlock.Header, newLightBlock.Header) 522 523 // Verifying between first and last trusted light block. In this situation 524 // we find the closest block prior to the target height then perform 525 // verification forwards. 526 default: 527 var closestBlock *types.LightBlock 528 closestBlock, err = c.trustedStore.LightBlockBefore(newLightBlock.Height) 529 if err != nil { 530 return fmt.Errorf("can't get signed header before height %d: %w", newLightBlock.Height, err) 531 } 532 err = verifyFunc(ctx, closestBlock, newLightBlock, now) 533 } 534 if err != nil { 535 c.logger.Error("failed to verify", "err", err) 536 return err 537 } 538 539 // Once verified, save and return 540 return c.updateTrustedLightBlock(newLightBlock) 541 } 542 543 // see VerifyHeader 544 func (c *Client) verifySequential( 545 ctx context.Context, 546 trustedBlock *types.LightBlock, 547 newLightBlock *types.LightBlock, 548 now time.Time) error { 549 550 var ( 551 verifiedBlock = trustedBlock 552 interimBlock *types.LightBlock 553 err error 554 trace = []*types.LightBlock{trustedBlock} 555 ) 556 557 for height := trustedBlock.Height + 1; height <= newLightBlock.Height; height++ { 558 // 1) Fetch interim light block if needed. 559 if height == newLightBlock.Height { // last light block 560 interimBlock = newLightBlock 561 } else { // intermediate light blocks 562 interimBlock, err = c.lightBlockFromPrimary(ctx, height) 563 if err != nil { 564 return ErrVerificationFailed{From: verifiedBlock.Height, To: height, Reason: err} 565 } 566 } 567 568 // 2) Verify them 569 c.logger.Debug("verify adjacent newLightBlock against verifiedBlock", 570 "trustedHeight", verifiedBlock.Height, 571 "trustedHash", verifiedBlock.Hash(), 572 "newHeight", interimBlock.Height, 573 "newHash", interimBlock.Hash()) 574 575 err = VerifyAdjacent(verifiedBlock.SignedHeader, interimBlock.SignedHeader, interimBlock.ValidatorSet, 576 c.trustingPeriod, now, c.maxClockDrift) 577 if err != nil { 578 err := ErrVerificationFailed{From: verifiedBlock.Height, To: interimBlock.Height, Reason: err} 579 580 switch errors.Unwrap(err).(type) { 581 case ErrInvalidHeader: 582 // If the target header is invalid, return immediately. 583 if err.To == newLightBlock.Height { 584 c.logger.Debug("target header is invalid", "err", err) 585 return err 586 } 587 588 // If some intermediate header is invalid, remove the primary and try again. 589 c.logger.Info("primary sent invalid header -> removing", "err", err, "primary", c.primary) 590 591 replacementBlock, removeErr := c.findNewPrimary(ctx, newLightBlock.Height, true) 592 if removeErr != nil { 593 c.logger.Debug("failed to replace primary. Returning original error", "err", removeErr) 594 return err 595 } 596 597 if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) { 598 c.logger.Debug("replaced primary but new primary has a different block to the initial one") 599 return err 600 } 601 602 // attempt to verify header again 603 height-- 604 605 continue 606 default: 607 return err 608 } 609 } 610 611 // 3) Update verifiedBlock 612 verifiedBlock = interimBlock 613 614 // 4) Add verifiedBlock to trace 615 trace = append(trace, verifiedBlock) 616 } 617 618 // Compare header with the witnesses to ensure it's not a fork. 619 // More witnesses we have, more chance to notice one. 620 // 621 // CORRECTNESS ASSUMPTION: there's at least 1 correct full node 622 // (primary or one of the witnesses). 623 return c.detectDivergence(ctx, trace, now) 624 } 625 626 // see VerifyHeader 627 // 628 // verifySkipping finds the middle light block between a trusted and new light block, 629 // reiterating the action until it verifies a light block. A cache of light blocks 630 // requested from source is kept such that when a verification is made, and the 631 // light client tries again to verify the new light block in the middle, the light 632 // client does not need to ask for all the same light blocks again. 633 // 634 // If this function errors, it should always wrap it in a `ErrVerifcationFailed` 635 // struct so that the calling function can determine where it failed and handle 636 // it accordingly. 637 func (c *Client) verifySkipping( 638 ctx context.Context, 639 source provider.Provider, 640 trustedBlock *types.LightBlock, 641 newLightBlock *types.LightBlock, 642 now time.Time) ([]*types.LightBlock, error) { 643 644 var ( 645 // The block cache is ordered in height from highest to lowest. We start 646 // with the newLightBlock and for any height requested in between we add 647 // it. 648 blockCache = []*types.LightBlock{newLightBlock} 649 depth = 0 650 651 verifiedBlock = trustedBlock 652 trace = []*types.LightBlock{trustedBlock} 653 ) 654 655 for { 656 c.logger.Debug("verify non-adjacent newHeader against verifiedBlock", 657 "trustedHeight", verifiedBlock.Height, 658 "trustedHash", verifiedBlock.Hash(), 659 "newHeight", blockCache[depth].Height, 660 "newHash", blockCache[depth].Hash()) 661 662 // Verify the untrusted header. This function is equivalent to 663 // ValidAndVerified in the spec 664 err := Verify(verifiedBlock.SignedHeader, verifiedBlock.ValidatorSet, blockCache[depth].SignedHeader, 665 blockCache[depth].ValidatorSet, c.trustingPeriod, now, c.maxClockDrift, c.trustLevel) 666 switch err.(type) { 667 case nil: 668 // If we have verified the last header then depth will be 0 and we 669 // can return a success along with the trace of intermediate headers 670 if depth == 0 { 671 trace = append(trace, newLightBlock) 672 return trace, nil 673 } 674 // If not, update the lower bound to the previous upper bound 675 verifiedBlock = blockCache[depth] 676 // Remove the light block at the lower bound in the header cache - it will no longer be needed 677 blockCache = blockCache[:depth] 678 // Reset the cache depth so that we start from the upper bound again 679 depth = 0 680 // add verifiedBlock to the trace 681 trace = append(trace, verifiedBlock) 682 683 case ErrNewValSetCantBeTrusted: 684 // the light block current passed validation, but the validator 685 // set is too different to verify it. We keep the block because it 686 // may become valuable later on. 687 // 688 // If we have reached the end of the cache we need to request a 689 // completely new block else we recycle a previously requested one. 690 // In both cases we are taking a block with a closer height to the 691 // previously verified one in the hope that it has a better chance 692 // of having a similar validator set 693 if depth == len(blockCache)-1 { 694 // schedule what the next height we need to fetch is 695 pivotHeight := c.schedule(verifiedBlock.Height, blockCache[depth].Height) 696 interimBlock, providerErr := source.LightBlock(ctx, pivotHeight) 697 if providerErr != nil { 698 return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: pivotHeight, Reason: providerErr} 699 } 700 blockCache = append(blockCache, interimBlock) 701 } 702 depth++ 703 704 // for any verification error we abort the operation and return the error 705 default: 706 return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: blockCache[depth].Height, Reason: err} 707 } 708 } 709 } 710 711 // schedule works out the next height to attempt sequential verification 712 func (c *Client) schedule(lastVerifiedHeight, lastFailedHeight int64) int64 { 713 return lastVerifiedHeight + 714 (lastFailedHeight-lastVerifiedHeight)*verifySkippingNumerator/verifySkippingDenominator 715 } 716 717 // verifySkippingAgainstPrimary does verifySkipping plus it compares new header with 718 // witnesses and replaces primary if it sends the light client an invalid header 719 func (c *Client) verifySkippingAgainstPrimary( 720 ctx context.Context, 721 trustedBlock *types.LightBlock, 722 newLightBlock *types.LightBlock, 723 now time.Time) error { 724 725 trace, err := c.verifySkipping(ctx, c.primary, trustedBlock, newLightBlock, now) 726 if err == nil { 727 // Success! Now compare the header with the witnesses to ensure it's not a fork. 728 // More witnesses we have, more chance to notice one. 729 // 730 // CORRECTNESS ASSUMPTION: there's at least 1 correct full node 731 // (primary or one of the witnesses). 732 if cmpErr := c.detectDivergence(ctx, trace, now); cmpErr != nil { 733 return cmpErr 734 } 735 } 736 737 var e = &ErrVerificationFailed{} 738 // all errors from verify skipping should be `ErrVerificationFailed` 739 // if it's not we just return the error directly 740 if !errors.As(err, e) { 741 return err 742 } 743 744 replace := true 745 switch e.Reason.(type) { 746 // Verification returned an invalid header 747 case ErrInvalidHeader: 748 // If it was the target header, return immediately. 749 if e.To == newLightBlock.Height { 750 c.logger.Debug("target header is invalid", "err", err) 751 return err 752 } 753 754 // If some intermediate header is invalid, remove the primary and try 755 // again. 756 757 // An intermediate header expired. We can no longer validate it as there is 758 // no longer the ability to punish invalid blocks as evidence of misbehavior 759 case ErrOldHeaderExpired: 760 return err 761 762 // This happens if there was a problem in finding the next block or a 763 // context was canceled. 764 default: 765 if errors.Is(e.Reason, context.Canceled) || errors.Is(e.Reason, context.DeadlineExceeded) { 766 return e.Reason 767 } 768 769 if !c.providerShouldBeRemoved(e.Reason) { 770 replace = false 771 } 772 } 773 774 // if we've reached here we're attempting to retry verification with a 775 // different provider 776 c.logger.Info("primary returned error", "err", e, "primary", c.primary, "replace", replace) 777 778 replacementBlock, removeErr := c.findNewPrimary(ctx, newLightBlock.Height, replace) 779 if removeErr != nil { 780 c.logger.Error("failed to replace primary. Returning original error", "err", removeErr) 781 return e.Reason 782 } 783 784 if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) { 785 c.logger.Debug("replaced primary but new primary has a different block to the initial one. Returning original error") 786 return e.Reason 787 } 788 789 // attempt to verify the header again from the trusted block 790 return c.verifySkippingAgainstPrimary(ctx, trustedBlock, replacementBlock, now) 791 } 792 793 // LastTrustedHeight returns a last trusted height. -1 and nil are returned if 794 // there are no trusted headers. 795 // 796 // Safe for concurrent use by multiple goroutines. 797 func (c *Client) LastTrustedHeight() (int64, error) { 798 return c.trustedStore.LastLightBlockHeight() 799 } 800 801 // FirstTrustedHeight returns a first trusted height. -1 and nil are returned if 802 // there are no trusted headers. 803 // 804 // Safe for concurrent use by multiple goroutines. 805 func (c *Client) FirstTrustedHeight() (int64, error) { 806 return c.trustedStore.FirstLightBlockHeight() 807 } 808 809 // ChainID returns the chain ID the light client was configured with. 810 // 811 // Safe for concurrent use by multiple goroutines. 812 func (c *Client) ChainID() string { 813 return c.chainID 814 } 815 816 // Primary returns the primary provider. 817 // 818 // NOTE: provider may be not safe for concurrent access. 819 func (c *Client) Primary() provider.Provider { 820 c.providerMutex.Lock() 821 defer c.providerMutex.Unlock() 822 return c.primary 823 } 824 825 // Witnesses returns the witness providers. 826 // 827 // NOTE: providers may be not safe for concurrent access. 828 func (c *Client) Witnesses() []provider.Provider { 829 c.providerMutex.Lock() 830 defer c.providerMutex.Unlock() 831 return c.witnesses 832 } 833 834 // AddProvider adds a providers to the light clients set 835 // 836 // NOTE: The light client does not check for uniqueness 837 func (c *Client) AddProvider(p provider.Provider) { 838 c.providerMutex.Lock() 839 defer c.providerMutex.Unlock() 840 c.witnesses = append(c.witnesses, p) 841 } 842 843 // Cleanup removes all the data (headers and validator sets) stored. Note: the 844 // client must be stopped at this point. 845 func (c *Client) Cleanup() error { 846 c.logger.Info("removing all light blocks") 847 c.latestTrustedBlock = nil 848 return c.trustedStore.Prune(0) 849 } 850 851 func (c *Client) updateTrustedLightBlock(l *types.LightBlock) error { 852 c.logger.Debug("updating trusted light block", "light_block", l) 853 854 if err := c.trustedStore.SaveLightBlock(l); err != nil { 855 return fmt.Errorf("failed to save trusted header: %w", err) 856 } 857 858 if c.pruningSize > 0 { 859 if err := c.trustedStore.Prune(c.pruningSize); err != nil { 860 return fmt.Errorf("prune: %w", err) 861 } 862 } 863 864 if c.latestTrustedBlock == nil || l.Height > c.latestTrustedBlock.Height { 865 c.latestTrustedBlock = l 866 } 867 868 return nil 869 } 870 871 // backwards verification (see VerifyHeaderBackwards func in the spec) verifies 872 // headers before a trusted header. If a sent header is invalid the primary is 873 // replaced with another provider and the operation is repeated. 874 func (c *Client) backwards( 875 ctx context.Context, 876 trustedHeader *types.Header, 877 newHeader *types.Header) error { 878 879 var ( 880 verifiedHeader = trustedHeader 881 interimHeader *types.Header 882 ) 883 884 for verifiedHeader.Height > newHeader.Height { 885 interimBlock, err := c.lightBlockFromPrimary(ctx, verifiedHeader.Height-1) 886 if err != nil { 887 return fmt.Errorf("failed to obtain the header at height #%d: %w", verifiedHeader.Height-1, err) 888 } 889 interimHeader = interimBlock.Header 890 c.logger.Debug("verify newHeader against verifiedHeader", 891 "trustedHeight", verifiedHeader.Height, 892 "trustedHash", verifiedHeader.Hash(), 893 "newHeight", interimHeader.Height, 894 "newHash", interimHeader.Hash()) 895 if err := VerifyBackwards(interimHeader, verifiedHeader); err != nil { 896 // verification has failed 897 c.logger.Info("backwards verification failed, replacing primary...", "err", err, "primary", c.primary) 898 899 // the client tries to see if it can get a witness to continue with the request 900 newPrimarysBlock, replaceErr := c.findNewPrimary(ctx, newHeader.Height, true) 901 if replaceErr != nil { 902 c.logger.Debug("failed to replace primary. Returning original error", "err", replaceErr) 903 return err 904 } 905 906 // before continuing we must check that they have the same target header to validate 907 if !bytes.Equal(newPrimarysBlock.Hash(), newHeader.Hash()) { 908 c.logger.Debug("replaced primary but new primary has a different block to the initial one") 909 // return the original error 910 return err 911 } 912 913 // try again with the new primary 914 return c.backwards(ctx, verifiedHeader, newPrimarysBlock.Header) 915 } 916 verifiedHeader = interimHeader 917 } 918 919 return nil 920 } 921 922 // lightBlockFromPrimary retrieves the lightBlock from the primary provider 923 // at the specified height. This method also handles provider behavior as follows: 924 // 925 // 1. If the provider does not respond or does not have the block, it tries again 926 // with a different provider 927 // 2. If all providers return the same error, the light client forwards the error to 928 // where the initial request came from 929 // 3. If the provider provides an invalid light block, is deemed unreliable or returns 930 // any other error, the primary is permanently dropped and is replaced by a witness. 931 func (c *Client) lightBlockFromPrimary(ctx context.Context, height int64) (*types.LightBlock, error) { 932 c.providerMutex.Lock() 933 l, err := c.primary.LightBlock(ctx, height) 934 c.providerMutex.Unlock() 935 936 switch err { 937 case nil: 938 // Everything went smoothly. We reset the lightBlockRequests and return the light block 939 return l, nil 940 941 // catch canceled contexts or deadlines 942 case context.Canceled, context.DeadlineExceeded: 943 return nil, err 944 945 case provider.ErrNoResponse, provider.ErrLightBlockNotFound, provider.ErrHeightTooHigh: 946 // we find a new witness to replace the primary 947 c.logger.Info("error from light block request from primary, replacing...", 948 "error", err, "height", height, "primary", c.primary) 949 return c.findNewPrimary(ctx, height, false) 950 951 default: 952 // The light client has most likely received either provider.ErrUnreliableProvider or provider.ErrBadLightBlock 953 // These errors mean that the light client should drop the primary and try with another provider instead 954 c.logger.Info("error from light block request from primary, removing...", 955 "error", err, "height", height, "primary", c.primary) 956 return c.findNewPrimary(ctx, height, true) 957 } 958 } 959 960 // NOTE: requires a providerMutex lock 961 func (c *Client) removeWitnesses(indexes []int) error { 962 // check that we will still have witnesses remaining 963 if len(c.witnesses) <= len(indexes) { 964 return ErrNoWitnesses 965 } 966 967 // we need to make sure that we remove witnesses by index in the reverse 968 // order so as to not affect the indexes themselves 969 sort.Ints(indexes) 970 for i := len(indexes) - 1; i >= 0; i-- { 971 c.witnesses[indexes[i]] = c.witnesses[len(c.witnesses)-1] 972 c.witnesses = c.witnesses[:len(c.witnesses)-1] 973 } 974 975 return nil 976 } 977 978 type witnessResponse struct { 979 lb *types.LightBlock 980 witnessIndex int 981 err error 982 } 983 984 // findNewPrimary concurrently sends a light block request, promoting the first witness to return 985 // a valid light block as the new primary. The remove option indicates whether the primary should be 986 // entire removed or just appended to the back of the witnesses list. This method also handles witness 987 // errors. If no witness is available, it returns the last error of the witness. 988 func (c *Client) findNewPrimary(ctx context.Context, height int64, remove bool) (*types.LightBlock, error) { 989 c.providerMutex.Lock() 990 defer c.providerMutex.Unlock() 991 992 if len(c.witnesses) <= 1 { 993 return nil, ErrNoWitnesses 994 } 995 996 var ( 997 witnessResponsesC = make(chan witnessResponse, len(c.witnesses)) 998 witnessesToRemove []int 999 lastError error 1000 wg sync.WaitGroup 1001 ) 1002 1003 // send out a light block request to all witnesses 1004 subctx, cancel := context.WithCancel(ctx) 1005 defer cancel() 1006 for index := range c.witnesses { 1007 wg.Add(1) 1008 go func(witnessIndex int, witnessResponsesC chan witnessResponse) { 1009 defer wg.Done() 1010 1011 lb, err := c.witnesses[witnessIndex].LightBlock(subctx, height) 1012 witnessResponsesC <- witnessResponse{lb, witnessIndex, err} 1013 }(index, witnessResponsesC) 1014 } 1015 1016 // process all the responses as they come in 1017 for i := 0; i < cap(witnessResponsesC); i++ { 1018 response := <-witnessResponsesC 1019 switch response.err { 1020 // success! We have found a new primary 1021 case nil: 1022 cancel() // cancel all remaining requests to other witnesses 1023 1024 wg.Wait() // wait for all goroutines to finish 1025 1026 // if we are not intending on removing the primary then append the old primary to the end of the witness slice 1027 if !remove { 1028 c.witnesses = append(c.witnesses, c.primary) 1029 } 1030 1031 // promote respondent as the new primary 1032 c.logger.Debug("found new primary", "primary", c.witnesses[response.witnessIndex]) 1033 c.primary = c.witnesses[response.witnessIndex] 1034 1035 // add promoted witness to the list of witnesses to be removed 1036 witnessesToRemove = append(witnessesToRemove, response.witnessIndex) 1037 1038 // remove witnesses marked as bad (the client must do this before we alter the witness slice and change the indexes 1039 // of witnesses). Removal is done in descending order 1040 if err := c.removeWitnesses(witnessesToRemove); err != nil { 1041 return nil, err 1042 } 1043 1044 // return the light block that new primary responded with 1045 return response.lb, nil 1046 1047 // catch canceled contexts or deadlines 1048 case context.Canceled, context.DeadlineExceeded: 1049 return nil, response.err 1050 1051 // process benign errors by logging them only 1052 case provider.ErrNoResponse, provider.ErrLightBlockNotFound, provider.ErrHeightTooHigh: 1053 lastError = response.err 1054 c.logger.Info("error on light block request from witness", 1055 "error", response.err, "primary", c.witnesses[response.witnessIndex]) 1056 continue 1057 1058 // process malevolent errors like ErrUnreliableProvider and ErrBadLightBlock by removing the witness 1059 default: 1060 lastError = response.err 1061 c.logger.Error("error on light block request from witness, removing...", 1062 "error", response.err, "primary", c.witnesses[response.witnessIndex]) 1063 witnessesToRemove = append(witnessesToRemove, response.witnessIndex) 1064 } 1065 } 1066 1067 return nil, lastError 1068 } 1069 1070 // compareFirstHeaderWithWitnesses concurrently compares h with all witnesses. If any 1071 // witness reports a different header than h, the function returns an error. 1072 func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.SignedHeader) error { 1073 compareCtx, cancel := context.WithCancel(ctx) 1074 defer cancel() 1075 1076 c.providerMutex.Lock() 1077 defer c.providerMutex.Unlock() 1078 1079 if len(c.witnesses) < 1 { 1080 return ErrNoWitnesses 1081 } 1082 1083 errc := make(chan error, len(c.witnesses)) 1084 for i, witness := range c.witnesses { 1085 go c.compareNewHeaderWithWitness(compareCtx, errc, h, witness, i) 1086 } 1087 1088 witnessesToRemove := make([]int, 0, len(c.witnesses)) 1089 1090 // handle errors from the header comparisons as they come in 1091 for i := 0; i < cap(errc); i++ { 1092 err := <-errc 1093 1094 switch e := err.(type) { 1095 case nil: 1096 continue 1097 case errConflictingHeaders: 1098 c.logger.Error(`witness has a different header. Please check primary is 1099 correct and remove witness. Otherwise, use a different primary`, 1100 "Witness", c.witnesses[e.WitnessIndex], "ExpHeader", h.Hash(), "GotHeader", e.Block.Hash()) 1101 return err 1102 case errBadWitness: 1103 // If witness sent us an invalid header, then remove it 1104 c.logger.Info("witness returned an error, removing...", 1105 "err", err) 1106 witnessesToRemove = append(witnessesToRemove, e.WitnessIndex) 1107 default: 1108 // check for canceled contexts or deadlines 1109 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { 1110 return err 1111 } 1112 1113 // the witness either didn't respond or didn't have the block. We ignore it. 1114 c.logger.Debug("unable to compare first header with witness, ignoring", 1115 "err", err) 1116 } 1117 1118 } 1119 1120 // remove all witnesses that misbehaved 1121 return c.removeWitnesses(witnessesToRemove) 1122 } 1123 1124 // providerShouldBeRemoved analyzes the nature of the error and whether the provider 1125 // should be removed from the light clients set 1126 func (c *Client) providerShouldBeRemoved(err error) bool { 1127 return errors.As(err, &provider.ErrUnreliableProvider{}) || 1128 errors.As(err, &provider.ErrBadLightBlock{}) || 1129 errors.Is(err, provider.ErrConnectionClosed) 1130 }