github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/blockfile_mgr.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package blkstorage 8 9 import ( 10 "bytes" 11 "fmt" 12 "math" 13 "sync" 14 "sync/atomic" 15 16 "github.com/davecgh/go-spew/spew" 17 "github.com/golang/protobuf/proto" 18 "github.com/hyperledger/fabric-protos-go/common" 19 "github.com/hyperledger/fabric-protos-go/peer" 20 "github.com/osdi23p228/fabric/common/ledger/util" 21 "github.com/osdi23p228/fabric/common/ledger/util/leveldbhelper" 22 "github.com/osdi23p228/fabric/protoutil" 23 "github.com/pkg/errors" 24 ) 25 26 const ( 27 blockfilePrefix = "blockfile_" 28 bootstrappingSnapshotInfoFile = "bootstrappingSnapshot.info" 29 bootstrappingSnapshotInfoTempFile = "bootstrappingSnapshotTemp.info" 30 ) 31 32 var ( 33 blkMgrInfoKey = []byte("blkMgrInfo") 34 ) 35 36 type blockfileMgr struct { 37 rootDir string 38 conf *Conf 39 db *leveldbhelper.DBHandle 40 index *blockIndex 41 blockfilesInfo *blockfilesInfo 42 bootstrappingSnapshotInfo *BootstrappingSnapshotInfo 43 blkfilesInfoCond *sync.Cond 44 currentFileWriter *blockfileWriter 45 bcInfo atomic.Value 46 } 47 48 /* 49 Creates a new manager that will manage the files used for block persistence. 50 This manager manages the file system FS including 51 -- the directory where the files are stored 52 -- the individual files where the blocks are stored 53 -- the blockfilesInfo which tracks the latest file being persisted to 54 -- the index which tracks what block and transaction is in what file 55 When a new blockfile manager is started (i.e. only on start-up), it checks 56 if this start-up is the first time the system is coming up or is this a restart 57 of the system. 58 59 The blockfile manager stores blocks of data into a file system. That file 60 storage is done by creating sequentially numbered files of a configured size 61 i.e blockfile_000000, blockfile_000001, etc.. 62 63 Each transaction in a block is stored with information about the number of 64 bytes in that transaction 65 Adding txLoc [fileSuffixNum=0, offset=3, bytesLength=104] for tx [1:0] to index 66 Adding txLoc [fileSuffixNum=0, offset=107, bytesLength=104] for tx [1:1] to index 67 Each block is stored with the total encoded length of that block as well as the 68 tx location offsets. 69 70 Remember that these steps are only done once at start-up of the system. 71 At start up a new manager: 72 *) Checks if the directory for storing files exists, if not creates the dir 73 *) Checks if the key value database exists, if not creates one 74 (will create a db dir) 75 *) Determines the blockfilesInfo used for storage 76 -- Loads from db if exist, if not instantiate a new blockfilesInfo 77 -- If blockfilesInfo was loaded from db, compares to FS 78 -- If blockfilesInfo and file system are not in sync, syncs blockfilesInfo from FS 79 *) Starts a new file writer 80 -- truncates file per blockfilesInfo to remove any excess past last block 81 *) Determines the index information used to find tx and blocks in 82 the file blkstorage 83 -- Instantiates a new blockIdxInfo 84 -- Loads the index from the db if exists 85 -- syncIndex comparing the last block indexed to what is in the FS 86 -- If index and file system are not in sync, syncs index from the FS 87 *) Updates blockchain info used by the APIs 88 */ 89 func newBlockfileMgr(id string, conf *Conf, indexConfig *IndexConfig, indexStore *leveldbhelper.DBHandle) (*blockfileMgr, error) { 90 logger.Debugf("newBlockfileMgr() initializing file-based block storage for ledger: %s ", id) 91 rootDir := conf.getLedgerBlockDir(id) 92 _, err := util.CreateDirIfMissing(rootDir) 93 if err != nil { 94 panic(fmt.Sprintf("Error creating block storage root dir [%s]: %s", rootDir, err)) 95 } 96 mgr := &blockfileMgr{rootDir: rootDir, conf: conf, db: indexStore} 97 98 blockfilesInfo, err := mgr.loadBlkfilesInfo() 99 if err != nil { 100 panic(fmt.Sprintf("Could not get block file info for current block file from db: %s", err)) 101 } 102 if blockfilesInfo == nil { 103 logger.Info(`Getting block information from block storage`) 104 if blockfilesInfo, err = constructBlockfilesInfo(rootDir); err != nil { 105 panic(fmt.Sprintf("Could not build blockfilesInfo info from block files: %s", err)) 106 } 107 logger.Debugf("Info constructed by scanning the blocks dir = %s", spew.Sdump(blockfilesInfo)) 108 } else { 109 logger.Debug(`Synching block information from block storage (if needed)`) 110 syncBlockfilesInfoFromFS(rootDir, blockfilesInfo) 111 } 112 err = mgr.saveBlkfilesInfo(blockfilesInfo, true) 113 if err != nil { 114 panic(fmt.Sprintf("Could not save next block file info to db: %s", err)) 115 } 116 117 currentFileWriter, err := newBlockfileWriter(deriveBlockfilePath(rootDir, blockfilesInfo.latestFileNumber)) 118 if err != nil { 119 panic(fmt.Sprintf("Could not open writer to current file: %s", err)) 120 } 121 err = currentFileWriter.truncateFile(blockfilesInfo.latestFileSize) 122 if err != nil { 123 panic(fmt.Sprintf("Could not truncate current file to known size in db: %s", err)) 124 } 125 if mgr.index, err = newBlockIndex(indexConfig, indexStore); err != nil { 126 panic(fmt.Sprintf("error in block index: %s", err)) 127 } 128 129 mgr.blockfilesInfo = blockfilesInfo 130 bsi, err := loadBootstrappingSnapshotInfo(rootDir) 131 if err != nil { 132 return nil, err 133 } 134 mgr.bootstrappingSnapshotInfo = bsi 135 mgr.currentFileWriter = currentFileWriter 136 mgr.blkfilesInfoCond = sync.NewCond(&sync.Mutex{}) 137 138 if err := mgr.syncIndex(); err != nil { 139 return nil, err 140 } 141 142 bcInfo := &common.BlockchainInfo{} 143 144 if mgr.bootstrappingSnapshotInfo != nil { 145 bcInfo.Height = mgr.bootstrappingSnapshotInfo.LastBlockNum + 1 146 bcInfo.CurrentBlockHash = mgr.bootstrappingSnapshotInfo.LastBlockHash 147 bcInfo.PreviousBlockHash = mgr.bootstrappingSnapshotInfo.PreviousBlockHash 148 } 149 150 if !blockfilesInfo.noBlockFiles { 151 lastBlockHeader, err := mgr.retrieveBlockHeaderByNumber(blockfilesInfo.lastPersistedBlock) 152 if err != nil { 153 panic(fmt.Sprintf("Could not retrieve header of the last block form file: %s", err)) 154 } 155 lastBlockHash := protoutil.BlockHeaderHash(lastBlockHeader) 156 previousBlockHash := lastBlockHeader.PreviousHash 157 bcInfo = &common.BlockchainInfo{ 158 Height: blockfilesInfo.lastPersistedBlock + 1, 159 CurrentBlockHash: lastBlockHash, 160 PreviousBlockHash: previousBlockHash} 161 } 162 mgr.bcInfo.Store(bcInfo) 163 return mgr, nil 164 } 165 166 func bootstrapFromSnapshottedTxIDs( 167 snapshotDir string, 168 snapshotInfo *SnapshotInfo, 169 conf *Conf, 170 indexStore *leveldbhelper.DBHandle, 171 ) error { 172 rootDir := conf.getLedgerBlockDir(snapshotInfo.LedgerID) 173 isEmpty, err := util.CreateDirIfMissing(rootDir) 174 if err != nil { 175 return err 176 } 177 if !isEmpty { 178 return errors.Errorf("dir %s not empty", rootDir) 179 } 180 181 bsi := &BootstrappingSnapshotInfo{ 182 LastBlockNum: snapshotInfo.LastBlockNum, 183 LastBlockHash: snapshotInfo.LastBlockHash, 184 PreviousBlockHash: snapshotInfo.PreviousBlockHash, 185 } 186 187 bsiBytes, err := proto.Marshal(bsi) 188 if err != nil { 189 return err 190 } 191 192 if err := createAndSyncFileAtomically( 193 rootDir, 194 bootstrappingSnapshotInfoTempFile, 195 bootstrappingSnapshotInfoFile, 196 bsiBytes, 197 ); err != nil { 198 return err 199 } 200 if err := importTxIDsFromSnapshot(snapshotDir, snapshotInfo.LastBlockNum, indexStore); err != nil { 201 return err 202 } 203 return nil 204 } 205 206 func syncBlockfilesInfoFromFS(rootDir string, blkfilesInfo *blockfilesInfo) { 207 logger.Debugf("Starting blockfilesInfo=%s", blkfilesInfo) 208 //Checks if the file suffix of where the last block was written exists 209 filePath := deriveBlockfilePath(rootDir, blkfilesInfo.latestFileNumber) 210 exists, size, err := util.FileExists(filePath) 211 if err != nil { 212 panic(fmt.Sprintf("Error in checking whether file [%s] exists: %s", filePath, err)) 213 } 214 logger.Debugf("status of file [%s]: exists=[%t], size=[%d]", filePath, exists, size) 215 //Test is !exists because when file number is first used the file does not exist yet 216 //checks that the file exists and that the size of the file is what is stored in blockfilesInfo 217 //status of file [<blkstorage_location>/blocks/blockfile_000000]: exists=[false], size=[0] 218 if !exists || int(size) == blkfilesInfo.latestFileSize { 219 // blockfilesInfo is in sync with the file on disk 220 return 221 } 222 //Scan the file system to verify that the blockfilesInfo stored in db is correct 223 _, endOffsetLastBlock, numBlocks, err := scanForLastCompleteBlock( 224 rootDir, blkfilesInfo.latestFileNumber, int64(blkfilesInfo.latestFileSize)) 225 if err != nil { 226 panic(fmt.Sprintf("Could not open current file for detecting last block in the file: %s", err)) 227 } 228 blkfilesInfo.latestFileSize = int(endOffsetLastBlock) 229 if numBlocks == 0 { 230 return 231 } 232 //Updates the blockfilesInfo for the actual last block number stored and it's end location 233 if blkfilesInfo.noBlockFiles { 234 blkfilesInfo.lastPersistedBlock = uint64(numBlocks - 1) 235 } else { 236 blkfilesInfo.lastPersistedBlock += uint64(numBlocks) 237 } 238 blkfilesInfo.noBlockFiles = false 239 logger.Debugf("blockfilesInfo after updates by scanning the last file segment:%s", blkfilesInfo) 240 } 241 242 func deriveBlockfilePath(rootDir string, suffixNum int) string { 243 return rootDir + "/" + blockfilePrefix + fmt.Sprintf("%06d", suffixNum) 244 } 245 246 func (mgr *blockfileMgr) close() { 247 mgr.currentFileWriter.close() 248 } 249 250 func (mgr *blockfileMgr) moveToNextFile() { 251 blkfilesInfo := &blockfilesInfo{ 252 latestFileNumber: mgr.blockfilesInfo.latestFileNumber + 1, 253 latestFileSize: 0, 254 lastPersistedBlock: mgr.blockfilesInfo.lastPersistedBlock} 255 256 nextFileWriter, err := newBlockfileWriter( 257 deriveBlockfilePath(mgr.rootDir, blkfilesInfo.latestFileNumber)) 258 259 if err != nil { 260 panic(fmt.Sprintf("Could not open writer to next file: %s", err)) 261 } 262 mgr.currentFileWriter.close() 263 err = mgr.saveBlkfilesInfo(blkfilesInfo, true) 264 if err != nil { 265 panic(fmt.Sprintf("Could not save next block file info to db: %s", err)) 266 } 267 mgr.currentFileWriter = nextFileWriter 268 mgr.updateBlockfilesInfo(blkfilesInfo) 269 } 270 271 func (mgr *blockfileMgr) addBlock(block *common.Block) error { 272 bcInfo := mgr.getBlockchainInfo() 273 if block.Header.Number != bcInfo.Height { 274 return errors.Errorf( 275 "block number should have been %d but was %d", 276 mgr.getBlockchainInfo().Height, block.Header.Number, 277 ) 278 } 279 280 // Add the previous hash check - Though, not essential but may not be a bad idea to 281 // verify the field `block.Header.PreviousHash` present in the block. 282 // This check is a simple bytes comparison and hence does not cause any observable performance penalty 283 // and may help in detecting a rare scenario if there is any bug in the ordering service. 284 if !bytes.Equal(block.Header.PreviousHash, bcInfo.CurrentBlockHash) { 285 return errors.Errorf( 286 "unexpected Previous block hash. Expected PreviousHash = [%x], PreviousHash referred in the latest block= [%x]", 287 bcInfo.CurrentBlockHash, block.Header.PreviousHash, 288 ) 289 } 290 blockBytes, info, err := serializeBlock(block) 291 if err != nil { 292 return errors.WithMessage(err, "error serializing block") 293 } 294 blockHash := protoutil.BlockHeaderHash(block.Header) 295 //Get the location / offset where each transaction starts in the block and where the block ends 296 txOffsets := info.txOffsets 297 currentOffset := mgr.blockfilesInfo.latestFileSize 298 299 blockBytesLen := len(blockBytes) 300 blockBytesEncodedLen := proto.EncodeVarint(uint64(blockBytesLen)) 301 totalBytesToAppend := blockBytesLen + len(blockBytesEncodedLen) 302 303 //Determine if we need to start a new file since the size of this block 304 //exceeds the amount of space left in the current file 305 if currentOffset+totalBytesToAppend > mgr.conf.maxBlockfileSize { 306 mgr.moveToNextFile() 307 currentOffset = 0 308 } 309 //append blockBytesEncodedLen to the file 310 err = mgr.currentFileWriter.append(blockBytesEncodedLen, false) 311 if err == nil { 312 //append the actual block bytes to the file 313 err = mgr.currentFileWriter.append(blockBytes, true) 314 } 315 if err != nil { 316 truncateErr := mgr.currentFileWriter.truncateFile(mgr.blockfilesInfo.latestFileSize) 317 if truncateErr != nil { 318 panic(fmt.Sprintf("Could not truncate current file to known size after an error during block append: %s", err)) 319 } 320 return errors.WithMessage(err, "error appending block to file") 321 } 322 323 //Update the blockfilesInfo with the results of adding the new block 324 currentBlkfilesInfo := mgr.blockfilesInfo 325 newBlkfilesInfo := &blockfilesInfo{ 326 latestFileNumber: currentBlkfilesInfo.latestFileNumber, 327 latestFileSize: currentBlkfilesInfo.latestFileSize + totalBytesToAppend, 328 noBlockFiles: false, 329 lastPersistedBlock: block.Header.Number} 330 //save the blockfilesInfo in the database 331 if err = mgr.saveBlkfilesInfo(newBlkfilesInfo, false); err != nil { 332 truncateErr := mgr.currentFileWriter.truncateFile(currentBlkfilesInfo.latestFileSize) 333 if truncateErr != nil { 334 panic(fmt.Sprintf("Error in truncating current file to known size after an error in saving blockfiles info: %s", err)) 335 } 336 return errors.WithMessage(err, "error saving blockfiles file info to db") 337 } 338 339 //Index block file location pointer updated with file suffex and offset for the new block 340 blockFLP := &fileLocPointer{fileSuffixNum: newBlkfilesInfo.latestFileNumber} 341 blockFLP.offset = currentOffset 342 // shift the txoffset because we prepend length of bytes before block bytes 343 for _, txOffset := range txOffsets { 344 txOffset.loc.offset += len(blockBytesEncodedLen) 345 } 346 //save the index in the database 347 if err = mgr.index.indexBlock(&blockIdxInfo{ 348 blockNum: block.Header.Number, blockHash: blockHash, 349 flp: blockFLP, txOffsets: txOffsets, metadata: block.Metadata}); err != nil { 350 return err 351 } 352 353 //update the blockfilesInfo (for storage) and the blockchain info (for APIs) in the manager 354 mgr.updateBlockfilesInfo(newBlkfilesInfo) 355 mgr.updateBlockchainInfo(blockHash, block) 356 return nil 357 } 358 359 func (mgr *blockfileMgr) syncIndex() error { 360 nextIndexableBlock := uint64(0) 361 lastBlockIndexed, err := mgr.index.getLastBlockIndexed() 362 if err != nil { 363 if err != errIndexSavePointKeyNotPresent { 364 return err 365 } 366 } else { 367 nextIndexableBlock = lastBlockIndexed + 1 368 } 369 370 if nextIndexableBlock == 0 && mgr.bootstrappedFromSnapshot() { 371 // This condition can happen only if there was a peer crash or failure during 372 // bootstrapping the ledger from a snapshot or the index is dropped/corrupted afterward 373 return errors.Errorf( 374 "cannot sync index with block files. blockstore is bootstrapped from a snapshot and first available block=[%d]", 375 mgr.firstPossibleBlockNumberInBlockFiles(), 376 ) 377 } 378 379 if mgr.blockfilesInfo.noBlockFiles { 380 logger.Debug("No block files present. This happens when there has not been any blocks added to the ledger yet") 381 return nil 382 } 383 384 if nextIndexableBlock == mgr.blockfilesInfo.lastPersistedBlock+1 { 385 logger.Debug("Both the block files and indices are in sync.") 386 return nil 387 } 388 389 startFileNum := 0 390 startOffset := 0 391 skipFirstBlock := false 392 endFileNum := mgr.blockfilesInfo.latestFileNumber 393 394 firstAvailableBlkNum, err := retrieveFirstBlockNumFromFile(mgr.rootDir, 0) 395 if err != nil { 396 return err 397 } 398 399 if nextIndexableBlock > firstAvailableBlkNum { 400 logger.Debugf("Last block indexed [%d], Last block present in block files [%d]", lastBlockIndexed, mgr.blockfilesInfo.lastPersistedBlock) 401 var flp *fileLocPointer 402 if flp, err = mgr.index.getBlockLocByBlockNum(lastBlockIndexed); err != nil { 403 return err 404 } 405 startFileNum = flp.fileSuffixNum 406 startOffset = flp.locPointer.offset 407 skipFirstBlock = true 408 } 409 410 logger.Infof("Start building index from block [%d] to last block [%d]", nextIndexableBlock, mgr.blockfilesInfo.lastPersistedBlock) 411 412 //open a blockstream to the file location that was stored in the index 413 var stream *blockStream 414 if stream, err = newBlockStream(mgr.rootDir, startFileNum, int64(startOffset), endFileNum); err != nil { 415 return err 416 } 417 var blockBytes []byte 418 var blockPlacementInfo *blockPlacementInfo 419 420 if skipFirstBlock { 421 if blockBytes, _, err = stream.nextBlockBytesAndPlacementInfo(); err != nil { 422 return err 423 } 424 if blockBytes == nil { 425 return errors.Errorf("block bytes for block num = [%d] should not be nil here. The indexes for the block are already present", 426 lastBlockIndexed) 427 } 428 } 429 430 //Should be at the last block already, but go ahead and loop looking for next blockBytes. 431 //If there is another block, add it to the index. 432 //This will ensure block indexes are correct, for example if peer had crashed before indexes got updated. 433 blockIdxInfo := &blockIdxInfo{} 434 for { 435 if blockBytes, blockPlacementInfo, err = stream.nextBlockBytesAndPlacementInfo(); err != nil { 436 return err 437 } 438 if blockBytes == nil { 439 break 440 } 441 info, err := extractSerializedBlockInfo(blockBytes) 442 if err != nil { 443 return err 444 } 445 446 //The blockStartOffset will get applied to the txOffsets prior to indexing within indexBlock(), 447 //therefore just shift by the difference between blockBytesOffset and blockStartOffset 448 numBytesToShift := int(blockPlacementInfo.blockBytesOffset - blockPlacementInfo.blockStartOffset) 449 for _, offset := range info.txOffsets { 450 offset.loc.offset += numBytesToShift 451 } 452 453 //Update the blockIndexInfo with what was actually stored in file system 454 blockIdxInfo.blockHash = protoutil.BlockHeaderHash(info.blockHeader) 455 blockIdxInfo.blockNum = info.blockHeader.Number 456 blockIdxInfo.flp = &fileLocPointer{fileSuffixNum: blockPlacementInfo.fileNum, 457 locPointer: locPointer{offset: int(blockPlacementInfo.blockStartOffset)}} 458 blockIdxInfo.txOffsets = info.txOffsets 459 blockIdxInfo.metadata = info.metadata 460 461 logger.Debugf("syncIndex() indexing block [%d]", blockIdxInfo.blockNum) 462 if err = mgr.index.indexBlock(blockIdxInfo); err != nil { 463 return err 464 } 465 if blockIdxInfo.blockNum%10000 == 0 { 466 logger.Infof("Indexed block number [%d]", blockIdxInfo.blockNum) 467 } 468 } 469 logger.Infof("Finished building index. Last block indexed [%d]", blockIdxInfo.blockNum) 470 return nil 471 } 472 473 func (mgr *blockfileMgr) getBlockchainInfo() *common.BlockchainInfo { 474 return mgr.bcInfo.Load().(*common.BlockchainInfo) 475 } 476 477 func (mgr *blockfileMgr) updateBlockfilesInfo(blkfilesInfo *blockfilesInfo) { 478 mgr.blkfilesInfoCond.L.Lock() 479 defer mgr.blkfilesInfoCond.L.Unlock() 480 mgr.blockfilesInfo = blkfilesInfo 481 logger.Debugf("Broadcasting about update blockfilesInfo: %s", blkfilesInfo) 482 mgr.blkfilesInfoCond.Broadcast() 483 } 484 485 func (mgr *blockfileMgr) updateBlockchainInfo(latestBlockHash []byte, latestBlock *common.Block) { 486 currentBCInfo := mgr.getBlockchainInfo() 487 newBCInfo := &common.BlockchainInfo{ 488 Height: currentBCInfo.Height + 1, 489 CurrentBlockHash: latestBlockHash, 490 PreviousBlockHash: latestBlock.Header.PreviousHash} 491 492 mgr.bcInfo.Store(newBCInfo) 493 } 494 495 func (mgr *blockfileMgr) retrieveBlockByHash(blockHash []byte) (*common.Block, error) { 496 logger.Debugf("retrieveBlockByHash() - blockHash = [%#v]", blockHash) 497 loc, err := mgr.index.getBlockLocByHash(blockHash) 498 if err != nil { 499 return nil, err 500 } 501 return mgr.fetchBlock(loc) 502 } 503 504 func (mgr *blockfileMgr) retrieveBlockByNumber(blockNum uint64) (*common.Block, error) { 505 logger.Debugf("retrieveBlockByNumber() - blockNum = [%d]", blockNum) 506 507 // interpret math.MaxUint64 as a request for last block 508 if blockNum == math.MaxUint64 { 509 blockNum = mgr.getBlockchainInfo().Height - 1 510 } 511 if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() { 512 return nil, errors.Errorf( 513 "cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]", 514 blockNum, mgr.firstPossibleBlockNumberInBlockFiles(), 515 ) 516 } 517 loc, err := mgr.index.getBlockLocByBlockNum(blockNum) 518 if err != nil { 519 return nil, err 520 } 521 return mgr.fetchBlock(loc) 522 } 523 524 func (mgr *blockfileMgr) retrieveBlockByTxID(txID string) (*common.Block, error) { 525 logger.Debugf("retrieveBlockByTxID() - txID = [%s]", txID) 526 loc, err := mgr.index.getBlockLocByTxID(txID) 527 if err == errNilValue { 528 return nil, errors.Errorf( 529 "details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]", 530 txID, mgr.firstPossibleBlockNumberInBlockFiles()) 531 } 532 if err != nil { 533 return nil, err 534 } 535 return mgr.fetchBlock(loc) 536 } 537 538 func (mgr *blockfileMgr) retrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) { 539 logger.Debugf("retrieveTxValidationCodeByTxID() - txID = [%s]", txID) 540 validationCode, err := mgr.index.getTxValidationCodeByTxID(txID) 541 if err == errNilValue { 542 return peer.TxValidationCode(-1), errors.Errorf( 543 "details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]", 544 txID, mgr.firstPossibleBlockNumberInBlockFiles()) 545 } 546 return validationCode, err 547 } 548 549 func (mgr *blockfileMgr) retrieveBlockHeaderByNumber(blockNum uint64) (*common.BlockHeader, error) { 550 logger.Debugf("retrieveBlockHeaderByNumber() - blockNum = [%d]", blockNum) 551 if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() { 552 return nil, errors.Errorf( 553 "cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]", 554 blockNum, mgr.firstPossibleBlockNumberInBlockFiles(), 555 ) 556 } 557 loc, err := mgr.index.getBlockLocByBlockNum(blockNum) 558 if err != nil { 559 return nil, err 560 } 561 blockBytes, err := mgr.fetchBlockBytes(loc) 562 if err != nil { 563 return nil, err 564 } 565 info, err := extractSerializedBlockInfo(blockBytes) 566 if err != nil { 567 return nil, err 568 } 569 return info.blockHeader, nil 570 } 571 572 func (mgr *blockfileMgr) retrieveBlocks(startNum uint64) (*blocksItr, error) { 573 if startNum < mgr.firstPossibleBlockNumberInBlockFiles() { 574 return nil, errors.Errorf( 575 "cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]", 576 startNum, mgr.firstPossibleBlockNumberInBlockFiles(), 577 ) 578 } 579 return newBlockItr(mgr, startNum), nil 580 } 581 582 func (mgr *blockfileMgr) retrieveTransactionByID(txID string) (*common.Envelope, error) { 583 logger.Debugf("retrieveTransactionByID() - txId = [%s]", txID) 584 loc, err := mgr.index.getTxLoc(txID) 585 if err == errNilValue { 586 return nil, errors.Errorf( 587 "details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]", 588 txID, mgr.firstPossibleBlockNumberInBlockFiles()) 589 } 590 if err != nil { 591 return nil, err 592 } 593 return mgr.fetchTransactionEnvelope(loc) 594 } 595 596 func (mgr *blockfileMgr) retrieveTransactionByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error) { 597 logger.Debugf("retrieveTransactionByBlockNumTranNum() - blockNum = [%d], tranNum = [%d]", blockNum, tranNum) 598 if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() { 599 return nil, errors.Errorf( 600 "cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]", 601 blockNum, mgr.firstPossibleBlockNumberInBlockFiles(), 602 ) 603 } 604 loc, err := mgr.index.getTXLocByBlockNumTranNum(blockNum, tranNum) 605 if err != nil { 606 return nil, err 607 } 608 return mgr.fetchTransactionEnvelope(loc) 609 } 610 611 func (mgr *blockfileMgr) fetchBlock(lp *fileLocPointer) (*common.Block, error) { 612 blockBytes, err := mgr.fetchBlockBytes(lp) 613 if err != nil { 614 return nil, err 615 } 616 block, err := deserializeBlock(blockBytes) 617 if err != nil { 618 return nil, err 619 } 620 return block, nil 621 } 622 623 func (mgr *blockfileMgr) fetchTransactionEnvelope(lp *fileLocPointer) (*common.Envelope, error) { 624 logger.Debugf("Entering fetchTransactionEnvelope() %v\n", lp) 625 var err error 626 var txEnvelopeBytes []byte 627 if txEnvelopeBytes, err = mgr.fetchRawBytes(lp); err != nil { 628 return nil, err 629 } 630 _, n := proto.DecodeVarint(txEnvelopeBytes) 631 return protoutil.GetEnvelopeFromBlock(txEnvelopeBytes[n:]) 632 } 633 634 func (mgr *blockfileMgr) fetchBlockBytes(lp *fileLocPointer) ([]byte, error) { 635 stream, err := newBlockfileStream(mgr.rootDir, lp.fileSuffixNum, int64(lp.offset)) 636 if err != nil { 637 return nil, err 638 } 639 defer stream.close() 640 b, err := stream.nextBlockBytes() 641 if err != nil { 642 return nil, err 643 } 644 return b, nil 645 } 646 647 func (mgr *blockfileMgr) fetchRawBytes(lp *fileLocPointer) ([]byte, error) { 648 filePath := deriveBlockfilePath(mgr.rootDir, lp.fileSuffixNum) 649 reader, err := newBlockfileReader(filePath) 650 if err != nil { 651 return nil, err 652 } 653 defer reader.close() 654 b, err := reader.read(lp.offset, lp.bytesLength) 655 if err != nil { 656 return nil, err 657 } 658 return b, nil 659 } 660 661 //Get the current blockfilesInfo information that is stored in the database 662 func (mgr *blockfileMgr) loadBlkfilesInfo() (*blockfilesInfo, error) { 663 var b []byte 664 var err error 665 if b, err = mgr.db.Get(blkMgrInfoKey); b == nil || err != nil { 666 return nil, err 667 } 668 i := &blockfilesInfo{} 669 if err = i.unmarshal(b); err != nil { 670 return nil, err 671 } 672 logger.Debugf("loaded blockfilesInfo:%s", i) 673 return i, nil 674 } 675 676 func (mgr *blockfileMgr) saveBlkfilesInfo(i *blockfilesInfo, sync bool) error { 677 b, err := i.marshal() 678 if err != nil { 679 return err 680 } 681 if err = mgr.db.Put(blkMgrInfoKey, b, sync); err != nil { 682 return err 683 } 684 return nil 685 } 686 687 func (mgr *blockfileMgr) firstPossibleBlockNumberInBlockFiles() uint64 { 688 if mgr.bootstrappingSnapshotInfo == nil { 689 return 0 690 } 691 return mgr.bootstrappingSnapshotInfo.LastBlockNum + 1 692 } 693 694 func (mgr *blockfileMgr) bootstrappedFromSnapshot() bool { 695 return mgr.firstPossibleBlockNumberInBlockFiles() > 0 696 } 697 698 // scanForLastCompleteBlock scan a given block file and detects the last offset in the file 699 // after which there may lie a block partially written (towards the end of the file in a crash scenario). 700 func scanForLastCompleteBlock(rootDir string, fileNum int, startingOffset int64) ([]byte, int64, int, error) { 701 //scan the passed file number suffix starting from the passed offset to find the last completed block 702 numBlocks := 0 703 var lastBlockBytes []byte 704 blockStream, errOpen := newBlockfileStream(rootDir, fileNum, startingOffset) 705 if errOpen != nil { 706 return nil, 0, 0, errOpen 707 } 708 defer blockStream.close() 709 var errRead error 710 var blockBytes []byte 711 for { 712 blockBytes, errRead = blockStream.nextBlockBytes() 713 if blockBytes == nil || errRead != nil { 714 break 715 } 716 lastBlockBytes = blockBytes 717 numBlocks++ 718 } 719 if errRead == ErrUnexpectedEndOfBlockfile { 720 logger.Debugf(`Error:%s 721 The error may happen if a crash has happened during block appending. 722 Resetting error to nil and returning current offset as a last complete block's end offset`, errRead) 723 errRead = nil 724 } 725 logger.Debugf("scanForLastCompleteBlock(): last complete block ends at offset=[%d]", blockStream.currentOffset) 726 return lastBlockBytes, blockStream.currentOffset, numBlocks, errRead 727 } 728 729 // blockfilesInfo maintains the summary about the blockfiles 730 type blockfilesInfo struct { 731 latestFileNumber int 732 latestFileSize int 733 noBlockFiles bool 734 lastPersistedBlock uint64 735 } 736 737 func (i *blockfilesInfo) marshal() ([]byte, error) { 738 buffer := proto.NewBuffer([]byte{}) 739 var err error 740 if err = buffer.EncodeVarint(uint64(i.latestFileNumber)); err != nil { 741 return nil, errors.Wrapf(err, "error encoding the latestFileNumber [%d]", i.latestFileNumber) 742 } 743 if err = buffer.EncodeVarint(uint64(i.latestFileSize)); err != nil { 744 return nil, errors.Wrapf(err, "error encoding the latestFileSize [%d]", i.latestFileSize) 745 } 746 if err = buffer.EncodeVarint(i.lastPersistedBlock); err != nil { 747 return nil, errors.Wrapf(err, "error encoding the lastPersistedBlock [%d]", i.lastPersistedBlock) 748 } 749 var noBlockFilesMarker uint64 750 if i.noBlockFiles { 751 noBlockFilesMarker = 1 752 } 753 if err = buffer.EncodeVarint(noBlockFilesMarker); err != nil { 754 return nil, errors.Wrapf(err, "error encoding noBlockFiles [%d]", noBlockFilesMarker) 755 } 756 return buffer.Bytes(), nil 757 } 758 759 func (i *blockfilesInfo) unmarshal(b []byte) error { 760 buffer := proto.NewBuffer(b) 761 var val uint64 762 var noBlockFilesMarker uint64 763 var err error 764 765 if val, err = buffer.DecodeVarint(); err != nil { 766 return err 767 } 768 i.latestFileNumber = int(val) 769 770 if val, err = buffer.DecodeVarint(); err != nil { 771 return err 772 } 773 i.latestFileSize = int(val) 774 775 if val, err = buffer.DecodeVarint(); err != nil { 776 return err 777 } 778 i.lastPersistedBlock = val 779 if noBlockFilesMarker, err = buffer.DecodeVarint(); err != nil { 780 return err 781 } 782 i.noBlockFiles = noBlockFilesMarker == 1 783 return nil 784 } 785 786 func (i *blockfilesInfo) String() string { 787 return fmt.Sprintf("latestFileNumber=[%d], latestFileSize=[%d], noBlockFiles=[%t], lastPersistedBlock=[%d]", 788 i.latestFileNumber, i.latestFileSize, i.noBlockFiles, i.lastPersistedBlock) 789 }