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