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