Progress #17: Lazy load genesis state
This speeds up startup for witness_node when starting on a database which is already initialized.
This commit is contained in:
parent
9c4ac2e064
commit
d64c9154a7
8 changed files with 86 additions and 75 deletions
|
|
@ -183,12 +183,14 @@ namespace detail {
|
|||
bool clean = !fc::exists(_data_dir / "blockchain/dblock");
|
||||
fc::create_directories(_data_dir / "blockchain/dblock");
|
||||
|
||||
genesis_state_type initial_state;
|
||||
if( _options->count("genesis-json") )
|
||||
initial_state = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
|
||||
.as<genesis_state_type>();
|
||||
else
|
||||
initial_state = create_example_genesis();
|
||||
auto initial_state = [&] {
|
||||
ilog("Initializing database...");
|
||||
if( _options->count("genesis-json") )
|
||||
return fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
|
||||
.as<genesis_state_type>();
|
||||
else
|
||||
return create_example_genesis();
|
||||
};
|
||||
|
||||
if( _options->count("resync-blockchain") )
|
||||
_chain_db->wipe(_data_dir / "blockchain", true);
|
||||
|
|
@ -196,12 +198,12 @@ namespace detail {
|
|||
if( _options->count("replay-blockchain") )
|
||||
{
|
||||
ilog("Replaying blockchain on user request.");
|
||||
_chain_db->reindex(_data_dir/"blockchain", initial_state);
|
||||
_chain_db->reindex(_data_dir/"blockchain", initial_state());
|
||||
} else if( clean )
|
||||
_chain_db->open(_data_dir / "blockchain", initial_state);
|
||||
else {
|
||||
wlog("Detected unclean shutdown. Replaying blockchain...");
|
||||
_chain_db->reindex(_data_dir / "blockchain", initial_state);
|
||||
_chain_db->reindex(_data_dir / "blockchain", initial_state());
|
||||
}
|
||||
|
||||
if( _options->count("apiaccess") )
|
||||
|
|
@ -226,13 +228,13 @@ namespace detail {
|
|||
reset_websocket_tls_server();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
optional< api_access_info > get_api_access_info( const string& username )const
|
||||
optional< api_access_info > get_api_access_info(const string& username)const
|
||||
{
|
||||
optional< api_access_info > result;
|
||||
auto it = _apiaccess.permission_map.find( username );
|
||||
auto it = _apiaccess.permission_map.find(username);
|
||||
if( it == _apiaccess.permission_map.end() )
|
||||
{
|
||||
it = _apiaccess.permission_map.find( "*" );
|
||||
it = _apiaccess.permission_map.find("*");
|
||||
if( it == _apiaccess.permission_map.end() )
|
||||
return result;
|
||||
}
|
||||
|
|
@ -242,7 +244,7 @@ namespace detail {
|
|||
/**
|
||||
* If delegate has the item, the network has no need to fetch it.
|
||||
*/
|
||||
virtual bool has_item( const net::item_id& id ) override
|
||||
virtual bool has_item(const net::item_id& id) override
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -262,7 +264,7 @@ namespace detail {
|
|||
*
|
||||
* @throws exception if error validating the item, otherwise the item is safe to broadcast on.
|
||||
*/
|
||||
virtual bool handle_block( const graphene::net::block_message& blk_msg, bool sync_mode ) override
|
||||
virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode) override
|
||||
{ try {
|
||||
ilog("Got block #${n} from network", ("n", blk_msg.block.block_num()));
|
||||
try {
|
||||
|
|
@ -273,7 +275,7 @@ namespace detail {
|
|||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) }
|
||||
|
||||
virtual bool handle_transaction( const graphene::net::trx_message& trx_msg, bool sync_mode ) override
|
||||
virtual bool handle_transaction(const graphene::net::trx_message& trx_msg, bool sync_mode) override
|
||||
{ try {
|
||||
ilog("Got transaction from network");
|
||||
_chain_db->push_transaction( trx_msg.trx );
|
||||
|
|
@ -328,18 +330,18 @@ namespace detail {
|
|||
/**
|
||||
* Given the hash of the requested data, fetch the body.
|
||||
*/
|
||||
virtual message get_item( const item_id& id ) override
|
||||
virtual message get_item(const item_id& id) override
|
||||
{ try {
|
||||
ilog("Request for item ${id}", ("id", id));
|
||||
if( id.item_type == graphene::net::block_message_type )
|
||||
{
|
||||
auto opt_block = _chain_db->fetch_block_by_id( id.item_hash );
|
||||
auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);
|
||||
if( !opt_block )
|
||||
elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}",
|
||||
("id", id.item_hash)("id2", _chain_db->get_block_id_for_num(block_header::num_from_id(id.item_hash))));
|
||||
FC_ASSERT( opt_block.valid() );
|
||||
ilog("Serving up block #${num}", ("num", opt_block->block_num()));
|
||||
return block_message( std::move(*opt_block) );
|
||||
return block_message(std::move(*opt_block));
|
||||
}
|
||||
return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );
|
||||
} FC_CAPTURE_AND_RETHROW( (id) ) }
|
||||
|
|
@ -364,18 +366,18 @@ namespace detail {
|
|||
* &c.
|
||||
* the last item in the list will be the hash of the most recent block on our preferred chain
|
||||
*/
|
||||
virtual std::vector<item_hash_t> get_blockchain_synopsis( uint32_t item_type,
|
||||
const graphene::net::item_hash_t& reference_point,
|
||||
uint32_t number_of_blocks_after_reference_point ) override
|
||||
virtual std::vector<item_hash_t> get_blockchain_synopsis(uint32_t item_type,
|
||||
const graphene::net::item_hash_t& reference_point,
|
||||
uint32_t number_of_blocks_after_reference_point) override
|
||||
{ try {
|
||||
std::vector<item_hash_t> result;
|
||||
result.reserve(30);
|
||||
auto head_block_num = _chain_db->head_block_num();
|
||||
result.push_back( _chain_db->head_block_id() );
|
||||
result.push_back(_chain_db->head_block_id());
|
||||
auto current = 1;
|
||||
while( current < head_block_num )
|
||||
{
|
||||
result.push_back( _chain_db->get_block_id_for_num( head_block_num - current ) );
|
||||
result.push_back(_chain_db->get_block_id_for_num(head_block_num - current));
|
||||
current = current*2;
|
||||
}
|
||||
std::reverse( result.begin(), result.end() );
|
||||
|
|
@ -390,7 +392,7 @@ namespace detail {
|
|||
* @param item_count the number of items known to the node that haven't been sent to handle_item() yet.
|
||||
* After `item_count` more calls to handle_item(), the node will be in sync
|
||||
*/
|
||||
virtual void sync_status( uint32_t item_type, uint32_t item_count ) override
|
||||
virtual void sync_status(uint32_t item_type, uint32_t item_count) override
|
||||
{
|
||||
// any status reports to GUI go here
|
||||
}
|
||||
|
|
@ -398,7 +400,7 @@ namespace detail {
|
|||
/**
|
||||
* Call any time the number of connected peers changes.
|
||||
*/
|
||||
virtual void connection_count_changed( uint32_t c ) override
|
||||
virtual void connection_count_changed(uint32_t c) override
|
||||
{
|
||||
// any status reports to GUI go here
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
p.parameters.current_fees.set_all_fees(0);
|
||||
|
||||
});
|
||||
create<dynamic_global_property_object>( [&](dynamic_global_property_object& p) {
|
||||
create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
|
||||
p.time = genesis_state.initial_timestamp;
|
||||
p.witness_budget = 0;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
database::database()
|
||||
|
|
@ -33,31 +35,10 @@ database::~database(){
|
|||
_pending_block_session->commit();
|
||||
}
|
||||
|
||||
void database::open( const fc::path& data_dir, const genesis_state_type& initial_allocation )
|
||||
{ try {
|
||||
object_database::open( data_dir );
|
||||
|
||||
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
|
||||
|
||||
if( !find(global_property_id_type()) )
|
||||
{
|
||||
// ilog( "Init Genesis State" );
|
||||
init_genesis(initial_allocation);
|
||||
}
|
||||
|
||||
_pending_block.previous = head_block_id();
|
||||
_pending_block.timestamp = head_block_time();
|
||||
|
||||
auto last_block= _block_id_to_block.last();
|
||||
if( last_block.valid() )
|
||||
_fork_db.start_block( *last_block );
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
|
||||
|
||||
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
|
||||
{ try {
|
||||
wipe(data_dir, false);
|
||||
open(data_dir, initial_allocation);
|
||||
open(data_dir, [&initial_allocation]{return initial_allocation;});
|
||||
|
||||
auto start = fc::time_point::now();
|
||||
auto last_block = _block_id_to_block.last();
|
||||
|
|
@ -69,13 +50,13 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
//_undo_db.disable();
|
||||
for( uint32_t i = 1; i <= last_block_num; ++i )
|
||||
{
|
||||
apply_block( *_block_id_to_block.fetch_by_number(i), skip_delegate_signature |
|
||||
apply_block(*_block_id_to_block.fetch_by_number(i), skip_delegate_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_undo_block |
|
||||
skip_undo_transaction |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_authority_check );
|
||||
skip_authority_check);
|
||||
}
|
||||
//_undo_db.enable();
|
||||
auto end = fc::time_point::now();
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class database;
|
|||
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
|
||||
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
|
||||
/// multiple keys in it.
|
||||
public_key_type memo_key;
|
||||
public_key_type memo_key;
|
||||
/// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake
|
||||
/// will be counted as voting for the referenced account's selected votes instead.
|
||||
account_id_type voting_account;
|
||||
|
|
@ -257,7 +257,7 @@ class database;
|
|||
|
||||
/**
|
||||
* @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account
|
||||
* is an potential signing authority.
|
||||
* is an potential signing authority.
|
||||
*/
|
||||
class account_member_index : public secondary_index
|
||||
{
|
||||
|
|
|
|||
|
|
@ -93,7 +93,19 @@ namespace graphene { namespace chain {
|
|||
skip_assert_evaluation = 0x400 ///< used while reindexing
|
||||
};
|
||||
|
||||
void open(const fc::path& data_dir, const genesis_state_type& initial_allocation = genesis_state_type());
|
||||
/**
|
||||
* @brief Open a database, creating a new one if necessary
|
||||
*
|
||||
* Opens a database in the specified directory. If no initialized database is found, genesis_loader is called
|
||||
* and its return value is used as the genesis state when initializing the new database
|
||||
*
|
||||
* genesis_loader will not be called if an existing database is found.
|
||||
*
|
||||
* @param data_dir Path to open or create database in
|
||||
* @param genesis_loader A callable object which returns the genesis state to initialize new databases on
|
||||
*/
|
||||
template<typename F>
|
||||
void open(const fc::path& data_dir, F&& genesis_loader);
|
||||
/**
|
||||
* @brief Rebuild object graph from block history and open detabase
|
||||
*
|
||||
|
|
@ -477,4 +489,24 @@ namespace graphene { namespace chain {
|
|||
(void)l;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void database::open(const fc::path& data_dir, F&& genesis_loader)
|
||||
{ try {
|
||||
object_database::open(data_dir);
|
||||
|
||||
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
|
||||
|
||||
if( !find(global_property_id_type()) )
|
||||
init_genesis(genesis_loader());
|
||||
|
||||
_pending_block.previous = head_block_id();
|
||||
_pending_block.timestamp = head_block_time();
|
||||
|
||||
auto last_block= _block_id_to_block.last();
|
||||
if( last_block.valid() )
|
||||
_fork_db.start_block( *last_block );
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ void database_fixture::open_database()
|
|||
{
|
||||
if( !data_dir ) {
|
||||
data_dir = fc::temp_directory();
|
||||
db.open(data_dir->path(), genesis_state);
|
||||
db.open(data_dir->path(), [this]{return genesis_state;});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis() );
|
||||
db.open(data_dir.path(), make_genesis );
|
||||
b = db.generate_block(now, db.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
|
||||
|
||||
for( uint32_t i = 1; i < 200; ++i )
|
||||
|
|
@ -137,25 +137,25 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
BOOST_CHECK( db.head_block_id() == b.id() );
|
||||
witness_id_type prev_witness = b.witness;
|
||||
now += db.block_interval();
|
||||
witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first;
|
||||
witness_id_type cur_witness = db.get_scheduled_witness(1).first;
|
||||
BOOST_CHECK( cur_witness != prev_witness );
|
||||
b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing );
|
||||
b = db.generate_block(now, cur_witness, delegate_priv_key, database::skip_nothing);
|
||||
BOOST_CHECK( b.witness == cur_witness );
|
||||
}
|
||||
db.close();
|
||||
}
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path() );
|
||||
db.open(data_dir.path(), []{return genesis_state_type();});
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), 200 );
|
||||
for( uint32_t i = 0; i < 200; ++i )
|
||||
{
|
||||
BOOST_CHECK( db.head_block_id() == b.id() );
|
||||
witness_id_type prev_witness = b.witness;
|
||||
now += db.block_interval();
|
||||
witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first;
|
||||
witness_id_type cur_witness = db.get_scheduled_witness(1).first;
|
||||
BOOST_CHECK( cur_witness != prev_witness );
|
||||
b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing );
|
||||
b = db.generate_block(now, cur_witness, delegate_priv_key, database::skip_nothing);
|
||||
}
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), 400 );
|
||||
}
|
||||
|
|
@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE( undo_block )
|
|||
fc::temp_directory data_dir;
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis() );
|
||||
db.open(data_dir.path(), make_genesis);
|
||||
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
|
||||
|
||||
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
|
|
@ -211,9 +211,9 @@ BOOST_AUTO_TEST_CASE( fork_blocks )
|
|||
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
|
||||
|
||||
database db1;
|
||||
db1.open(data_dir1.path(), make_genesis());
|
||||
db1.open(data_dir1.path(), make_genesis);
|
||||
database db2;
|
||||
db2.open(data_dir2.path(), make_genesis());
|
||||
db2.open(data_dir2.path(), make_genesis);
|
||||
|
||||
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
for( uint32_t i = 0; i < 10; ++i )
|
||||
|
|
@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE( undo_pending )
|
|||
fc::temp_directory data_dir;
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis());
|
||||
db.open(data_dir.path(), make_genesis);
|
||||
|
||||
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
public_key_type delegate_pub_key = delegate_priv_key.get_public_key();
|
||||
|
|
@ -334,8 +334,8 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
|
|||
dir2;
|
||||
database db1,
|
||||
db2;
|
||||
db1.open(dir1.path(), make_genesis());
|
||||
db2.open(dir2.path(), make_genesis());
|
||||
db1.open(dir1.path(), make_genesis);
|
||||
db2.open(dir2.path(), make_genesis);
|
||||
|
||||
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
|
||||
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
|
|
@ -392,8 +392,8 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions )
|
|||
dir2;
|
||||
database db1,
|
||||
db2;
|
||||
db1.open(dir1.path(), make_genesis());
|
||||
db2.open(dir2.path(), make_genesis());
|
||||
db1.open(dir1.path(), make_genesis);
|
||||
db2.open(dir2.path(), make_genesis);
|
||||
|
||||
auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check;
|
||||
|
||||
|
|
@ -441,8 +441,8 @@ BOOST_AUTO_TEST_CASE( tapos )
|
|||
dir2;
|
||||
database db1,
|
||||
db2;
|
||||
db1.open(dir1.path(), make_genesis());
|
||||
db2.open(dir2.path(), make_genesis());
|
||||
db1.open(dir1.path(), make_genesis);
|
||||
db2.open(dir2.path(), make_genesis);
|
||||
|
||||
const account_object& init1 = *db1.get_index_type<account_index>().indices().get<by_name>().find("init1");
|
||||
|
||||
|
|
@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE( tapos )
|
|||
const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type);
|
||||
|
||||
now += db1.block_interval();
|
||||
auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing );
|
||||
auto b = db1.generate_block(now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing);
|
||||
|
||||
signed_transaction trx;
|
||||
//This transaction must be in the next block after its reference, or it is invalid.
|
||||
|
|
@ -467,10 +467,6 @@ BOOST_AUTO_TEST_CASE( tapos )
|
|||
db1.push_transaction(trx);
|
||||
now += db1.block_interval();
|
||||
b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
|
||||
/*
|
||||
now += db1.block_interval();
|
||||
b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
|
||||
*/
|
||||
trx.clear();
|
||||
|
||||
trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)}));
|
||||
|
|
|
|||
|
|
@ -1001,7 +1001,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
|
|||
|
||||
genesis_state.initial_accounts.emplace_back("n", n_key.get_public_key());
|
||||
|
||||
db.open(td.path(), genesis_state);
|
||||
db.open(td.path(), [this]{return genesis_state;});
|
||||
const balance_object& balance = balance_id_type()(db);
|
||||
BOOST_CHECK_EQUAL(balance.balance.amount.value, 1);
|
||||
BOOST_CHECK_EQUAL(balance_id_type(1)(db).balance.amount.value, 1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue