|
4 | 4 |
|
5 | 5 | #include <map>
|
6 | 6 |
|
| 7 | +#include <dbwrapper.h> |
7 | 8 | #include <index/blockfilterindex.h>
|
8 | 9 | #include <util/system.h>
|
9 | 10 | #include <validation.h>
|
@@ -143,6 +144,26 @@ bool BlockFilterIndex::CommitInternal(CDBBatch& batch)
|
143 | 144 | return BaseIndex::CommitInternal(batch);
|
144 | 145 | }
|
145 | 146 |
|
| 147 | +bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const |
| 148 | +{ |
| 149 | + CAutoFile filein(m_filter_fileseq->Open(pos, true), SER_DISK, CLIENT_VERSION); |
| 150 | + if (filein.IsNull()) { |
| 151 | + return false; |
| 152 | + } |
| 153 | + |
| 154 | + uint256 block_hash; |
| 155 | + std::vector<unsigned char> encoded_filter; |
| 156 | + try { |
| 157 | + filein >> block_hash >> encoded_filter; |
| 158 | + filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter)); |
| 159 | + } |
| 160 | + catch (const std::exception& e) { |
| 161 | + return error("%s: Failed to deserialize block filter from disk: %s", __func__, e.what()); |
| 162 | + } |
| 163 | + |
| 164 | + return true; |
| 165 | +} |
| 166 | + |
146 | 167 | size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter)
|
147 | 168 | {
|
148 | 169 | assert(filter.GetFilterType() == GetFilterType());
|
@@ -280,3 +301,134 @@ bool BlockFilterIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex*
|
280 | 301 |
|
281 | 302 | return BaseIndex::Rewind(current_tip, new_tip);
|
282 | 303 | }
|
| 304 | + |
| 305 | +static bool LookupOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result) |
| 306 | +{ |
| 307 | + // First check if the result is stored under the height index and the value there matches the |
| 308 | + // block hash. This should be the case if the block is on the active chain. |
| 309 | + std::pair<uint256, DBVal> read_out; |
| 310 | + if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) { |
| 311 | + return false; |
| 312 | + } |
| 313 | + if (read_out.first == block_index->GetBlockHash()) { |
| 314 | + result = std::move(read_out.second); |
| 315 | + return true; |
| 316 | + } |
| 317 | + |
| 318 | + // If value at the height index corresponds to an different block, the result will be stored in |
| 319 | + // the hash index. |
| 320 | + return db.Read(DBHashKey(block_index->GetBlockHash()), result); |
| 321 | +} |
| 322 | + |
| 323 | +static bool LookupRange(CDBWrapper& db, const std::string& index_name, int start_height, |
| 324 | + const CBlockIndex* stop_index, std::vector<DBVal>& results) |
| 325 | +{ |
| 326 | + if (start_height < 0) { |
| 327 | + return error("%s: start height (%d) is negative", __func__, start_height); |
| 328 | + } |
| 329 | + if (start_height > stop_index->nHeight) { |
| 330 | + return error("%s: start height (%d) is greater than stop height (%d)", |
| 331 | + __func__, start_height, stop_index->nHeight); |
| 332 | + } |
| 333 | + |
| 334 | + size_t results_size = static_cast<size_t>(stop_index->nHeight - start_height + 1); |
| 335 | + std::vector<std::pair<uint256, DBVal>> values(results_size); |
| 336 | + |
| 337 | + DBHeightKey key(start_height); |
| 338 | + std::unique_ptr<CDBIterator> db_it(db.NewIterator()); |
| 339 | + db_it->Seek(DBHeightKey(start_height)); |
| 340 | + for (int height = start_height; height <= stop_index->nHeight; ++height) { |
| 341 | + if (!db_it->Valid() || !db_it->GetKey(key) || key.height != height) { |
| 342 | + return false; |
| 343 | + } |
| 344 | + |
| 345 | + size_t i = static_cast<size_t>(height - start_height); |
| 346 | + if (!db_it->GetValue(values[i])) { |
| 347 | + return error("%s: unable to read value in %s at key (%c, %d)", |
| 348 | + __func__, index_name, DB_BLOCK_HEIGHT, height); |
| 349 | + } |
| 350 | + |
| 351 | + db_it->Next(); |
| 352 | + } |
| 353 | + |
| 354 | + results.resize(results_size); |
| 355 | + |
| 356 | + // Iterate backwards through block indexes collecting results in order to access the block hash |
| 357 | + // of each entry in case we need to look it up in the hash index. |
| 358 | + for (const CBlockIndex* block_index = stop_index; |
| 359 | + block_index && block_index->nHeight >= start_height; |
| 360 | + block_index = block_index->pprev) { |
| 361 | + uint256 block_hash = block_index->GetBlockHash(); |
| 362 | + |
| 363 | + size_t i = static_cast<size_t>(block_index->nHeight - start_height); |
| 364 | + if (block_hash == values[i].first) { |
| 365 | + results[i] = std::move(values[i].second); |
| 366 | + continue; |
| 367 | + } |
| 368 | + |
| 369 | + if (!db.Read(DBHashKey(block_hash), results[i])) { |
| 370 | + return error("%s: unable to read value in %s at key (%c, %s)", |
| 371 | + __func__, index_name, DB_BLOCK_HASH, block_hash.ToString()); |
| 372 | + } |
| 373 | + } |
| 374 | + |
| 375 | + return true; |
| 376 | +} |
| 377 | + |
| 378 | +bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const |
| 379 | +{ |
| 380 | + DBVal entry; |
| 381 | + if (!LookupOne(*m_db, block_index, entry)) { |
| 382 | + return false; |
| 383 | + } |
| 384 | + |
| 385 | + return ReadFilterFromDisk(entry.pos, filter_out); |
| 386 | +} |
| 387 | + |
| 388 | +bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const |
| 389 | +{ |
| 390 | + DBVal entry; |
| 391 | + if (!LookupOne(*m_db, block_index, entry)) { |
| 392 | + return false; |
| 393 | + } |
| 394 | + |
| 395 | + header_out = entry.header; |
| 396 | + return true; |
| 397 | +} |
| 398 | + |
| 399 | +bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* stop_index, |
| 400 | + std::vector<BlockFilter>& filters_out) const |
| 401 | +{ |
| 402 | + std::vector<DBVal> entries; |
| 403 | + if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) { |
| 404 | + return false; |
| 405 | + } |
| 406 | + |
| 407 | + filters_out.resize(entries.size()); |
| 408 | + auto filter_pos_it = filters_out.begin(); |
| 409 | + for (const auto& entry : entries) { |
| 410 | + if (!ReadFilterFromDisk(entry.pos, *filter_pos_it)) { |
| 411 | + return false; |
| 412 | + } |
| 413 | + ++filter_pos_it; |
| 414 | + } |
| 415 | + |
| 416 | + return true; |
| 417 | +} |
| 418 | + |
| 419 | +bool BlockFilterIndex::LookupFilterHashRange(int start_height, const CBlockIndex* stop_index, |
| 420 | + std::vector<uint256>& hashes_out) const |
| 421 | + |
| 422 | +{ |
| 423 | + std::vector<DBVal> entries; |
| 424 | + if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) { |
| 425 | + return false; |
| 426 | + } |
| 427 | + |
| 428 | + hashes_out.clear(); |
| 429 | + hashes_out.reserve(entries.size()); |
| 430 | + for (const auto& entry : entries) { |
| 431 | + hashes_out.push_back(entry.hash); |
| 432 | + } |
| 433 | + return true; |
| 434 | +} |
0 commit comments