github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/store/store.go (about) 1 package store 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "strconv" 8 9 "github.com/gogo/protobuf/proto" 10 "github.com/google/orderedcode" 11 dbm "github.com/tendermint/tm-db" 12 13 tmproto "github.com/ari-anchor/sei-tendermint/proto/tendermint/types" 14 "github.com/ari-anchor/sei-tendermint/types" 15 ) 16 17 /* 18 BlockStore is a simple low level store for blocks. 19 20 There are three types of information stored: 21 - BlockMeta: Meta information about each block 22 - Block part: Parts of each block, aggregated w/ PartSet 23 - Commit: The commit part of each block, for gossiping precommit votes 24 25 Currently the precommit signatures are duplicated in the Block parts as 26 well as the Commit. In the future this may change, perhaps by moving 27 the Commit data outside the Block. (TODO) 28 29 The store can be assumed to contain all contiguous blocks between base and height (inclusive). 30 31 // NOTE: BlockStore methods will panic if they encounter errors 32 // deserializing loaded data, indicating probable corruption on disk. 33 */ 34 type BlockStore struct { 35 db dbm.DB 36 } 37 38 // NewBlockStore returns a new BlockStore with the given DB, 39 // initialized to the last height that was committed to the DB. 40 func NewBlockStore(db dbm.DB) *BlockStore { 41 return &BlockStore{db} 42 } 43 44 // Base returns the first known contiguous block height, or 0 for empty block stores. 45 func (bs *BlockStore) Base() int64 { 46 iter, err := bs.db.Iterator( 47 blockMetaKey(1), 48 blockMetaKey(1<<63-1), 49 ) 50 if err != nil { 51 panic(err) 52 } 53 defer iter.Close() 54 55 if iter.Valid() { 56 height, err := decodeBlockMetaKey(iter.Key()) 57 if err == nil { 58 return height 59 } 60 } 61 if err := iter.Error(); err != nil { 62 panic(err) 63 } 64 65 return 0 66 } 67 68 // Height returns the last known contiguous block height, or 0 for empty block stores. 69 func (bs *BlockStore) Height() int64 { 70 iter, err := bs.db.ReverseIterator( 71 blockMetaKey(1), 72 blockMetaKey(1<<63-1), 73 ) 74 75 if err != nil { 76 panic(err) 77 } 78 defer iter.Close() 79 80 if iter.Valid() { 81 height, err := decodeBlockMetaKey(iter.Key()) 82 if err == nil { 83 return height 84 } 85 } 86 if err := iter.Error(); err != nil { 87 panic(err) 88 } 89 return 0 90 } 91 92 // Size returns the number of blocks in the block store. 93 func (bs *BlockStore) Size() int64 { 94 height := bs.Height() 95 if height == 0 { 96 return 0 97 } 98 return height + 1 - bs.Base() 99 } 100 101 // LoadBase atomically loads the base block meta, or returns nil if no base is found. 102 func (bs *BlockStore) LoadBaseMeta() *types.BlockMeta { 103 iter, err := bs.db.Iterator( 104 blockMetaKey(1), 105 blockMetaKey(1<<63-1), 106 ) 107 if err != nil { 108 return nil 109 } 110 defer iter.Close() 111 112 if iter.Valid() { 113 var pbbm = new(tmproto.BlockMeta) 114 err = proto.Unmarshal(iter.Value(), pbbm) 115 if err != nil { 116 panic(fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err)) 117 } 118 119 blockMeta, err := types.BlockMetaFromProto(pbbm) 120 if err != nil { 121 panic(fmt.Errorf("error from proto blockMeta: %w", err)) 122 } 123 124 return blockMeta 125 } 126 127 return nil 128 } 129 130 // LoadBlock returns the block with the given height. 131 // If no block is found for that height, it returns nil. 132 func (bs *BlockStore) LoadBlock(height int64) *types.Block { 133 var blockMeta = bs.LoadBlockMeta(height) 134 if blockMeta == nil { 135 return nil 136 } 137 138 pbb := new(tmproto.Block) 139 buf := []byte{} 140 for i := 0; i < int(blockMeta.BlockID.PartSetHeader.Total); i++ { 141 part := bs.LoadBlockPart(height, i) 142 // If the part is missing (e.g. since it has been deleted after we 143 // loaded the block meta) we consider the whole block to be missing. 144 if part == nil { 145 return nil 146 } 147 buf = append(buf, part.Bytes...) 148 } 149 err := proto.Unmarshal(buf, pbb) 150 if err != nil { 151 // NOTE: The existence of meta should imply the existence of the 152 // block. So, make sure meta is only saved after blocks are saved. 153 panic(fmt.Errorf("error reading block: %w", err)) 154 } 155 156 block, err := types.BlockFromProto(pbb) 157 if err != nil { 158 panic(fmt.Errorf("error from proto block: %w", err)) 159 } 160 161 return block 162 } 163 164 // LoadBlockByHash returns the block with the given hash. 165 // If no block is found for that hash, it returns nil. 166 // Panics if it fails to parse height associated with the given hash. 167 func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block { 168 bz, err := bs.db.Get(blockHashKey(hash)) 169 if err != nil { 170 panic(err) 171 } 172 if len(bz) == 0 { 173 return nil 174 } 175 176 s := string(bz) 177 height, err := strconv.ParseInt(s, 10, 64) 178 179 if err != nil { 180 panic(fmt.Sprintf("failed to extract height from %s: %v", s, err)) 181 } 182 return bs.LoadBlock(height) 183 } 184 185 // LoadBlockMetaByHash returns the blockmeta who's header corresponds to the given 186 // hash. If none is found, returns nil. 187 func (bs *BlockStore) LoadBlockMetaByHash(hash []byte) *types.BlockMeta { 188 bz, err := bs.db.Get(blockHashKey(hash)) 189 if err != nil { 190 panic(err) 191 } 192 if len(bz) == 0 { 193 return nil 194 } 195 196 s := string(bz) 197 height, err := strconv.ParseInt(s, 10, 64) 198 199 if err != nil { 200 panic(fmt.Sprintf("failed to extract height from %s: %v", s, err)) 201 } 202 return bs.LoadBlockMeta(height) 203 } 204 205 // LoadBlockPart returns the Part at the given index 206 // from the block at the given height. 207 // If no part is found for the given height and index, it returns nil. 208 func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part { 209 var pbpart = new(tmproto.Part) 210 211 bz, err := bs.db.Get(blockPartKey(height, index)) 212 if err != nil { 213 panic(err) 214 } 215 if len(bz) == 0 { 216 return nil 217 } 218 219 err = proto.Unmarshal(bz, pbpart) 220 if err != nil { 221 panic(fmt.Errorf("unmarshal to tmproto.Part failed: %w", err)) 222 } 223 part, err := types.PartFromProto(pbpart) 224 if err != nil { 225 panic(fmt.Errorf("error reading block part: %w", err)) 226 } 227 228 return part 229 } 230 231 // LoadBlockMeta returns the BlockMeta for the given height. 232 // If no block is found for the given height, it returns nil. 233 func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { 234 var pbbm = new(tmproto.BlockMeta) 235 bz, err := bs.db.Get(blockMetaKey(height)) 236 237 if err != nil { 238 panic(err) 239 } 240 241 if len(bz) == 0 { 242 return nil 243 } 244 245 err = proto.Unmarshal(bz, pbbm) 246 if err != nil { 247 panic(fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err)) 248 } 249 250 blockMeta, err := types.BlockMetaFromProto(pbbm) 251 if err != nil { 252 panic(fmt.Errorf("error from proto blockMeta: %w", err)) 253 } 254 255 return blockMeta 256 } 257 258 // LoadBlockCommit returns the Commit for the given height. 259 // This commit consists of the +2/3 and other Precommit-votes for block at `height`, 260 // and it comes from the block.LastCommit for `height+1`. 261 // If no commit is found for the given height, it returns nil. 262 func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit { 263 var pbc = new(tmproto.Commit) 264 bz, err := bs.db.Get(blockCommitKey(height)) 265 if err != nil { 266 panic(err) 267 } 268 if len(bz) == 0 { 269 return nil 270 } 271 err = proto.Unmarshal(bz, pbc) 272 if err != nil { 273 panic(fmt.Errorf("error reading block commit: %w", err)) 274 } 275 commit, err := types.CommitFromProto(pbc) 276 if err != nil { 277 panic(fmt.Errorf("converting commit to proto: %w", err)) 278 } 279 return commit 280 } 281 282 // LoadExtendedCommit returns the ExtendedCommit for the given height. 283 // The extended commit is not guaranteed to contain the same +2/3 precommits data 284 // as the commit in the block. 285 func (bs *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit { 286 pbec := new(tmproto.ExtendedCommit) 287 bz, err := bs.db.Get(extCommitKey(height)) 288 if err != nil { 289 panic(fmt.Errorf("fetching extended commit: %w", err)) 290 } 291 if len(bz) == 0 { 292 return nil 293 } 294 err = proto.Unmarshal(bz, pbec) 295 if err != nil { 296 panic(fmt.Errorf("decoding extended commit: %w", err)) 297 } 298 extCommit, err := types.ExtendedCommitFromProto(pbec) 299 if err != nil { 300 panic(fmt.Errorf("converting extended commit: %w", err)) 301 } 302 return extCommit 303 } 304 305 // LoadSeenCommit returns the last locally seen Commit before being 306 // cannonicalized. This is useful when we've seen a commit, but there 307 // has not yet been a new block at `height + 1` that includes this 308 // commit in its block.LastCommit. 309 func (bs *BlockStore) LoadSeenCommit() *types.Commit { 310 var pbc = new(tmproto.Commit) 311 bz, err := bs.db.Get(seenCommitKey()) 312 if err != nil { 313 panic(err) 314 } 315 if len(bz) == 0 { 316 return nil 317 } 318 err = proto.Unmarshal(bz, pbc) 319 if err != nil { 320 panic(fmt.Errorf("error reading block seen commit: %w", err)) 321 } 322 323 commit, err := types.CommitFromProto(pbc) 324 if err != nil { 325 panic(fmt.Errorf("converting seen commit: %w", err)) 326 } 327 return commit 328 } 329 330 // PruneBlocks removes block up to (but not including) a height. It returns the number of blocks pruned. 331 func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) { 332 if height <= 0 { 333 return 0, fmt.Errorf("height must be greater than 0") 334 } 335 336 if height > bs.Height() { 337 return 0, fmt.Errorf("height must be equal to or less than the latest height %d", bs.Height()) 338 } 339 340 // when removing the block meta, use the hash to remove the hash key at the same time 341 removeBlockHash := func(key, value []byte, batch dbm.Batch) error { 342 // unmarshal block meta 343 var pbbm = new(tmproto.BlockMeta) 344 err := proto.Unmarshal(value, pbbm) 345 if err != nil { 346 return fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err) 347 } 348 349 blockMeta, err := types.BlockMetaFromProto(pbbm) 350 if err != nil { 351 return fmt.Errorf("error from proto blockMeta: %w", err) 352 } 353 354 // delete the hash key corresponding to the block meta's hash 355 if err := batch.Delete(blockHashKey(blockMeta.BlockID.Hash)); err != nil { 356 return fmt.Errorf("failed to delete hash key: %X: %w", blockHashKey(blockMeta.BlockID.Hash), err) 357 } 358 359 return nil 360 } 361 362 // remove block meta first as this is used to indicate whether the block exists. 363 // For this reason, we also use ony block meta as a measure of the amount of blocks pruned 364 pruned, err := bs.pruneRange(blockMetaKey(0), blockMetaKey(height), removeBlockHash) 365 if err != nil { 366 return pruned, err 367 } 368 369 if _, err := bs.pruneRange(blockPartKey(0, 0), blockPartKey(height, 0), nil); err != nil { 370 return pruned, err 371 } 372 373 if _, err := bs.pruneRange(blockCommitKey(0), blockCommitKey(height), nil); err != nil { 374 return pruned, err 375 } 376 377 return pruned, nil 378 } 379 380 // pruneRange is a generic function for deleting a range of values based on the lowest 381 // height up to but excluding retainHeight. For each key/value pair, an optional hook can be 382 // executed before the deletion itself is made. pruneRange will use batch delete to delete 383 // keys in batches of at most 1000 keys. 384 func (bs *BlockStore) pruneRange( 385 start []byte, 386 end []byte, 387 preDeletionHook func(key, value []byte, batch dbm.Batch) error, 388 ) (uint64, error) { 389 var ( 390 err error 391 pruned uint64 392 totalPruned uint64 393 ) 394 395 batch := bs.db.NewBatch() 396 defer batch.Close() 397 398 pruned, start, err = bs.batchDelete(batch, start, end, preDeletionHook) 399 if err != nil { 400 return totalPruned, err 401 } 402 403 // loop until we have finished iterating over all the keys by writing, opening a new batch 404 // and incrementing through the next range of keys. 405 for !bytes.Equal(start, end) { 406 if err := batch.Write(); err != nil { 407 return totalPruned, err 408 } 409 410 totalPruned += pruned 411 412 if err := batch.Close(); err != nil { 413 return totalPruned, err 414 } 415 416 batch = bs.db.NewBatch() 417 418 pruned, start, err = bs.batchDelete(batch, start, end, preDeletionHook) 419 if err != nil { 420 return totalPruned, err 421 } 422 } 423 424 // once we looped over all keys we do a final flush to disk 425 if err := batch.WriteSync(); err != nil { 426 return totalPruned, err 427 } 428 totalPruned += pruned 429 return totalPruned, nil 430 } 431 432 // batchDelete runs an iterator over a set of keys, first preforming a pre deletion hook before adding it to the batch. 433 // The function ends when either 1000 keys have been added to the batch or the iterator has reached the end. 434 func (bs *BlockStore) batchDelete( 435 batch dbm.Batch, 436 start, end []byte, 437 preDeletionHook func(key, value []byte, batch dbm.Batch) error, 438 ) (uint64, []byte, error) { 439 var pruned uint64 440 iter, err := bs.db.Iterator(start, end) 441 if err != nil { 442 return pruned, start, err 443 } 444 defer iter.Close() 445 446 for ; iter.Valid(); iter.Next() { 447 key := iter.Key() 448 if preDeletionHook != nil { 449 if err := preDeletionHook(key, iter.Value(), batch); err != nil { 450 return 0, start, fmt.Errorf("pruning error at key %X: %w", iter.Key(), err) 451 } 452 } 453 454 if err := batch.Delete(key); err != nil { 455 return 0, start, fmt.Errorf("pruning error at key %X: %w", iter.Key(), err) 456 } 457 458 pruned++ 459 if pruned == 1000 { 460 return pruned, iter.Key(), iter.Error() 461 } 462 } 463 464 return pruned, end, iter.Error() 465 } 466 467 // SaveBlock persists the given block, blockParts, and seenCommit to the underlying db. 468 // blockParts: Must be parts of the block 469 // seenCommit: The +2/3 precommits that were seen which committed at height. 470 // 471 // If all the nodes restart after committing a block, 472 // we need this to reload the precommits to catch-up nodes to the 473 // most recent height. Otherwise they'd stall at H-1. 474 func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { 475 if block == nil { 476 panic("BlockStore can only save a non-nil block") 477 } 478 batch := bs.db.NewBatch() 479 if err := bs.saveBlockToBatch(batch, block, blockParts, seenCommit); err != nil { 480 panic(err) 481 } 482 483 if err := batch.WriteSync(); err != nil { 484 panic(err) 485 } 486 487 if err := batch.Close(); err != nil { 488 panic(err) 489 } 490 } 491 492 // SaveBlockWithExtendedCommit persists the given block, blockParts, and 493 // seenExtendedCommit to the underlying db. seenExtendedCommit is stored under 494 // two keys in the database: as the seenCommit and as the ExtendedCommit data for the 495 // height. This allows the vote extension data to be persisted for all blocks 496 // that are saved. 497 func (bs *BlockStore) SaveBlockWithExtendedCommit(block *types.Block, blockParts *types.PartSet, seenExtendedCommit *types.ExtendedCommit) { 498 if block == nil { 499 panic("BlockStore can only save a non-nil block") 500 } 501 if err := seenExtendedCommit.EnsureExtensions(); err != nil { 502 panic(fmt.Errorf("saving block with extensions: %w", err)) 503 } 504 batch := bs.db.NewBatch() 505 if err := bs.saveBlockToBatch(batch, block, blockParts, seenExtendedCommit.ToCommit()); err != nil { 506 panic(err) 507 } 508 height := block.Height 509 510 pbec := seenExtendedCommit.ToProto() 511 extCommitBytes := mustEncode(pbec) 512 if err := batch.Set(extCommitKey(height), extCommitBytes); err != nil { 513 panic(err) 514 } 515 516 if err := batch.WriteSync(); err != nil { 517 panic(err) 518 } 519 520 if err := batch.Close(); err != nil { 521 panic(err) 522 } 523 } 524 525 func (bs *BlockStore) saveBlockToBatch(batch dbm.Batch, block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) error { 526 if block == nil { 527 panic("BlockStore can only save a non-nil block") 528 } 529 530 height := block.Height 531 hash := block.Hash() 532 533 if g, w := height, bs.Height()+1; bs.Base() > 0 && g != w { 534 return fmt.Errorf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g) 535 } 536 if !blockParts.IsComplete() { 537 return errors.New("BlockStore can only save complete block part sets") 538 } 539 if height != seenCommit.Height { 540 return fmt.Errorf("BlockStore cannot save seen commit of a different height (block: %d, commit: %d)", height, seenCommit.Height) 541 } 542 543 // Save block parts. This must be done before the block meta, since callers 544 // typically load the block meta first as an indication that the block exists 545 // and then go on to load block parts - we must make sure the block is 546 // complete as soon as the block meta is written. 547 for i := 0; i < int(blockParts.Total()); i++ { 548 part := blockParts.GetPart(i) 549 bs.saveBlockPart(height, i, part, batch) 550 } 551 552 blockMeta := types.NewBlockMeta(block, blockParts) 553 pbm := blockMeta.ToProto() 554 if pbm == nil { 555 return errors.New("nil blockmeta") 556 } 557 558 metaBytes := mustEncode(pbm) 559 if err := batch.Set(blockMetaKey(height), metaBytes); err != nil { 560 return err 561 } 562 563 if err := batch.Set(blockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil { 564 return err 565 } 566 567 pbc := block.LastCommit.ToProto() 568 blockCommitBytes := mustEncode(pbc) 569 if err := batch.Set(blockCommitKey(height-1), blockCommitBytes); err != nil { 570 return err 571 } 572 573 // Save seen commit (seen +2/3 precommits for block) 574 pbsc := seenCommit.ToProto() 575 seenCommitBytes := mustEncode(pbsc) 576 if err := batch.Set(seenCommitKey(), seenCommitBytes); err != nil { 577 return err 578 } 579 580 return nil 581 } 582 583 func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part, batch dbm.Batch) { 584 pbp, err := part.ToProto() 585 if err != nil { 586 panic(fmt.Errorf("unable to make part into proto: %w", err)) 587 } 588 partBytes := mustEncode(pbp) 589 if err := batch.Set(blockPartKey(height, index), partBytes); err != nil { 590 panic(err) 591 } 592 } 593 594 // SaveSeenCommit saves a seen commit, used by e.g. the state sync reactor when bootstrapping node. 595 func (bs *BlockStore) SaveSeenCommit(height int64, seenCommit *types.Commit) error { 596 pbc := seenCommit.ToProto() 597 seenCommitBytes, err := proto.Marshal(pbc) 598 if err != nil { 599 return fmt.Errorf("unable to marshal commit: %w", err) 600 } 601 return bs.db.Set(seenCommitKey(), seenCommitBytes) 602 } 603 604 func (bs *BlockStore) SaveSignedHeader(sh *types.SignedHeader, blockID types.BlockID) error { 605 // first check that the block store doesn't already have the block 606 bz, err := bs.db.Get(blockMetaKey(sh.Height)) 607 if err != nil { 608 return err 609 } 610 if bz != nil { 611 return fmt.Errorf("block at height %d already saved", sh.Height) 612 } 613 614 // FIXME: saving signed headers although necessary for proving evidence, 615 // doesn't have complete parity with block meta's thus block size and num 616 // txs are filled with negative numbers. We should aim to find a solution to 617 // this. 618 blockMeta := &types.BlockMeta{ 619 BlockID: blockID, 620 BlockSize: -1, 621 Header: *sh.Header, 622 NumTxs: -1, 623 } 624 625 batch := bs.db.NewBatch() 626 627 pbm := blockMeta.ToProto() 628 metaBytes := mustEncode(pbm) 629 if err := batch.Set(blockMetaKey(sh.Height), metaBytes); err != nil { 630 return fmt.Errorf("unable to save block meta: %w", err) 631 } 632 633 pbc := sh.Commit.ToProto() 634 blockCommitBytes := mustEncode(pbc) 635 if err := batch.Set(blockCommitKey(sh.Height), blockCommitBytes); err != nil { 636 return fmt.Errorf("unable to save commit: %w", err) 637 } 638 639 if err := batch.WriteSync(); err != nil { 640 return err 641 } 642 643 return batch.Close() 644 } 645 646 func (bs *BlockStore) Close() error { 647 return bs.db.Close() 648 } 649 650 //---------------------------------- KEY ENCODING ----------------------------------------- 651 652 // key prefixes 653 // NB: Before modifying these, cross-check them with those in 654 // * internal/store/store.go [0..4, 13] 655 // * internal/state/store.go [5..8, 14] 656 // * internal/evidence/pool.go [9..10] 657 // * light/store/db/db.go [11..12] 658 // TODO(thane): Move all these to their own package. 659 // TODO: what about these (they already collide): 660 // * scripts/scmigrate/migrate.go [3] --> Looks OK, as it is also called "SeenCommit" 661 // * internal/p2p/peermanager.go [1] 662 const ( 663 // prefixes are unique across all tm db's 664 prefixBlockMeta = int64(0) 665 prefixBlockPart = int64(1) 666 prefixBlockCommit = int64(2) 667 prefixSeenCommit = int64(3) 668 prefixBlockHash = int64(4) 669 prefixExtCommit = int64(13) 670 ) 671 672 func blockMetaKey(height int64) []byte { 673 key, err := orderedcode.Append(nil, prefixBlockMeta, height) 674 if err != nil { 675 panic(err) 676 } 677 return key 678 } 679 680 func decodeBlockMetaKey(key []byte) (height int64, err error) { 681 var prefix int64 682 remaining, err := orderedcode.Parse(string(key), &prefix, &height) 683 if err != nil { 684 return 685 } 686 if len(remaining) != 0 { 687 return -1, fmt.Errorf("expected complete key but got remainder: %s", remaining) 688 } 689 if prefix != prefixBlockMeta { 690 return -1, fmt.Errorf("incorrect prefix. Expected %v, got %v", prefixBlockMeta, prefix) 691 } 692 return 693 } 694 695 func blockPartKey(height int64, partIndex int) []byte { 696 key, err := orderedcode.Append(nil, prefixBlockPart, height, int64(partIndex)) 697 if err != nil { 698 panic(err) 699 } 700 return key 701 } 702 703 func blockCommitKey(height int64) []byte { 704 key, err := orderedcode.Append(nil, prefixBlockCommit, height) 705 if err != nil { 706 panic(err) 707 } 708 return key 709 } 710 711 func seenCommitKey() []byte { 712 key, err := orderedcode.Append(nil, prefixSeenCommit) 713 if err != nil { 714 panic(err) 715 } 716 return key 717 } 718 719 func extCommitKey(height int64) []byte { 720 key, err := orderedcode.Append(nil, prefixExtCommit, height) 721 if err != nil { 722 panic(err) 723 } 724 return key 725 } 726 727 func blockHashKey(hash []byte) []byte { 728 key, err := orderedcode.Append(nil, prefixBlockHash, string(hash)) 729 if err != nil { 730 panic(err) 731 } 732 return key 733 } 734 735 //----------------------------------------------------------------------------- 736 737 // mustEncode proto encodes a proto.message and panics if fails 738 func mustEncode(pb proto.Message) []byte { 739 bz, err := proto.Marshal(pb) 740 if err != nil { 741 panic(fmt.Errorf("unable to marshal: %w", err)) 742 } 743 return bz 744 } 745 746 //----------------------------------------------------------------------------- 747 748 // DeleteLatestBlock removes the block pointed to by height, 749 // lowering height by one. 750 func (bs *BlockStore) DeleteLatestBlock() error { 751 targetHeight := bs.Height() 752 batch := bs.db.NewBatch() 753 fmt.Printf("Permanently deleting target height=%d from block store\n", targetHeight) 754 // delete what we can, skipping what's already missing, to ensure partial 755 // blocks get deleted fully. 756 if meta := bs.LoadBlockMeta(targetHeight); meta != nil { 757 if err := batch.Delete(blockHashKey(meta.BlockID.Hash)); err != nil { 758 return err 759 } 760 for p := 0; p < int(meta.BlockID.PartSetHeader.Total); p++ { 761 if err := batch.Delete(blockPartKey(targetHeight, p)); err != nil { 762 return err 763 } 764 } 765 } 766 if err := batch.Delete(blockCommitKey(targetHeight)); err != nil { 767 return err 768 } 769 if err := batch.Delete(seenCommitKey()); err != nil { 770 return err 771 } 772 // delete last, so as to not leave keys built on meta.BlockID dangling 773 if err := batch.Delete(blockMetaKey(targetHeight)); err != nil { 774 return err 775 } 776 777 err := batch.WriteSync() 778 if err != nil { 779 return fmt.Errorf("failed to delete height %v: %w", targetHeight, err) 780 } 781 782 if err := batch.Close(); err != nil { 783 panic(err) 784 } 785 return nil 786 }