github.com/Oyster-zx/tendermint@v0.34.24-fork/store/store.go (about) 1 package store 2 3 import ( 4 "fmt" 5 "strconv" 6 7 "github.com/gogo/protobuf/proto" 8 dbm "github.com/tendermint/tm-db" 9 10 tmsync "github.com/tendermint/tendermint/libs/sync" 11 tmstore "github.com/tendermint/tendermint/proto/tendermint/store" 12 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 13 "github.com/tendermint/tendermint/types" 14 ) 15 16 /* 17 BlockStore is a simple low level store for blocks. 18 19 There are three types of information stored: 20 - BlockMeta: Meta information about each block 21 - Block part: Parts of each block, aggregated w/ PartSet 22 - Commit: The commit part of each block, for gossiping precommit votes 23 24 Currently the precommit signatures are duplicated in the Block parts as 25 well as the Commit. In the future this may change, perhaps by moving 26 the Commit data outside the Block. (TODO) 27 28 The store can be assumed to contain all contiguous blocks between base and height (inclusive). 29 30 // NOTE: BlockStore methods will panic if they encounter errors 31 // deserializing loaded data, indicating probable corruption on disk. 32 */ 33 type BlockStore struct { 34 db dbm.DB 35 36 // mtx guards access to the struct fields listed below it. We rely on the database to enforce 37 // fine-grained concurrency control for its data, and thus this mutex does not apply to 38 // database contents. The only reason for keeping these fields in the struct is that the data 39 // can't efficiently be queried from the database since the key encoding we use is not 40 // lexicographically ordered (see https://github.com/tendermint/tendermint/issues/4567). 41 mtx tmsync.RWMutex 42 base int64 43 height int64 44 } 45 46 // NewBlockStore returns a new BlockStore with the given DB, 47 // initialized to the last height that was committed to the DB. 48 func NewBlockStore(db dbm.DB) *BlockStore { 49 bs := LoadBlockStoreState(db) 50 return &BlockStore{ 51 base: bs.Base, 52 height: bs.Height, 53 db: db, 54 } 55 } 56 57 // Base returns the first known contiguous block height, or 0 for empty block stores. 58 func (bs *BlockStore) Base() int64 { 59 bs.mtx.RLock() 60 defer bs.mtx.RUnlock() 61 return bs.base 62 } 63 64 // Height returns the last known contiguous block height, or 0 for empty block stores. 65 func (bs *BlockStore) Height() int64 { 66 bs.mtx.RLock() 67 defer bs.mtx.RUnlock() 68 return bs.height 69 } 70 71 // Size returns the number of blocks in the block store. 72 func (bs *BlockStore) Size() int64 { 73 bs.mtx.RLock() 74 defer bs.mtx.RUnlock() 75 if bs.height == 0 { 76 return 0 77 } 78 return bs.height - bs.base + 1 79 } 80 81 // LoadBase atomically loads the base block meta, or returns nil if no base is found. 82 func (bs *BlockStore) LoadBaseMeta() *types.BlockMeta { 83 bs.mtx.RLock() 84 defer bs.mtx.RUnlock() 85 if bs.base == 0 { 86 return nil 87 } 88 return bs.LoadBlockMeta(bs.base) 89 } 90 91 // LoadBlock returns the block with the given height. 92 // If no block is found for that height, it returns nil. 93 func (bs *BlockStore) LoadBlock(height int64) *types.Block { 94 var blockMeta = bs.LoadBlockMeta(height) 95 if blockMeta == nil { 96 return nil 97 } 98 99 pbb := new(tmproto.Block) 100 buf := []byte{} 101 for i := 0; i < int(blockMeta.BlockID.PartSetHeader.Total); i++ { 102 part := bs.LoadBlockPart(height, i) 103 // If the part is missing (e.g. since it has been deleted after we 104 // loaded the block meta) we consider the whole block to be missing. 105 if part == nil { 106 return nil 107 } 108 buf = append(buf, part.Bytes...) 109 } 110 err := proto.Unmarshal(buf, pbb) 111 if err != nil { 112 // NOTE: The existence of meta should imply the existence of the 113 // block. So, make sure meta is only saved after blocks are saved. 114 panic(fmt.Sprintf("Error reading block: %v", err)) 115 } 116 117 block, err := types.BlockFromProto(pbb) 118 if err != nil { 119 panic(fmt.Errorf("error from proto block: %w", err)) 120 } 121 122 return block 123 } 124 125 // LoadBlockByHash returns the block with the given hash. 126 // If no block is found for that hash, it returns nil. 127 // Panics if it fails to parse height associated with the given hash. 128 func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block { 129 bz, err := bs.db.Get(calcBlockHashKey(hash)) 130 if err != nil { 131 panic(err) 132 } 133 if len(bz) == 0 { 134 return nil 135 } 136 137 s := string(bz) 138 height, err := strconv.ParseInt(s, 10, 64) 139 140 if err != nil { 141 panic(fmt.Sprintf("failed to extract height from %s: %v", s, err)) 142 } 143 return bs.LoadBlock(height) 144 } 145 146 // LoadBlockPart returns the Part at the given index 147 // from the block at the given height. 148 // If no part is found for the given height and index, it returns nil. 149 func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part { 150 var pbpart = new(tmproto.Part) 151 152 bz, err := bs.db.Get(calcBlockPartKey(height, index)) 153 if err != nil { 154 panic(err) 155 } 156 if len(bz) == 0 { 157 return nil 158 } 159 160 err = proto.Unmarshal(bz, pbpart) 161 if err != nil { 162 panic(fmt.Errorf("unmarshal to tmproto.Part failed: %w", err)) 163 } 164 part, err := types.PartFromProto(pbpart) 165 if err != nil { 166 panic(fmt.Sprintf("Error reading block part: %v", err)) 167 } 168 169 return part 170 } 171 172 // LoadBlockMeta returns the BlockMeta for the given height. 173 // If no block is found for the given height, it returns nil. 174 func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { 175 var pbbm = new(tmproto.BlockMeta) 176 bz, err := bs.db.Get(calcBlockMetaKey(height)) 177 178 if err != nil { 179 panic(err) 180 } 181 182 if len(bz) == 0 { 183 return nil 184 } 185 186 err = proto.Unmarshal(bz, pbbm) 187 if err != nil { 188 panic(fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err)) 189 } 190 191 blockMeta, err := types.BlockMetaFromProto(pbbm) 192 if err != nil { 193 panic(fmt.Errorf("error from proto blockMeta: %w", err)) 194 } 195 196 return blockMeta 197 } 198 199 // LoadBlockCommit returns the Commit for the given height. 200 // This commit consists of the +2/3 and other Precommit-votes for block at `height`, 201 // and it comes from the block.LastCommit for `height+1`. 202 // If no commit is found for the given height, it returns nil. 203 func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit { 204 var pbc = new(tmproto.Commit) 205 bz, err := bs.db.Get(calcBlockCommitKey(height)) 206 if err != nil { 207 panic(err) 208 } 209 if len(bz) == 0 { 210 return nil 211 } 212 err = proto.Unmarshal(bz, pbc) 213 if err != nil { 214 panic(fmt.Errorf("error reading block commit: %w", err)) 215 } 216 commit, err := types.CommitFromProto(pbc) 217 if err != nil { 218 panic(fmt.Sprintf("Error reading block commit: %v", err)) 219 } 220 return commit 221 } 222 223 // LoadSeenCommit returns the locally seen Commit for the given height. 224 // This is useful when we've seen a commit, but there has not yet been 225 // a new block at `height + 1` that includes this commit in its block.LastCommit. 226 func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { 227 var pbc = new(tmproto.Commit) 228 bz, err := bs.db.Get(calcSeenCommitKey(height)) 229 if err != nil { 230 panic(err) 231 } 232 if len(bz) == 0 { 233 return nil 234 } 235 err = proto.Unmarshal(bz, pbc) 236 if err != nil { 237 panic(fmt.Sprintf("error reading block seen commit: %v", err)) 238 } 239 240 commit, err := types.CommitFromProto(pbc) 241 if err != nil { 242 panic(fmt.Errorf("error from proto commit: %w", err)) 243 } 244 return commit 245 } 246 247 // PruneBlocks removes block up to (but not including) a height. It returns number of blocks pruned. 248 func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) { 249 if height <= 0 { 250 return 0, fmt.Errorf("height must be greater than 0") 251 } 252 bs.mtx.RLock() 253 if height > bs.height { 254 bs.mtx.RUnlock() 255 return 0, fmt.Errorf("cannot prune beyond the latest height %v", bs.height) 256 } 257 base := bs.base 258 bs.mtx.RUnlock() 259 if height < base { 260 return 0, fmt.Errorf("cannot prune to height %v, it is lower than base height %v", 261 height, base) 262 } 263 264 pruned := uint64(0) 265 batch := bs.db.NewBatch() 266 defer batch.Close() 267 flush := func(batch dbm.Batch, base int64) error { 268 // We can't trust batches to be atomic, so update base first to make sure noone 269 // tries to access missing blocks. 270 bs.mtx.Lock() 271 bs.base = base 272 bs.mtx.Unlock() 273 bs.saveState() 274 275 err := batch.WriteSync() 276 if err != nil { 277 return fmt.Errorf("failed to prune up to height %v: %w", base, err) 278 } 279 batch.Close() 280 return nil 281 } 282 283 for h := base; h < height; h++ { 284 meta := bs.LoadBlockMeta(h) 285 if meta == nil { // assume already deleted 286 continue 287 } 288 if err := batch.Delete(calcBlockMetaKey(h)); err != nil { 289 return 0, err 290 } 291 if err := batch.Delete(calcBlockHashKey(meta.BlockID.Hash)); err != nil { 292 return 0, err 293 } 294 if err := batch.Delete(calcBlockCommitKey(h)); err != nil { 295 return 0, err 296 } 297 if err := batch.Delete(calcSeenCommitKey(h)); err != nil { 298 return 0, err 299 } 300 for p := 0; p < int(meta.BlockID.PartSetHeader.Total); p++ { 301 if err := batch.Delete(calcBlockPartKey(h, p)); err != nil { 302 return 0, err 303 } 304 } 305 pruned++ 306 307 // flush every 1000 blocks to avoid batches becoming too large 308 if pruned%1000 == 0 && pruned > 0 { 309 err := flush(batch, h) 310 if err != nil { 311 return 0, err 312 } 313 batch = bs.db.NewBatch() 314 defer batch.Close() 315 } 316 } 317 318 err := flush(batch, height) 319 if err != nil { 320 return 0, err 321 } 322 return pruned, nil 323 } 324 325 // SaveBlock persists the given block, blockParts, and seenCommit to the underlying db. 326 // blockParts: Must be parts of the block 327 // seenCommit: The +2/3 precommits that were seen which committed at height. 328 // 329 // If all the nodes restart after committing a block, 330 // we need this to reload the precommits to catch-up nodes to the 331 // most recent height. Otherwise they'd stall at H-1. 332 func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { 333 if block == nil { 334 panic("BlockStore can only save a non-nil block") 335 } 336 337 height := block.Height 338 hash := block.Hash() 339 340 if g, w := height, bs.Height()+1; bs.Base() > 0 && g != w { 341 panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g)) 342 } 343 if !blockParts.IsComplete() { 344 panic("BlockStore can only save complete block part sets") 345 } 346 347 // Save block parts. This must be done before the block meta, since callers 348 // typically load the block meta first as an indication that the block exists 349 // and then go on to load block parts - we must make sure the block is 350 // complete as soon as the block meta is written. 351 for i := 0; i < int(blockParts.Total()); i++ { 352 part := blockParts.GetPart(i) 353 bs.saveBlockPart(height, i, part) 354 } 355 356 // Save block meta 357 blockMeta := types.NewBlockMeta(block, blockParts) 358 pbm := blockMeta.ToProto() 359 if pbm == nil { 360 panic("nil blockmeta") 361 } 362 metaBytes := mustEncode(pbm) 363 if err := bs.db.Set(calcBlockMetaKey(height), metaBytes); err != nil { 364 panic(err) 365 } 366 if err := bs.db.Set(calcBlockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil { 367 panic(err) 368 } 369 370 // Save block commit (duplicate and separate from the Block) 371 pbc := block.LastCommit.ToProto() 372 blockCommitBytes := mustEncode(pbc) 373 if err := bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes); err != nil { 374 panic(err) 375 } 376 377 // Save seen commit (seen +2/3 precommits for block) 378 // NOTE: we can delete this at a later height 379 pbsc := seenCommit.ToProto() 380 seenCommitBytes := mustEncode(pbsc) 381 if err := bs.db.Set(calcSeenCommitKey(height), seenCommitBytes); err != nil { 382 panic(err) 383 } 384 385 // Done! 386 bs.mtx.Lock() 387 bs.height = height 388 if bs.base == 0 { 389 bs.base = height 390 } 391 bs.mtx.Unlock() 392 393 // Save new BlockStoreState descriptor. This also flushes the database. 394 bs.saveState() 395 } 396 397 func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) { 398 pbp, err := part.ToProto() 399 if err != nil { 400 panic(fmt.Errorf("unable to make part into proto: %w", err)) 401 } 402 partBytes := mustEncode(pbp) 403 if err := bs.db.Set(calcBlockPartKey(height, index), partBytes); err != nil { 404 panic(err) 405 } 406 } 407 408 func (bs *BlockStore) saveState() { 409 bs.mtx.RLock() 410 bss := tmstore.BlockStoreState{ 411 Base: bs.base, 412 Height: bs.height, 413 } 414 bs.mtx.RUnlock() 415 SaveBlockStoreState(&bss, bs.db) 416 } 417 418 // SaveSeenCommit saves a seen commit, used by e.g. the state sync reactor when bootstrapping node. 419 func (bs *BlockStore) SaveSeenCommit(height int64, seenCommit *types.Commit) error { 420 pbc := seenCommit.ToProto() 421 seenCommitBytes, err := proto.Marshal(pbc) 422 if err != nil { 423 return fmt.Errorf("unable to marshal commit: %w", err) 424 } 425 return bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) 426 } 427 428 func (bs *BlockStore) Close() error { 429 return bs.db.Close() 430 } 431 432 //----------------------------------------------------------------------------- 433 434 func calcBlockMetaKey(height int64) []byte { 435 return []byte(fmt.Sprintf("H:%v", height)) 436 } 437 438 func calcBlockPartKey(height int64, partIndex int) []byte { 439 return []byte(fmt.Sprintf("P:%v:%v", height, partIndex)) 440 } 441 442 func calcBlockCommitKey(height int64) []byte { 443 return []byte(fmt.Sprintf("C:%v", height)) 444 } 445 446 func calcSeenCommitKey(height int64) []byte { 447 return []byte(fmt.Sprintf("SC:%v", height)) 448 } 449 450 func calcBlockHashKey(hash []byte) []byte { 451 return []byte(fmt.Sprintf("BH:%x", hash)) 452 } 453 454 //----------------------------------------------------------------------------- 455 456 var blockStoreKey = []byte("blockStore") 457 458 // SaveBlockStoreState persists the blockStore state to the database. 459 func SaveBlockStoreState(bsj *tmstore.BlockStoreState, db dbm.DB) { 460 bytes, err := proto.Marshal(bsj) 461 if err != nil { 462 panic(fmt.Sprintf("Could not marshal state bytes: %v", err)) 463 } 464 if err := db.SetSync(blockStoreKey, bytes); err != nil { 465 panic(err) 466 } 467 } 468 469 // LoadBlockStoreState returns the BlockStoreState as loaded from disk. 470 // If no BlockStoreState was previously persisted, it returns the zero value. 471 func LoadBlockStoreState(db dbm.DB) tmstore.BlockStoreState { 472 bytes, err := db.Get(blockStoreKey) 473 if err != nil { 474 panic(err) 475 } 476 477 if len(bytes) == 0 { 478 return tmstore.BlockStoreState{ 479 Base: 0, 480 Height: 0, 481 } 482 } 483 484 var bsj tmstore.BlockStoreState 485 if err := proto.Unmarshal(bytes, &bsj); err != nil { 486 panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes)) 487 } 488 489 // Backwards compatibility with persisted data from before Base existed. 490 if bsj.Height > 0 && bsj.Base == 0 { 491 bsj.Base = 1 492 } 493 return bsj 494 } 495 496 // mustEncode proto encodes a proto.message and panics if fails 497 func mustEncode(pb proto.Message) []byte { 498 bz, err := proto.Marshal(pb) 499 if err != nil { 500 panic(fmt.Errorf("unable to marshal: %w", err)) 501 } 502 return bz 503 }