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