Commit 357ecf30 authored by Bartek Wrona's avatar Bartek Wrona
Browse files

Branch 'bw_last_irr_block_fixes' rebased onto develop

parents d3a99692 e345d461
......@@ -147,6 +147,7 @@ void database::open( const open_args& args )
initialize_indexes();
initialize_evaluators();
initialize_irreversible_storage();
if( !find< dynamic_global_property_object >() )
with_write_lock( [&]()
......@@ -161,11 +162,26 @@ void database::open( const open_args& args )
_block_log.open( args.data_dir / "block_log" );
});
auto hb = head_block_num();
auto last_irreversible_block = get_last_irreversible_block_num();
FC_ASSERT(hb >= last_irreversible_block);
ilog("Opened a blockchain database holding a state specific to head block: ${hb} and last irreversible block: ${lb}", ("hb", hb)("lb", last_irreversible_block));
// Rewind all undo state. This should return us to the state at the last irreversible block.
with_write_lock( [&]()
{
undo_all();
auto new_hb = head_block_num();
FC_ASSERT(new_hb >= last_irreversible_block);
FC_ASSERT(this->get_last_irreversible_block_num() == last_irreversible_block, "Undo operation should not touch irreversible block value");
ilog("Blockchain state database is AT IRREVERSIBLE state specific to head block: ${hb} and LIB: ${lb}", ("hb", head_block_num())("lb", this->get_last_irreversible_block_num()));
if( args.chainbase_flags & chainbase::skip_env_check )
{
set_revision( head_block_num() );
......@@ -422,6 +438,11 @@ void database::close(bool rewind)
clear_pending();
chainbase::database::flush();
auto lib = this->get_last_irreversible_block_num();
ilog("Database flushed at last irreversible block: ${b}", ("b", lib));
chainbase::database::close();
_block_log.close();
......@@ -3564,9 +3585,21 @@ node_property_object& database::node_properties()
return _node_property_object;
}
uint32_t database::last_non_undoable_block_num() const
uint32_t database::get_last_irreversible_block_num() const
{
//ilog("getting last_irreversible_block_num irreversible is ${l}", ("l", irreversible_object->last_irreversible_block_num));
//ilog("getting last_irreversible_block_num head is ${l}", ("l", head_block_num()));
return irreversible_object->last_irreversible_block_num;
}
void database::set_last_irreversible_block_num(uint32_t block_num)
{
return get_dynamic_global_properties().last_irreversible_block_num;
//ilog("setting last_irreversible_block_num previous ${l}", ("l", irreversible_object->last_irreversible_block_num));
FC_ASSERT(block_num >= irreversible_object->last_irreversible_block_num, "Irreversible block can only move forward. Old: ${o}, new: ${n}",
("o", irreversible_object->last_irreversible_block_num)("n", block_num));
irreversible_object->last_irreversible_block_num = block_num;
//ilog("setting last_irreversible_block_num new ${l}", ("l", irreversible_object->last_irreversible_block_num));
}
void database::initialize_evaluators()
......@@ -3668,6 +3701,13 @@ void database::initialize_indexes()
_plugin_index_signal();
}
void database::initialize_irreversible_storage()
{
auto s = get_segment_manager();
irreversible_object = s->find_or_construct<irreversible_object_type>( "irreversible" )();
}
void database::resetState(const open_args& args)
{
wipe(args.data_dir, args.shared_mem_dir, false);
......@@ -4563,7 +4603,7 @@ void database::process_optional_actions( const optional_automated_actions& actio
// and it is safe to delete.
const auto& pending_action_idx = get_index< pending_optional_action_index, by_execution >();
auto pending_itr = pending_action_idx.begin();
auto lib = fetch_block_by_number( get_dynamic_global_properties().last_irreversible_block_num );
auto lib = fetch_block_by_number( get_last_irreversible_block_num() );
// This is always valid when running on mainnet because there are irreversible blocks
// Testnet and unit tests, not so much. Could be ifdeffed with IS_TEST_NET, but seems
......@@ -4865,10 +4905,10 @@ FC_TODO( "#ifndef not needed after HF 20 is live" );
if( !(get_node_properties().skip_flags & skip_undo_history_check) )
{
HIVE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < HIVE_MAX_UNDO_HISTORY, undo_database_exception,
HIVE_ASSERT( _dgp.head_block_number - get_last_irreversible_block_num() < HIVE_MAX_UNDO_HISTORY, undo_database_exception,
"The database does not have enough undo history to support a blockchain with so many missed blocks. "
"Please add a checkpoint if you would like to continue applying blocks beyond this point.",
("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number)
("last_irreversible_block_num",get_last_irreversible_block_num())("head", _dgp.head_block_number)
("max_undo",HIVE_MAX_UNDO_HISTORY) );
}
} FC_CAPTURE_AND_RETHROW() }
......@@ -4934,8 +4974,7 @@ void database::update_signing_witness(const witness_object& signing_witness, con
uint32_t database::update_last_irreversible_block()
{ try {
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
uint32_t old_last_irreversible = dpo.last_irreversible_block_num;
uint32_t old_last_irreversible = get_last_irreversible_block_num();
/**
* Prior to voting taking over, we must be more conservative...
......@@ -4943,11 +4982,8 @@ uint32_t database::update_last_irreversible_block()
*/
if( head_block_num() < HIVE_START_MINER_VOTING_BLOCK )
{
modify( dpo, [&]( dynamic_global_property_object& _dpo )
{
if ( head_block_num() > HIVE_MAX_WITNESSES )
_dpo.last_irreversible_block_num = head_block_num() - HIVE_MAX_WITNESSES;
} );
if ( head_block_num() > HIVE_MAX_WITNESSES )
set_last_irreversible_block_num(head_block_num() - HIVE_MAX_WITNESSES);
}
else
{
......@@ -4974,12 +5010,10 @@ uint32_t database::update_last_irreversible_block()
uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num;
if( new_last_irreversible_block_num > dpo.last_irreversible_block_num )
if( new_last_irreversible_block_num > get_last_irreversible_block_num() )
{
modify( dpo, [&]( dynamic_global_property_object& _dpo )
{
_dpo.last_irreversible_block_num = new_last_irreversible_block_num;
} );
//ilog("Last irreversible block changed to ${b}. Got from witness: ${w}", ("b", new_last_irreversible_block_num)("w", wit_objs[offset]->owner));
set_last_irreversible_block_num(new_last_irreversible_block_num);
}
}
return old_last_irreversible;
......@@ -5010,11 +5044,11 @@ void database::migrate_irreversible_state(uint32_t old_last_irreversible)
if( tmp_head )
log_head_num = tmp_head->block_num();
if( log_head_num < dpo.last_irreversible_block_num )
if( log_head_num < get_last_irreversible_block_num() )
{
// Check for all blocks that we want to write out to the block log but don't write any
// unless we are certain they all exist in the fork db
while( log_head_num < dpo.last_irreversible_block_num )
while( log_head_num < get_last_irreversible_block_num() )
{
item_ptr block_ptr = _fork_db.fetch_block_on_main_branch_by_number( log_head_num+1 );
FC_ASSERT( block_ptr, "Current fork in the fork database does not contain the last_irreversible_block" );
......@@ -5032,15 +5066,22 @@ void database::migrate_irreversible_state(uint32_t old_last_irreversible)
}
// This deletes blocks from the fork db
_fork_db.set_max_size( dpo.head_block_number - dpo.last_irreversible_block_num + 1 );
_fork_db.set_max_size( dpo.head_block_number - get_last_irreversible_block_num() + 1 );
// This deletes undo state
commit( dpo.last_irreversible_block_num );
commit( get_last_irreversible_block_num() );
for( uint32_t i = old_last_irreversible + 1; i <= dpo.last_irreversible_block_num; ++i )
if(old_last_irreversible < get_last_irreversible_block_num() )
{
notify_irreversible_block( i );
//ilog("Updating last irreversible block to: ${b}. Old last irreversible was: ${ob}.",
// ("b", get_last_irreversible_block_num())("ob", old_last_irreversible));
for( uint32_t i = old_last_irreversible + 1; i <= get_last_irreversible_block_num(); ++i )
{
notify_irreversible_block( i );
}
}
}
FC_CAPTURE_AND_RETHROW()
}
......
......@@ -2427,7 +2427,7 @@ void pow2_evaluator::do_apply( const pow2_operation& o )
const auto& work = o.work.get< equihash_pow >();
FC_ASSERT( work.prev_block == db.head_block_id(), "Equihash pow op not for last block" );
auto recent_block_num = protocol::block_header::num_from_id( work.input.prev_block );
FC_ASSERT( recent_block_num > dgp.last_irreversible_block_num,
FC_ASSERT( recent_block_num > db.get_last_irreversible_block_num(),
"Equihash pow done for block older than last irreversible block num" );
FC_ASSERT( work.pow_summary < target_pow, "Insufficient work difficulty. Work: ${w}, Target: ${t}", ("w",work.pow_summary)("t", target_pow) );
worker_account = work.input.worker_account;
......
......@@ -547,7 +547,12 @@ namespace chain {
node_property_object& node_properties();
uint32_t last_non_undoable_block_num() const;
uint32_t get_last_irreversible_block_num()const;
void set_last_irreversible_block_num(uint32_t block_num);
struct irreversible_object_type
{
uint32_t last_irreversible_block_num = 0;
} *irreversible_object = nullptr;
//////////////////// db_init.cpp ////////////////////
void initialize_evaluators();
......@@ -557,6 +562,9 @@ namespace chain {
/// Reset the object graph in-memory
void initialize_indexes();
// Reset irreversible state (unaffected by undo)
void initialize_irreversible_storage();
void resetState(const open_args& args);
void init_schema();
......
......@@ -137,8 +137,6 @@ namespace hive { namespace chain {
fc::uint128_t recent_slots_filled = fc::uint128::max_value();
uint8_t participation_count = 128; ///< Divide by 128 to compute participation percentage
uint32_t last_irreversible_block_num = 0;
/**
* The number of votes regenerated per day. Any user voting slower than this rate will be
* "wasting" voting power through spillover; any user voting faster than this rate will have
......@@ -222,7 +220,6 @@ FC_REFLECT( hive::chain::dynamic_global_property_object,
(current_aslot)
(recent_slots_filled)
(participation_count)
(last_irreversible_block_num)
(vote_power_reserve_rate)
(delegation_return_period)
(reverse_auction_seconds)
......
......@@ -361,6 +361,23 @@ namespace chainbase {
return idx.erase(objI);
}
template< typename ByIndex, typename ExternalStorageProcessor, typename Iterator = typename MultiIndexType::template index_iterator<ByIndex>::type >
void move_to_external_storage(Iterator begin, Iterator end, ExternalStorageProcessor&& processor)
{
auto& idx = _indices.template get< ByIndex >();
for(auto objectI = begin; objectI != end;)
{
processor(*objectI);
auto nextI = objectI;
++nextI;
auto successor = idx.erase(objectI);
FC_ASSERT(successor == nextI);
objectI = successor;
}
}
template<typename CompatibleKey>
const value_type* find( CompatibleKey&& key )const {
auto itr = _indices.find( std::forward<CompatibleKey>( key ) );
......
......@@ -48,7 +48,7 @@ void block_info_plugin::on_applied_block( const chain::signed_block& b )
info.block_id = b.id();
info.block_size = fc::raw::pack_size( b );
info.aslot = dgpo.current_aslot;
info.last_irreversible_block_num = dgpo.last_irreversible_block_num;
info.last_irreversible_block_num = db.get_last_irreversible_block_num();
info.num_pow_witnesses = dgpo.num_pow_witnesses;
return;
}
......
......@@ -88,7 +88,8 @@ void delayed_node_plugin::sync_with_trusted_node()
uint32_t pass_count = 0;
while( true )
{
hive::chain::dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties();
api_dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties();
//hive::chain::dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties();
if( remote_dpo.last_irreversible_block_num <= db.head_block_num() )
{
if( remote_dpo.last_irreversible_block_num < db.head_block_num() )
......
......@@ -1857,7 +1857,8 @@ void account_history_rocksdb_plugin::impl::on_post_reindex(const hive::chain::re
flushStorage();
_collectedOpsWriteLimit = 1;
_reindexing = false;
update_lib( note.last_block_number ); // We always reindex irreversible blocks.
uint32_t last_irreversible_block_num =_mainDb.get_last_irreversible_block_num();
update_lib( last_irreversible_block_num ); // Set same value as in main database, as result of witness participation
printReport( note.last_block_number, "RocksDB data reindex finished." );
}
......@@ -2032,14 +2033,16 @@ void account_history_rocksdb_plugin::impl::on_irreversible_block( uint32_t block
/// In case of genesis block (and fresh testnet) there can be no LIB at begin.
if( block_num <= get_lib(&fallbackIrreversibleBlock) ) return;
auto stored_lib = get_lib(&fallbackIrreversibleBlock);
FC_ASSERT(block_num > stored_lib, "New irreversible block: ${nb} can't be less than already stored one: ${ob}", ("nb", block_num)("ob", stored_lib));
_currently_persisted_irreversible_block.store(block_num);
auto& volatileOpsGenericIndex = _mainDb.get_mutable_index<volatile_operation_index>();
const auto& volatile_idx = _mainDb.get_index< volatile_operation_index, by_block >();
auto itr = volatile_idx.begin();
vector< const volatile_operation_object* > to_delete;
_currently_persisted_irreversible_block.store(block_num);
{
std::lock_guard<std::mutex> lk(_currently_persisted_irreversible_mtx);
......@@ -2067,7 +2070,6 @@ void account_history_rocksdb_plugin::impl::on_irreversible_block( uint32_t block
while(itr != volatile_idx.end() && itr->block == this_itr_block)
{
rocksdb_operation_object obj(*itr);
to_delete.push_back(&(*itr));
// check that operation is already stored as irreversible as it will be not imported
FC_ASSERT(ops.count(obj), "operation in block ${block} was not imported until irreversible block ${irreversible}", ("block", this_itr_block)("irreversible", block_num));
hive::protocol::operation op = fc::raw::unpack_from_buffer< hive::protocol::operation >( obj.serialized_op );
......@@ -2078,18 +2080,18 @@ void account_history_rocksdb_plugin::impl::on_irreversible_block( uint32_t block
}
}
while(itr != volatile_idx.end() && itr->block == block_num)
{
rocksdb_operation_object obj(*itr);
importOperation(obj, itr->impacted);
to_delete.push_back(&(*itr));
++itr;
}
/// Range of reversible (volatile) ops to be processed should come from blocks (stored_lib, block_num]
auto moveRangeBeginI = volatile_idx.upper_bound(stored_lib);
for(const volatile_operation_object* o : to_delete)
{
_mainDb.remove(*o);
}
FC_ASSERT(moveRangeBeginI == volatile_idx.begin() || moveRangeBeginI == volatile_idx.end(), "All volatile ops processed by previous irreversible blocks should be already flushed");
auto moveRangeEndI = volatile_idx.upper_bound(block_num);
volatileOpsGenericIndex.move_to_external_storage<by_block>(moveRangeBeginI, moveRangeEndI, [this](const volatile_operation_object& operation) -> void
{
rocksdb_operation_object obj(operation);
importOperation(obj, operation.impacted);
}
);
update_lib(block_num);
}
......
......@@ -236,7 +236,7 @@ struct api_dynamic_global_property_object
current_aslot( o.current_aslot ),
recent_slots_filled( o.recent_slots_filled ),
participation_count( o.participation_count ),
last_irreversible_block_num( o.last_irreversible_block_num ),
last_irreversible_block_num( db.get_last_irreversible_block_num()),
vote_power_reserve_rate( o.vote_power_reserve_rate ),
delegation_return_period( o.delegation_return_period ),
reverse_auction_seconds( o.reverse_auction_seconds ),
......
......@@ -27,7 +27,7 @@ DEFINE_API_IMPL( transaction_status_api_impl, find_transaction )
// Have we begun tracking?
if ( _db.head_block_num() >= earliest_tracked_block_num )
{
auto last_irreversible_block_num = _db.get_dynamic_global_properties().last_irreversible_block_num;
auto last_irreversible_block_num = _db.get_last_irreversible_block_num();
auto tso = _db.find< transaction_status::transaction_status_object, transaction_status::by_trx_id >( args.transaction_id );
// If we are actively tracking this transaction
......
......@@ -75,15 +75,13 @@ void block_log_info_plugin_impl::on_post_apply_block( const block_notification&
}
}
const dynamic_global_property_object& dgpo = _db.get_dynamic_global_properties();
const auto& idx = _db.get_index< block_log_pending_message_index, by_id >();
while( true )
{
auto it = idx.begin();
if( it == idx.end() )
break;
if( it->data.block_num > dgpo.last_irreversible_block_num )
if( it->data.block_num > _db.get_last_irreversible_block_num())
break;
print_message( it->data );
_db.remove( *it );
......
......@@ -584,7 +584,7 @@ void chain_plugin_impl::process_snapshot()
void chain_plugin_impl::work( synchronization_type& on_sync )
{
ilog( "Started on blockchain with ${n} blocks", ("n", db.head_block_num()) );
ilog( "Started on blockchain with ${n} blocks, LIB: ${lb}", ("n", db.head_block_num())("lb", db.get_last_irreversible_block_num()) );
on_sync();
......
......@@ -342,7 +342,7 @@ std::vector< graphene::net::item_hash_t > p2p_plugin_impl::get_blockchain_synops
synopsis.reserve(30);
uint32_t high_block_num;
uint32_t non_fork_high_block_num;
uint32_t low_block_num = chain.db().last_non_undoable_block_num();
uint32_t low_block_num = chain.db().get_last_irreversible_block_num();
std::vector<block_id_type> fork_history;
if (reference_point != item_hash_t())
......
......@@ -915,7 +915,7 @@ class state_snapshot_plugin::impl final : protected chain::state_snapshot_provid
void store_snapshot_manifest(const bfs::path& actualStoragePath, const std::vector<std::unique_ptr<index_dump_writer>>& builtWriters,
const snapshot_dump_supplement_helper& dumpHelper) const;
std::pair<snapshot_manifest, plugin_external_data_index> load_snapshot_manifest(const bfs::path& actualStoragePath);
std::tuple<snapshot_manifest, plugin_external_data_index, uint32_t> load_snapshot_manifest(const bfs::path& actualStoragePath);
void load_snapshot_external_data(const plugin_external_data_index& idx);
private:
......@@ -991,6 +991,7 @@ void state_snapshot_plugin::impl::store_snapshot_manifest(const bfs::path& actua
rocksdb_cleanup_helper db = rocksdb_cleanup_helper::open(dbOptions, manifestDbPath);
::rocksdb::ColumnFamilyHandle* manifestCF = db.create_column_family("INDEX_MANIFEST");
::rocksdb::ColumnFamilyHandle* externalDataCF = db.create_column_family("EXTERNAL_DATA");
::rocksdb::ColumnFamilyHandle* snapshotManifestCF = db.create_column_family("IRREVERSIBLE_STATE");
::rocksdb::WriteOptions writeOptions;
......@@ -1039,14 +1040,28 @@ void state_snapshot_plugin::impl::store_snapshot_manifest(const bfs::path& actua
throw std::exception();
}
}
{
Slice key("LAST_IRREVERSIBLE_BLOCK");
uint32_t lib = _mainDb.get_last_irreversible_block_num();
Slice value(reinterpret_cast<const char*>(&lib), sizeof(uint32_t));
auto status = db->Put(writeOptions, snapshotManifestCF, key, value);
if(status.ok() == false)
{
elog("Cannot write an index manifest entry to output file: `${p}'. Error details: `${e}'.", ("p", manifestDbPath.string())("e", status.ToString()));
ilog("Failing key value: \"LAST_IRREVERSIBLE_BLOCK\"");
throw std::exception();
}
}
db.close();
}
std::pair<snapshot_manifest, plugin_external_data_index> state_snapshot_plugin::impl::load_snapshot_manifest(const bfs::path& actualStoragePath)
{
std::tuple<snapshot_manifest, plugin_external_data_index, uint32_t> state_snapshot_plugin::impl::load_snapshot_manifest(const bfs::path& actualStoragePath)
{
bfs::path manifestDbPath(actualStoragePath);
manifestDbPath /= "snapshot-manifest";
......@@ -1065,6 +1080,10 @@ std::pair<snapshot_manifest, plugin_external_data_index> state_snapshot_plugin::
cfDescriptor.name = "EXTERNAL_DATA";
cfDescriptors.push_back(cfDescriptor);
cfDescriptor = ::rocksdb::ColumnFamilyDescriptor();
cfDescriptor.name = "IRREVERSIBLE_STATE";
cfDescriptors.push_back(cfDescriptor);
std::vector<::rocksdb::ColumnFamilyHandle*> cfHandles;
std::unique_ptr<::rocksdb::DB> manifestDbPtr;
::rocksdb::DB* manifestDb = nullptr;
......@@ -1085,29 +1104,29 @@ std::pair<snapshot_manifest, plugin_external_data_index> state_snapshot_plugin::
snapshot_manifest retVal;
{
::rocksdb::ReadOptions rOptions;
::rocksdb::ReadOptions rOptions;
std::unique_ptr<::rocksdb::Iterator> indexIterator(manifestDb->NewIterator(rOptions, cfHandles[1]));
std::unique_ptr<::rocksdb::Iterator> indexIterator(manifestDb->NewIterator(rOptions, cfHandles[1]));
std::vector<char> buffer;
for(indexIterator->SeekToFirst(); indexIterator->Valid(); indexIterator->Next())
std::vector<char> buffer;
for(indexIterator->SeekToFirst(); indexIterator->Valid(); indexIterator->Next())
{
auto keySlice = indexIterator->key();
auto valueSlice = indexIterator->value();
auto keySlice = indexIterator->key();
auto valueSlice = indexIterator->value();
buffer.insert(buffer.end(), valueSlice.data(), valueSlice.data() + valueSlice.size());
buffer.insert(buffer.end(), valueSlice.data(), valueSlice.data() + valueSlice.size());
index_manifest_info info;
index_manifest_info info;
chainbase::serialization::unpack_from_buffer(info, buffer);
chainbase::serialization::unpack_from_buffer(info, buffer);
FC_ASSERT(keySlice.ToString() == info.name);
FC_ASSERT(keySlice.ToString() == info.name);
ilog("Loaded manifest info for index ${i} having storage files: ${sf}", ("i", info.name)("sf", info.storage_files));
ilog("Loaded manifest info for index ${i} having storage files: ${sf}", ("i", info.name)("sf", info.storage_files));
retVal.emplace(std::move(info));
retVal.emplace(std::move(info));
buffer.clear();
buffer.clear();
}
}
......@@ -1149,20 +1168,41 @@ std::pair<snapshot_manifest, plugin_external_data_index> state_snapshot_plugin::
}
}
uint32_t lib = 0;
{
::rocksdb::ReadOptions rOptions;
std::unique_ptr<::rocksdb::Iterator> irreversibleStateIterator(manifestDb->NewIterator(rOptions, cfHandles[3]));
irreversibleStateIterator->SeekToFirst();
FC_ASSERT(irreversibleStateIterator->Valid(), "No entry for IRREVERSIBLE_STATE. Probably used old snapshot format (must be regenerated).");
std::vector<char> buffer;
auto valueSlice = irreversibleStateIterator->value();
buffer.insert(buffer.end(), valueSlice.data(), valueSlice.data() + valueSlice.size());
chainbase::serialization::unpack_from_buffer(lib, buffer);
buffer.clear();
//ilog("lib: ${s}", ("s", lib));
irreversibleStateIterator->Next();
FC_ASSERT(irreversibleStateIterator->Valid() == false, "Multiple entries specifying irreversible block ?");
}
for(auto* cfh : cfHandles)
{
{
status = manifestDb->DestroyColumnFamilyHandle(cfh);
if(status.ok() == false)
{
{
elog("Cannot destroy column family handle...'. Error details: `${e}'.", ("e", status.ToString()));
}
}
}
manifestDb->Close();
manifestDbPtr.release();
return std::make_pair(retVal, extDataIdx);
}
return std::make_tuple(retVal, extDataIdx, lib);
}
void state_snapshot_plugin::impl::load_snapshot_external_data(const plugin_external_data_index& idx)
{
......@@ -1301,7 +1341,7 @@ void state_snapshot_plugin::impl::load_snapshot(const std::string& snapshotName,
for(chainbase::abstract_index* idx : indices)
{
builtReaders.emplace_back(std::make_unique<index_dump_reader>(snapshotManifest.first, actualStoragePath));
builtReaders.emplace_back(std::make_unique<index_dump_reader>(std::get<0>(snapshotManifest), actualStoragePath));
index_dump_reader* reader = builtReaders.back().get();
if(_allow_concurrency)
......@@ -1317,18 +1357,23 @@ void state_snapshot_plugin::impl::load_snapshot(const std::string& snapshotName,
threadpool.join_all();
if(snapshotManifest.second.empty())
plugin_external_data_index& extDataIdx = std::get<1>(snapshotManifest);
if(extDataIdx.empty())
{
ilog("Skipping external data load due to lack of data saved to the snapshot");
}
else
{
load_snapshot_external_data(snapshotManifest.second);
load_snapshot_external_data(extDataIdx);
}
auto last_irr_block = std::get<2>(snapshotManifest);
// set irreversible block number after database::resetState
_mainDb.set_last_irreversible_block_num(last_irr_block);
auto blockNo = _mainDb.head_block_num();
ilog("Setting chainbase revision to ${b} block...", ("b", blockNo));
ilo