github.com/vipernet-xyz/tendermint-core@v0.32.0/store/store.go (about) 1 package store 2 3 import ( 4 "fmt" 5 "strconv" 6 "sync" 7 "time" 8 9 "github.com/pkg/errors" 10 11 db "github.com/tendermint/tm-db" 12 dbm "github.com/tendermint/tm-db" 13 14 "github.com/tendermint/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 mtx sync.RWMutex 38 base int64 39 height int64 40 } 41 42 // NewBlockStore returns a new BlockStore with the given DB, 43 // initialized to the last height that was committed to the DB. 44 func NewBlockStore(db dbm.DB) *BlockStore { 45 bsjson := LoadBlockStoreStateJSON(db) 46 return &BlockStore{ 47 base: bsjson.Base, 48 height: bsjson.Height, 49 db: db, 50 } 51 } 52 53 // Base returns the first known contiguous block height, or 0 for empty block stores. 54 func (bs *BlockStore) Base() int64 { 55 bs.mtx.RLock() 56 defer bs.mtx.RUnlock() 57 return bs.base 58 } 59 60 // Height returns the last known contiguous block height, or 0 for empty block stores. 61 func (bs *BlockStore) Height() int64 { 62 bs.mtx.RLock() 63 defer bs.mtx.RUnlock() 64 return bs.height 65 } 66 67 // Size returns the number of blocks in the block store. 68 func (bs *BlockStore) Size() int64 { 69 bs.mtx.RLock() 70 defer bs.mtx.RUnlock() 71 if bs.height == 0 { 72 return 0 73 } 74 return bs.height - bs.base + 1 75 } 76 77 // LoadBlock returns the block with the given height. 78 // If no block is found for that height, it returns nil. 79 func (bs *BlockStore) LoadBlock(height int64) *types.Block { 80 var blockMeta = bs.LoadBlockMeta(height) 81 if blockMeta == nil { 82 return nil 83 } 84 85 var block = new(types.Block) 86 buf := []byte{} 87 for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ { 88 part := bs.LoadBlockPart(height, i) 89 buf = append(buf, part.Bytes...) 90 } 91 err := cdc.UnmarshalBinaryLengthPrefixed(buf, block) 92 if err != nil { 93 // NOTE: The existence of meta should imply the existence of the 94 // block. So, make sure meta is only saved after blocks are saved. 95 panic(errors.Wrap(err, "Error reading block")) 96 } 97 return block 98 } 99 100 // LoadBlockByHash returns the block with the given hash. 101 // If no block is found for that hash, it returns nil. 102 // Panics if it fails to parse height associated with the given hash. 103 func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block { 104 bz, err := bs.db.Get(calcBlockHashKey(hash)) 105 if err != nil { 106 panic(err) 107 } 108 if len(bz) == 0 { 109 return nil 110 } 111 112 s := string(bz) 113 height, err := strconv.ParseInt(s, 10, 64) 114 115 if err != nil { 116 panic(errors.Wrapf(err, "failed to extract height from %s", s)) 117 } 118 return bs.LoadBlock(height) 119 } 120 121 // LoadBlockPart returns the Part at the given index 122 // from the block at the given height. 123 // If no part is found for the given height and index, it returns nil. 124 func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part { 125 var part = new(types.Part) 126 bz, err := bs.db.Get(calcBlockPartKey(height, index)) 127 if err != nil { 128 panic(err) 129 } 130 if len(bz) == 0 { 131 return nil 132 } 133 err = cdc.UnmarshalBinaryBare(bz, part) 134 if err != nil { 135 panic(errors.Wrap(err, "Error reading block part")) 136 } 137 return part 138 } 139 140 // LoadBlockMeta returns the BlockMeta for the given height. 141 // If no block is found for the given height, it returns nil. 142 func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { 143 var blockMeta = new(types.BlockMeta) 144 bz, err := bs.db.Get(calcBlockMetaKey(height)) 145 if err != nil { 146 panic(err) 147 } 148 if len(bz) == 0 { 149 return nil 150 } 151 err = cdc.UnmarshalBinaryBare(bz, blockMeta) 152 if err != nil { 153 panic(errors.Wrap(err, "Error reading block meta")) 154 } 155 return blockMeta 156 } 157 158 // LoadBlockCommit returns the Commit for the given height. 159 // This commit consists of the +2/3 and other Precommit-votes for block at `height`, 160 // and it comes from the block.LastCommit for `height+1`. 161 // If no commit is found for the given height, it returns nil. 162 func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit { 163 var commit = new(types.Commit) 164 bz, err := bs.db.Get(calcBlockCommitKey(height)) 165 if err != nil { 166 panic(err) 167 } 168 if len(bz) == 0 { 169 return nil 170 } 171 err = cdc.UnmarshalBinaryBare(bz, commit) 172 if err != nil { 173 panic(errors.Wrap(err, "Error reading block commit")) 174 } 175 return commit 176 } 177 178 // LoadSeenCommit returns the locally seen Commit for the given height. 179 // This is useful when we've seen a commit, but there has not yet been 180 // a new block at `height + 1` that includes this commit in its block.LastCommit. 181 func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { 182 var commit = new(types.Commit) 183 bz, err := bs.db.Get(calcSeenCommitKey(height)) 184 if err != nil { 185 panic(err) 186 } 187 if len(bz) == 0 { 188 return nil 189 } 190 err = cdc.UnmarshalBinaryBare(bz, commit) 191 if err != nil { 192 panic(errors.Wrap(err, "Error reading block seen commit")) 193 } 194 return commit 195 } 196 197 // PruneBlocks removes block up to (but not including) a height. It returns number of blocks pruned. 198 func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) { 199 if height <= 0 { 200 return 0, fmt.Errorf("height must be greater than 0") 201 } 202 bs.mtx.RLock() 203 if height > bs.height { 204 bs.mtx.RUnlock() 205 return 0, fmt.Errorf("cannot prune beyond the latest height %v", bs.height) 206 } 207 base := bs.base 208 bs.mtx.RUnlock() 209 if height < base { 210 return 0, fmt.Errorf("cannot prune to height %v, it is lower than base height %v", 211 height, base) 212 } 213 214 pruned := uint64(0) 215 batch := bs.db.NewBatch() 216 defer batch.Close() 217 flush := func(batch db.Batch, base int64) error { 218 // We can't trust batches to be atomic, so update base first to make sure noone 219 // tries to access missing blocks. 220 bs.mtx.Lock() 221 bs.base = base 222 bs.mtx.Unlock() 223 bs.saveState() 224 225 err := batch.WriteSync() 226 if err != nil { 227 return fmt.Errorf("failed to prune up to height %v: %w", base, err) 228 } 229 batch.Close() 230 return nil 231 } 232 233 for h := base; h < height; h++ { 234 meta := bs.LoadBlockMeta(h) 235 if meta == nil { // assume already deleted 236 continue 237 } 238 batch.Delete(calcBlockMetaKey(h)) 239 batch.Delete(calcBlockHashKey(meta.BlockID.Hash)) 240 batch.Delete(calcBlockCommitKey(h)) 241 batch.Delete(calcSeenCommitKey(h)) 242 for p := 0; p < meta.BlockID.PartsHeader.Total; p++ { 243 batch.Delete(calcBlockPartKey(h, p)) 244 } 245 pruned++ 246 247 // flush every 1000 blocks to avoid batches becoming too large 248 if pruned%1000 == 0 && pruned > 0 { 249 err := flush(batch, h) 250 if err != nil { 251 return 0, err 252 } 253 batch = bs.db.NewBatch() 254 defer batch.Close() 255 } 256 } 257 258 err := flush(batch, height) 259 if err != nil { 260 return 0, err 261 } 262 return pruned, nil 263 } 264 265 // SaveBlock persists the given block, blockParts, and seenCommit to the underlying db. 266 // blockParts: Must be parts of the block 267 // seenCommit: The +2/3 precommits that were seen which committed at height. 268 // If all the nodes restart after committing a block, 269 // we need this to reload the precommits to catch-up nodes to the 270 // most recent height. Otherwise they'd stall at H-1. 271 func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { 272 defer types.TimeTrack(time.Now(), nil) 273 274 if block == nil { 275 panic("BlockStore can only save a non-nil block") 276 } 277 278 height := block.Height 279 hash := block.Hash() 280 281 if g, w := height, bs.Height()+1; bs.Base() > 0 && g != w { 282 panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g)) 283 } 284 if !blockParts.IsComplete() { 285 panic(fmt.Sprintf("BlockStore can only save complete block part sets")) 286 } 287 288 // Save block meta 289 blockMeta := types.NewBlockMeta(block, blockParts) 290 metaBytes := cdc.MustMarshalBinaryBare(blockMeta) 291 bs.db.Set(calcBlockMetaKey(height), metaBytes) 292 bs.db.Set(calcBlockHashKey(hash), []byte(fmt.Sprintf("%d", height))) 293 294 // Save block parts 295 for i := 0; i < blockParts.Total(); i++ { 296 part := blockParts.GetPart(i) 297 bs.saveBlockPart(height, i, part) 298 } 299 300 // Save block commit (duplicate and separate from the Block) 301 blockCommitBytes := cdc.MustMarshalBinaryBare(block.LastCommit) 302 bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes) 303 304 // Save seen commit (seen +2/3 precommits for block) 305 // NOTE: we can delete this at a later height 306 seenCommitBytes := cdc.MustMarshalBinaryBare(seenCommit) 307 bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) 308 309 // Done! 310 bs.mtx.Lock() 311 bs.height = height 312 if bs.base == 0 { 313 bs.base = height 314 } 315 bs.mtx.Unlock() 316 317 // Save new BlockStoreStateJSON descriptor 318 bs.saveState() 319 320 // Flush 321 bs.db.SetSync(nil, nil) 322 } 323 324 func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) { 325 partBytes := cdc.MustMarshalBinaryBare(part) 326 bs.db.Set(calcBlockPartKey(height, index), partBytes) 327 } 328 329 func (bs *BlockStore) saveState() { 330 bs.mtx.RLock() 331 bsJSON := BlockStoreStateJSON{ 332 Base: bs.base, 333 Height: bs.height, 334 } 335 bs.mtx.RUnlock() 336 bsJSON.Save(bs.db) 337 } 338 339 //----------------------------------------------------------------------------- 340 341 func calcBlockMetaKey(height int64) []byte { 342 return []byte(fmt.Sprintf("H:%v", height)) 343 } 344 345 func calcBlockPartKey(height int64, partIndex int) []byte { 346 return []byte(fmt.Sprintf("P:%v:%v", height, partIndex)) 347 } 348 349 func calcBlockCommitKey(height int64) []byte { 350 return []byte(fmt.Sprintf("C:%v", height)) 351 } 352 353 func calcSeenCommitKey(height int64) []byte { 354 return []byte(fmt.Sprintf("SC:%v", height)) 355 } 356 357 func calcBlockHashKey(hash []byte) []byte { 358 return []byte(fmt.Sprintf("BH:%x", hash)) 359 } 360 361 //----------------------------------------------------------------------------- 362 363 var blockStoreKey = []byte("blockStore") 364 365 // BlockStoreStateJSON is the block store state JSON structure. 366 type BlockStoreStateJSON struct { 367 Base int64 `json:"base"` 368 Height int64 `json:"height"` 369 } 370 371 // Save persists the blockStore state to the database as JSON. 372 func (bsj BlockStoreStateJSON) Save(db dbm.DB) { 373 bytes, err := cdc.MarshalJSON(bsj) 374 if err != nil { 375 panic(fmt.Sprintf("Could not marshal state bytes: %v", err)) 376 } 377 db.SetSync(blockStoreKey, bytes) 378 } 379 380 // LoadBlockStoreStateJSON returns the BlockStoreStateJSON as loaded from disk. 381 // If no BlockStoreStateJSON was previously persisted, it returns the zero value. 382 func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON { 383 bytes, err := db.Get(blockStoreKey) 384 if err != nil { 385 panic(err) 386 } 387 if len(bytes) == 0 { 388 return BlockStoreStateJSON{ 389 Base: 0, 390 Height: 0, 391 } 392 } 393 bsj := BlockStoreStateJSON{} 394 err = cdc.UnmarshalJSON(bytes, &bsj) 395 if err != nil { 396 panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes)) 397 } 398 // Backwards compatibility with persisted data from before Base existed. 399 if bsj.Height > 0 && bsj.Base == 0 { 400 bsj.Base = 1 401 } 402 return bsj 403 }