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