| 85 | |
| 86 | template <typename ParserTag> |
| 87 | std::vector<blocksci::RawBlock> updateChain(const ParserConfiguration<ParserTag> &config, blocksci::BlockHeight maxBlockNum, HashIndexCreator &hashDb) { |
| 88 | using namespace std::chrono_literals; |
| 89 | |
| 90 | /* Load and update the persisted (serialized) ChainIndex object that contains information about all blocks (without transaction data) |
| 91 | * Generate the ChainIndex if no data is available on disk. |
| 92 | * |
| 93 | * This step represents the "xx.x% done fetching block headers" step of the parser output messages. |
| 94 | */ |
| 95 | auto chainBlocks = [&]() { |
| 96 | ChainIndex<ParserTag> index; |
| 97 | std::ifstream inFile(config.blockListPath().str(), std::ios::binary); |
| 98 | if (inFile.good()) { |
| 99 | try { |
| 100 | cereal::BinaryInputArchive ia(inFile); |
| 101 | ia(index); |
| 102 | } catch (const std::exception &) { |
| 103 | std::cout << "Error loading chain index. Reparsing from scratch\n"; |
| 104 | index = ChainIndex<ParserTag>{}; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | index.update(config, maxBlockNum); |
| 109 | auto blocks = index.generateChain(maxBlockNum); |
| 110 | std::ofstream of(config.blockListPath().str(), std::ios::binary); |
| 111 | cereal::BinaryOutputArchive oa(of); |
| 112 | oa(index); |
| 113 | return blocks; |
| 114 | }(); |
| 115 | |
| 116 | /* Determine whether blocks have to be removed from the old chain (due to a fork, eg. caused by miners) |
| 117 | * |
| 118 | * This step represents the "Starting with chain of X blocks" and "Adding X blocks" step of the parser output messages. |
| 119 | */ |
| 120 | blocksci::BlockHeight splitPoint = [&]() { |
| 121 | blocksci::ChainAccess oldChain{config.dataConfig.chainDirectory(), config.dataConfig.blocksIgnored, config.dataConfig.errorOnReorg}; |
| 122 | blocksci::BlockHeight maxSize = std::min(oldChain.blockCount(), static_cast<blocksci::BlockHeight>(chainBlocks.size())); |
| 123 | auto splitPoint = maxSize; |
| 124 | for (blocksci::BlockHeight i{0}; i < maxSize; i++) { |
| 125 | blocksci::uint256 oldHash = oldChain.getBlock(maxSize - 1 - i)->hash; |
| 126 | blocksci::uint256 newHash = chainBlocks[static_cast<size_t>(static_cast<int>(maxSize - 1 - i))].hash; |
| 127 | if (!(oldHash == newHash)) { |
| 128 | splitPoint = maxSize - 1 - i; |
| 129 | break; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | if(static_cast<blocksci::BlockHeight>(oldChain.blockCount()) != splitPoint) { |
| 134 | std::cout << "Previously parsed chain is on a different fork than the longest chain. You may need to reparse. Aborting." << std::endl; |
| 135 | exit(1); |
| 136 | } |
| 137 | |
| 138 | std::cout << "Starting with chain of " << oldChain.blockCount() << " blocks" << std::endl; |
| 139 | std::cout << "Adding " << static_cast<blocksci::BlockHeight>(chainBlocks.size()) - splitPoint << " blocks" << std::endl; |
| 140 | |
| 141 | return splitPoint; |
| 142 | }(); |
| 143 | |
| 144 | std::vector<BlockInfo<ParserTag>> blocksToAdd{chainBlocks.begin() + static_cast<int>(splitPoint), chainBlocks.end()}; |
no test coverage detected