github.com/soomindae/tendermint@v0.0.5-0.20210528140126-84a0c70c8162/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/soomindae/tendermint/libs/log" 13 tmmath "github.com/soomindae/tendermint/libs/math" 14 tmsync "github.com/soomindae/tendermint/libs/sync" 15 "github.com/soomindae/tendermint/light/provider" 16 "github.com/soomindae/tendermint/light/store" 17 "github.com/soomindae/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) < 1 { 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/soomindae/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 if providerErr != nil { 752 return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: pivotHeight, Reason: providerErr} 753 } 754 blockCache = append(blockCache, interimBlock) 755 } 756 depth++ 757 758 default: 759 return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: blockCache[depth].Height, Reason: err} 760 } 761 } 762 } 763 764 // verifySkippingAgainstPrimary does verifySkipping plus it compares new header with 765 // witnesses and replaces primary if it sends the light client an invalid header 766 func (c *Client) verifySkippingAgainstPrimary( 767 ctx context.Context, 768 trustedBlock *types.LightBlock, 769 newLightBlock *types.LightBlock, 770 now time.Time) error { 771 772 trace, err := c.verifySkipping(ctx, c.primary, trustedBlock, newLightBlock, now) 773 774 switch errors.Unwrap(err).(type) { 775 case ErrInvalidHeader: 776 // If the target header is invalid, return immediately. 777 invalidHeaderHeight := err.(ErrVerificationFailed).To 778 if invalidHeaderHeight == newLightBlock.Height { 779 c.logger.Debug("Target header is invalid", "err", err) 780 return err 781 } 782 783 // If some intermediate header is invalid, replace the primary and try 784 // again. 785 c.logger.Error("primary sent invalid header -> replacing", "err", err) 786 787 replacementBlock, removeErr := c.findNewPrimary(ctx, newLightBlock.Height, true) 788 if removeErr != nil { 789 c.logger.Error("failed to replace primary. Returning original error", "err", removeErr) 790 return err 791 } 792 793 if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) { 794 c.logger.Error("Replacement provider has a different light block", 795 "newHash", newLightBlock.Hash(), 796 "replHash", replacementBlock.Hash()) 797 // return original error 798 return err 799 } 800 801 // attempt to verify the header again 802 return c.verifySkippingAgainstPrimary(ctx, trustedBlock, replacementBlock, now) 803 case nil: 804 // Compare header with the witnesses to ensure it's not a fork. 805 // More witnesses we have, more chance to notice one. 806 // 807 // CORRECTNESS ASSUMPTION: there's at least 1 correct full node 808 // (primary or one of the witnesses). 809 if cmpErr := c.detectDivergence(ctx, trace, now); cmpErr != nil { 810 return cmpErr 811 } 812 default: 813 return err 814 } 815 816 return nil 817 } 818 819 // LastTrustedHeight returns a last trusted height. -1 and nil are returned if 820 // there are no trusted headers. 821 // 822 // Safe for concurrent use by multiple goroutines. 823 func (c *Client) LastTrustedHeight() (int64, error) { 824 return c.trustedStore.LastLightBlockHeight() 825 } 826 827 // FirstTrustedHeight returns a first trusted height. -1 and nil are returned if 828 // there are no trusted headers. 829 // 830 // Safe for concurrent use by multiple goroutines. 831 func (c *Client) FirstTrustedHeight() (int64, error) { 832 return c.trustedStore.FirstLightBlockHeight() 833 } 834 835 // ChainID returns the chain ID the light client was configured with. 836 // 837 // Safe for concurrent use by multiple goroutines. 838 func (c *Client) ChainID() string { 839 return c.chainID 840 } 841 842 // Primary returns the primary provider. 843 // 844 // NOTE: provider may be not safe for concurrent access. 845 func (c *Client) Primary() provider.Provider { 846 c.providerMutex.Lock() 847 defer c.providerMutex.Unlock() 848 return c.primary 849 } 850 851 // Witnesses returns the witness providers. 852 // 853 // NOTE: providers may be not safe for concurrent access. 854 func (c *Client) Witnesses() []provider.Provider { 855 c.providerMutex.Lock() 856 defer c.providerMutex.Unlock() 857 return c.witnesses 858 } 859 860 // Cleanup removes all the data (headers and validator sets) stored. Note: the 861 // client must be stopped at this point. 862 func (c *Client) Cleanup() error { 863 c.logger.Info("Removing all the data") 864 c.latestTrustedBlock = nil 865 return c.trustedStore.Prune(0) 866 } 867 868 // cleanupAfter deletes all headers & validator sets after +height+. It also 869 // resets latestTrustedBlock to the latest header. 870 func (c *Client) cleanupAfter(height int64) error { 871 prevHeight := c.latestTrustedBlock.Height 872 873 for { 874 h, err := c.trustedStore.LightBlockBefore(prevHeight) 875 if err == store.ErrLightBlockNotFound || (h != nil && h.Height <= height) { 876 break 877 } else if err != nil { 878 return fmt.Errorf("failed to get header before %d: %w", prevHeight, err) 879 } 880 881 err = c.trustedStore.DeleteLightBlock(h.Height) 882 if err != nil { 883 c.logger.Error("can't remove a trusted header & validator set", "err", err, 884 "height", h.Height) 885 } 886 887 prevHeight = h.Height 888 } 889 890 c.latestTrustedBlock = nil 891 err := c.restoreTrustedLightBlock() 892 if err != nil { 893 return err 894 } 895 896 return nil 897 } 898 899 func (c *Client) updateTrustedLightBlock(l *types.LightBlock) error { 900 c.logger.Debug("updating trusted light block", "light_block", l) 901 902 if err := c.trustedStore.SaveLightBlock(l); err != nil { 903 return fmt.Errorf("failed to save trusted header: %w", err) 904 } 905 906 if c.pruningSize > 0 { 907 if err := c.trustedStore.Prune(c.pruningSize); err != nil { 908 return fmt.Errorf("prune: %w", err) 909 } 910 } 911 912 if c.latestTrustedBlock == nil || l.Height > c.latestTrustedBlock.Height { 913 c.latestTrustedBlock = l 914 } 915 916 return nil 917 } 918 919 // backwards verification (see VerifyHeaderBackwards func in the spec) verifies 920 // headers before a trusted header. If a sent header is invalid the primary is 921 // replaced with another provider and the operation is repeated. 922 func (c *Client) backwards( 923 ctx context.Context, 924 trustedHeader *types.Header, 925 newHeader *types.Header) error { 926 927 var ( 928 verifiedHeader = trustedHeader 929 interimHeader *types.Header 930 ) 931 932 for verifiedHeader.Height > newHeader.Height { 933 interimBlock, err := c.lightBlockFromPrimary(ctx, verifiedHeader.Height-1) 934 if err != nil { 935 return fmt.Errorf("failed to obtain the header at height #%d: %w", verifiedHeader.Height-1, err) 936 } 937 interimHeader = interimBlock.Header 938 c.logger.Debug("Verify newHeader against verifiedHeader", 939 "trustedHeight", verifiedHeader.Height, 940 "trustedHash", verifiedHeader.Hash(), 941 "newHeight", interimHeader.Height, 942 "newHash", interimHeader.Hash()) 943 if err := VerifyBackwards(interimHeader, verifiedHeader); err != nil { 944 // verification has failed 945 c.logger.Error("backwards verification failed, replacing primary...", "err", err, "primary", c.primary) 946 947 // the client tries to see if it can get a witness to continue with the request 948 newPrimarysBlock, replaceErr := c.findNewPrimary(ctx, newHeader.Height, true) 949 if replaceErr != nil { 950 c.logger.Debug("failed to replace primary. Returning original error", "err", replaceErr) 951 return err 952 } 953 954 // before continuing we must check that they have the same target header to validate 955 if !bytes.Equal(newPrimarysBlock.Hash(), newHeader.Hash()) { 956 c.logger.Debug("replaced primary but new primary has a different block to the initial one") 957 // return the original error 958 return err 959 } 960 961 // try again with the new primary 962 return c.backwards(ctx, verifiedHeader, newPrimarysBlock.Header) 963 } 964 verifiedHeader = interimHeader 965 } 966 967 return nil 968 } 969 970 // lightBlockFromPrimary retrieves the lightBlock from the primary provider 971 // at the specified height. This method also handles provider behavior as follows: 972 // 973 // 1. If the provider does not respond or does not have the block, it tries again 974 // with a different provider 975 // 2. If all providers return the same error, the light client forwards the error to 976 // where the initial request came from 977 // 3. If the provider provides an invalid light block, is deemed unreliable or returns 978 // any other error, the primary is permanently dropped and is replaced by a witness. 979 func (c *Client) lightBlockFromPrimary(ctx context.Context, height int64) (*types.LightBlock, error) { 980 c.providerMutex.Lock() 981 l, err := c.primary.LightBlock(ctx, height) 982 c.providerMutex.Unlock() 983 984 switch err { 985 case nil: 986 // Everything went smoothly. We reset the lightBlockRequests and return the light block 987 return l, nil 988 989 case provider.ErrNoResponse, provider.ErrLightBlockNotFound: 990 // we find a new witness to replace the primary 991 c.logger.Debug("error from light block request from primary, replacing...", 992 "error", err, "height", height, "primary", c.primary) 993 return c.findNewPrimary(ctx, height, false) 994 995 default: 996 // The light client has most likely received either provider.ErrUnreliableProvider or provider.ErrBadLightBlock 997 // These errors mean that the light client should drop the primary and try with another provider instead 998 c.logger.Error("error from light block request from primary, removing...", 999 "error", err, "height", height, "primary", c.primary) 1000 return c.findNewPrimary(ctx, height, true) 1001 } 1002 } 1003 1004 // NOTE: requires a providerMutex lock 1005 func (c *Client) removeWitnesses(indexes []int) error { 1006 // check that we will still have witnesses remaining 1007 if len(c.witnesses) <= len(indexes) { 1008 return ErrNoWitnesses 1009 } 1010 1011 // we need to make sure that we remove witnesses by index in the reverse 1012 // order so as to not affect the indexes themselves 1013 sort.Ints(indexes) 1014 for i := len(indexes) - 1; i >= 0; i-- { 1015 c.witnesses[indexes[i]] = c.witnesses[len(c.witnesses)-1] 1016 c.witnesses = c.witnesses[:len(c.witnesses)-1] 1017 } 1018 1019 return nil 1020 } 1021 1022 type witnessResponse struct { 1023 lb *types.LightBlock 1024 witnessIndex int 1025 err error 1026 } 1027 1028 // findNewPrimary concurrently sends a light block request, promoting the first witness to return 1029 // a valid light block as the new primary. The remove option indicates whether the primary should be 1030 // entire removed or just appended to the back of the witnesses list. This method also handles witness 1031 // errors. If no witness is available, it returns the last error of the witness. 1032 func (c *Client) findNewPrimary(ctx context.Context, height int64, remove bool) (*types.LightBlock, error) { 1033 c.providerMutex.Lock() 1034 defer c.providerMutex.Unlock() 1035 1036 if len(c.witnesses) <= 1 { 1037 return nil, ErrNoWitnesses 1038 } 1039 1040 var ( 1041 witnessResponsesC = make(chan witnessResponse, len(c.witnesses)) 1042 witnessesToRemove []int 1043 lastError error 1044 wg sync.WaitGroup 1045 ) 1046 1047 // send out a light block request to all witnesses 1048 subctx, cancel := context.WithCancel(ctx) 1049 defer cancel() 1050 for index := range c.witnesses { 1051 wg.Add(1) 1052 go func(witnessIndex int, witnessResponsesC chan witnessResponse) { 1053 defer wg.Done() 1054 1055 lb, err := c.witnesses[witnessIndex].LightBlock(subctx, height) 1056 witnessResponsesC <- witnessResponse{lb, witnessIndex, err} 1057 }(index, witnessResponsesC) 1058 } 1059 1060 // process all the responses as they come in 1061 for i := 0; i < cap(witnessResponsesC); i++ { 1062 response := <-witnessResponsesC 1063 switch response.err { 1064 // success! We have found a new primary 1065 case nil: 1066 cancel() // cancel all remaining requests to other witnesses 1067 1068 wg.Wait() // wait for all goroutines to finish 1069 1070 // if we are not intending on removing the primary then append the old primary to the end of the witness slice 1071 if !remove { 1072 c.witnesses = append(c.witnesses, c.primary) 1073 } 1074 1075 // promote respondent as the new primary 1076 c.logger.Debug("found new primary", "primary", c.witnesses[response.witnessIndex]) 1077 c.primary = c.witnesses[response.witnessIndex] 1078 1079 // add promoted witness to the list of witnesses to be removed 1080 witnessesToRemove = append(witnessesToRemove, response.witnessIndex) 1081 1082 // remove witnesses marked as bad (the client must do this before we alter the witness slice and change the indexes 1083 // of witnesses). Removal is done in descending order 1084 if err := c.removeWitnesses(witnessesToRemove); err != nil { 1085 return nil, err 1086 } 1087 1088 // return the light block that new primary responded with 1089 return response.lb, nil 1090 1091 // process benign errors by logging them only 1092 case provider.ErrNoResponse, provider.ErrLightBlockNotFound: 1093 lastError = response.err 1094 c.logger.Debug("error on light block request from witness", 1095 "error", response.err, "primary", c.witnesses[response.witnessIndex]) 1096 continue 1097 1098 // process malevolent errors like ErrUnreliableProvider and ErrBadLightBlock by removing the witness 1099 default: 1100 lastError = response.err 1101 c.logger.Error("error on light block request from witness, removing...", 1102 "error", response.err, "primary", c.witnesses[response.witnessIndex]) 1103 witnessesToRemove = append(witnessesToRemove, response.witnessIndex) 1104 } 1105 } 1106 1107 return nil, lastError 1108 } 1109 1110 // compareFirstHeaderWithWitnesses compares h with all witnesses. If any 1111 // witness reports a different header than h, the function returns an error. 1112 func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.SignedHeader) error { 1113 compareCtx, cancel := context.WithCancel(ctx) 1114 defer cancel() 1115 1116 c.providerMutex.Lock() 1117 defer c.providerMutex.Unlock() 1118 1119 if len(c.witnesses) < 1 { 1120 return errNoWitnesses{} 1121 } 1122 1123 errc := make(chan error, len(c.witnesses)) 1124 for i, witness := range c.witnesses { 1125 go c.compareNewHeaderWithWitness(compareCtx, errc, h, witness, i) 1126 } 1127 1128 witnessesToRemove := make([]int, 0, len(c.witnesses)) 1129 1130 // handle errors from the header comparisons as they come in 1131 for i := 0; i < cap(errc); i++ { 1132 err := <-errc 1133 1134 switch e := err.(type) { 1135 case nil: 1136 continue 1137 case errConflictingHeaders: 1138 c.logger.Error(fmt.Sprintf(`Witness #%d has a different header. Please check primary is correct 1139 and remove witness. Otherwise, use the different primary`, e.WitnessIndex), "witness", c.witnesses[e.WitnessIndex]) 1140 return err 1141 case errBadWitness: 1142 // If witness sent us an invalid header, then remove it. If it didn't 1143 // respond or couldn't find the block, then we ignore it and move on to 1144 // the next witness. 1145 if _, ok := e.Reason.(provider.ErrBadLightBlock); ok { 1146 c.logger.Info("Witness sent us invalid header / vals -> removing it", 1147 "witness", c.witnesses[e.WitnessIndex], "err", err) 1148 witnessesToRemove = append(witnessesToRemove, e.WitnessIndex) 1149 } 1150 } 1151 1152 } 1153 1154 // remove witnesses that have misbehaved 1155 if err := c.removeWitnesses(witnessesToRemove); err != nil { 1156 return err 1157 } 1158 1159 return nil 1160 }