Merge branch 'develop' into feature/GRPH-93-v2
This commit is contained in:
commit
94157497bb
18 changed files with 365 additions and 224 deletions
|
|
@ -300,7 +300,6 @@ namespace detail {
|
|||
|
||||
~application_impl()
|
||||
{
|
||||
fc::remove_all(_data_dir / "blockchain/dblock");
|
||||
}
|
||||
|
||||
void set_dbg_init_key( genesis_state_type& genesis, const std::string& init_key )
|
||||
|
|
@ -314,8 +313,7 @@ namespace detail {
|
|||
|
||||
void startup()
|
||||
{ try {
|
||||
bool clean = !fc::exists(_data_dir / "blockchain/dblock");
|
||||
fc::create_directories(_data_dir / "blockchain/dblock");
|
||||
fc::create_directories(_data_dir / "blockchain");
|
||||
|
||||
auto initial_state = [&] {
|
||||
ilog("Initializing database...");
|
||||
|
|
@ -381,64 +379,17 @@ namespace detail {
|
|||
bool replay = false;
|
||||
std::string replay_reason = "reason not provided";
|
||||
|
||||
// never replay if data dir is empty
|
||||
if( fc::exists( _data_dir ) && fc::directory_iterator( _data_dir ) != fc::directory_iterator() )
|
||||
{
|
||||
if( _options->count("replay-blockchain") )
|
||||
{
|
||||
replay = true;
|
||||
replay_reason = "replay-blockchain argument specified";
|
||||
}
|
||||
else if( !clean )
|
||||
{
|
||||
replay = true;
|
||||
replay_reason = "unclean shutdown detected";
|
||||
}
|
||||
else if( !fc::exists( _data_dir / "db_version" ) )
|
||||
{
|
||||
replay = true;
|
||||
replay_reason = "db_version file not found";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string version_string;
|
||||
fc::read_file_contents( _data_dir / "db_version", version_string );
|
||||
if( _options->count("replay-blockchain") )
|
||||
_chain_db->wipe( _data_dir / "blockchain", false );
|
||||
|
||||
if( version_string != GRAPHENE_CURRENT_DB_VERSION )
|
||||
{
|
||||
replay = true;
|
||||
replay_reason = "db_version file content mismatch";
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
_chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION );
|
||||
}
|
||||
|
||||
if( !replay )
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
try
|
||||
{
|
||||
_chain_db->open( _data_dir / "blockchain", initial_state );
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
ilog( "Caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
|
||||
replay = true;
|
||||
replay_reason = "exception in open()";
|
||||
}
|
||||
}
|
||||
|
||||
if( replay )
|
||||
{
|
||||
ilog( "Replaying blockchain due to: ${reason}", ("reason", replay_reason) );
|
||||
|
||||
fc::remove_all( _data_dir / "db_version" );
|
||||
_chain_db->reindex( _data_dir / "blockchain", initial_state() );
|
||||
|
||||
const auto mode = std::ios::out | std::ios::binary | std::ios::trunc;
|
||||
std::ofstream db_version( (_data_dir / "db_version").generic_string().c_str(), mode );
|
||||
std::string version_string = GRAPHENE_CURRENT_DB_VERSION;
|
||||
db_version.write( version_string.c_str(), version_string.size() );
|
||||
db_version.close();
|
||||
elog( "Caught exception ${e} in open(), you might want to force a replay", ("e", e.to_detail_string()) );
|
||||
throw;
|
||||
}
|
||||
|
||||
if( _options->count("force-validate") )
|
||||
|
|
|
|||
|
|
@ -45,14 +45,15 @@ void block_database::open( const fc::path& dbdir )
|
|||
_block_num_to_pos.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
_blocks.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
|
||||
if( !fc::exists( dbdir/"index" ) )
|
||||
_index_filename = dbdir / "index";
|
||||
if( !fc::exists( _index_filename ) )
|
||||
{
|
||||
_block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
|
||||
_block_num_to_pos.open( _index_filename.generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
|
||||
_blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
_block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
|
||||
_block_num_to_pos.open( _index_filename.generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
|
||||
_blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (dbdir) ) }
|
||||
|
|
@ -121,7 +122,7 @@ bool block_database::contains( const block_id_type& id )const
|
|||
index_entry e;
|
||||
auto index_pos = sizeof(e)*block_header::num_from_id(id);
|
||||
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
||||
if ( _block_num_to_pos.tellg() <= index_pos )
|
||||
if ( _block_num_to_pos.tellg() < index_pos + sizeof(e) )
|
||||
return false;
|
||||
_block_num_to_pos.seekg( index_pos );
|
||||
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
||||
|
|
@ -206,34 +207,47 @@ optional<signed_block> block_database::fetch_by_number( uint32_t block_num )cons
|
|||
return optional<signed_block>();
|
||||
}
|
||||
|
||||
optional<signed_block> block_database::last()const
|
||||
{
|
||||
optional<index_entry> block_database::last_index_entry()const {
|
||||
try
|
||||
{
|
||||
index_entry e;
|
||||
|
||||
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
||||
std::streampos pos = _block_num_to_pos.tellg();
|
||||
if( pos < sizeof(index_entry) )
|
||||
return optional<index_entry>();
|
||||
|
||||
if( _block_num_to_pos.tellp() < sizeof(index_entry) )
|
||||
return optional<signed_block>();
|
||||
pos -= pos % sizeof(index_entry);
|
||||
|
||||
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
|
||||
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
||||
uint64_t pos = _block_num_to_pos.tellg();
|
||||
while( e.block_size == 0 && pos > 0 )
|
||||
_blocks.seekg( 0, _block_num_to_pos.end );
|
||||
const std::streampos blocks_size = _blocks.tellg();
|
||||
while( pos > 0 )
|
||||
{
|
||||
pos -= sizeof(index_entry);
|
||||
_block_num_to_pos.seekg( pos );
|
||||
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
||||
if( _block_num_to_pos.gcount() == sizeof(e) && e.block_size > 0
|
||||
&& e.block_pos + e.block_size <= blocks_size )
|
||||
try
|
||||
{
|
||||
vector<char> data( e.block_size );
|
||||
_blocks.seekg( e.block_pos );
|
||||
_blocks.read( data.data(), e.block_size );
|
||||
if( _blocks.gcount() == e.block_size )
|
||||
{
|
||||
const signed_block block = fc::raw::unpack<signed_block>(data);
|
||||
if( block.id() == e.block_id )
|
||||
return e;
|
||||
}
|
||||
}
|
||||
catch (const fc::exception&)
|
||||
{
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
}
|
||||
fc::resize_file( _index_filename, pos );
|
||||
}
|
||||
|
||||
if( e.block_size == 0 )
|
||||
return optional<signed_block>();
|
||||
|
||||
vector<char> data( e.block_size );
|
||||
_blocks.seekg( e.block_pos );
|
||||
_blocks.read( data.data(), e.block_size );
|
||||
auto result = fc::raw::unpack<signed_block>(data);
|
||||
return result;
|
||||
}
|
||||
catch (const fc::exception&)
|
||||
{
|
||||
|
|
@ -241,42 +255,21 @@ optional<signed_block> block_database::last()const
|
|||
catch (const std::exception&)
|
||||
{
|
||||
}
|
||||
return optional<index_entry>();
|
||||
}
|
||||
|
||||
optional<signed_block> block_database::last()const
|
||||
{
|
||||
optional<index_entry> entry = last_index_entry();
|
||||
if( entry.valid() ) return fetch_by_number( block_header::num_from_id(entry->block_id) );
|
||||
return optional<signed_block>();
|
||||
}
|
||||
|
||||
optional<block_id_type> block_database::last_id()const
|
||||
{
|
||||
try
|
||||
{
|
||||
index_entry e;
|
||||
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
||||
|
||||
if( _block_num_to_pos.tellp() < sizeof(index_entry) )
|
||||
return optional<block_id_type>();
|
||||
|
||||
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
|
||||
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
||||
uint64_t pos = _block_num_to_pos.tellg();
|
||||
while( e.block_size == 0 && pos > 0 )
|
||||
{
|
||||
pos -= sizeof(index_entry);
|
||||
_block_num_to_pos.seekg( pos );
|
||||
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
||||
}
|
||||
|
||||
if( e.block_size == 0 )
|
||||
return optional<block_id_type>();
|
||||
|
||||
return e.block_id;
|
||||
}
|
||||
catch (const fc::exception&)
|
||||
{
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
}
|
||||
optional<index_entry> entry = last_index_entry();
|
||||
if( entry.valid() ) return entry->block_id;
|
||||
return optional<block_id_type>();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -215,12 +215,15 @@ bool database::_push_block(const signed_block& new_block)
|
|||
|
||||
// pop blocks until we hit the forked block
|
||||
while( head_block_id() != branches.second.back()->data.previous )
|
||||
{
|
||||
ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) );
|
||||
pop_block();
|
||||
}
|
||||
|
||||
// push all blocks on the new fork
|
||||
for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr )
|
||||
{
|
||||
ilog( "pushing blocks from fork ${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->data.id()) );
|
||||
ilog( "pushing block from fork #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) );
|
||||
optional<fc::exception> except;
|
||||
try {
|
||||
undo_database::session session = _undo_db.start_undo_session();
|
||||
|
|
@ -235,21 +238,27 @@ bool database::_push_block(const signed_block& new_block)
|
|||
// remove the rest of branches.first from the fork_db, those blocks are invalid
|
||||
while( ritr != branches.first.rend() )
|
||||
{
|
||||
_fork_db.remove( (*ritr)->data.id() );
|
||||
ilog( "removing block from fork_db #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) );
|
||||
_fork_db.remove( (*ritr)->id );
|
||||
++ritr;
|
||||
}
|
||||
_fork_db.set_head( branches.second.front() );
|
||||
|
||||
// pop all blocks from the bad fork
|
||||
while( head_block_id() != branches.second.back()->data.previous )
|
||||
pop_block();
|
||||
|
||||
// restore all blocks from the good fork
|
||||
for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr )
|
||||
{
|
||||
ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) );
|
||||
pop_block();
|
||||
}
|
||||
|
||||
ilog( "Switching back to fork: ${id}", ("id",branches.second.front()->data.id()) );
|
||||
// restore all blocks from the good fork
|
||||
for( auto ritr2 = branches.second.rbegin(); ritr2 != branches.second.rend(); ++ritr2 )
|
||||
{
|
||||
ilog( "pushing block #${n} ${id}", ("n",(*ritr2)->data.block_num())("id",(*ritr2)->id) );
|
||||
auto session = _undo_db.start_undo_session();
|
||||
apply_block( (*ritr)->data, skip );
|
||||
_block_id_to_block.store( new_block.id(), (*ritr)->data );
|
||||
apply_block( (*ritr2)->data, skip );
|
||||
_block_id_to_block.store( (*ritr2)->id, (*ritr2)->data );
|
||||
session.commit();
|
||||
}
|
||||
throw *except;
|
||||
|
|
@ -506,7 +515,6 @@ void database::pop_block()
|
|||
GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
|
||||
|
||||
_fork_db.pop_block();
|
||||
_block_id_to_block.remove( head_id );
|
||||
pop_undo();
|
||||
|
||||
_popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() );
|
||||
|
|
|
|||
|
|
@ -47,33 +47,86 @@ database::~database()
|
|||
clear_pending();
|
||||
}
|
||||
|
||||
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
|
||||
{ try {
|
||||
ilog( "reindexing blockchain" );
|
||||
wipe(data_dir, false);
|
||||
open(data_dir, [&initial_allocation]{return initial_allocation;});
|
||||
// Right now, we leave undo_db enabled when replaying when the bookie plugin is
|
||||
// enabled. It depends on new/changed/removed object notifications, and those are
|
||||
// only fired when the undo_db is enabled.
|
||||
// So we use this helper object to disable undo_db only if it is not forbidden
|
||||
// with _slow_replays flag.
|
||||
class auto_undo_enabler
|
||||
{
|
||||
const bool _slow_replays;
|
||||
undo_database& _undo_db;
|
||||
bool _disabled;
|
||||
public:
|
||||
auto_undo_enabler(bool slow_replays, undo_database& undo_db) :
|
||||
_slow_replays(slow_replays),
|
||||
_undo_db(undo_db),
|
||||
_disabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
auto start = fc::time_point::now();
|
||||
~auto_undo_enabler()
|
||||
{
|
||||
try{
|
||||
enable();
|
||||
} FC_CAPTURE_AND_LOG(("undo_db enabling crash"))
|
||||
}
|
||||
|
||||
void enable()
|
||||
{
|
||||
if(!_disabled)
|
||||
return;
|
||||
_undo_db.enable();
|
||||
_disabled = false;
|
||||
}
|
||||
|
||||
void disable()
|
||||
{
|
||||
if(_disabled)
|
||||
return;
|
||||
if(_slow_replays)
|
||||
return;
|
||||
_undo_db.disable();
|
||||
_disabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
void database::reindex( fc::path data_dir )
|
||||
{ try {
|
||||
auto last_block = _block_id_to_block.last();
|
||||
if( !last_block ) {
|
||||
elog( "!no last block" );
|
||||
edump((last_block));
|
||||
return;
|
||||
}
|
||||
if( last_block->block_num() <= head_block_num()) return;
|
||||
|
||||
ilog( "reindexing blockchain" );
|
||||
auto start = fc::time_point::now();
|
||||
const auto last_block_num = last_block->block_num();
|
||||
uint32_t flush_point = last_block_num < 10000 ? 0 : last_block_num - 10000;
|
||||
uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50;
|
||||
|
||||
ilog( "Replaying blocks..." );
|
||||
// Right now, we leave undo_db enabled when replaying when the bookie plugin is
|
||||
// enabled. It depends on new/changed/removed object notifications, and those are
|
||||
// only fired when the undo_db is enabled
|
||||
if (!_slow_replays)
|
||||
_undo_db.disable();
|
||||
for( uint32_t i = 1; i <= last_block_num; ++i )
|
||||
ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) );
|
||||
auto_undo_enabler undo(_slow_replays, _undo_db);
|
||||
if( head_block_num() >= undo_point )
|
||||
{
|
||||
if( i == 1 ||
|
||||
i % 10000 == 0 )
|
||||
std::cerr << " " << double(i*100)/last_block_num << "% "<< i << " of " <<last_block_num<<" \n";
|
||||
if( head_block_num() > 0 )
|
||||
_fork_db.start_block( *fetch_block_by_number( head_block_num() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
undo.disable();
|
||||
}
|
||||
for( uint32_t i = head_block_num() + 1; i <= last_block_num; ++i )
|
||||
{
|
||||
if( i % 10000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "<<i << " of " <<last_block_num<<" \n";
|
||||
if( i == flush_point )
|
||||
{
|
||||
ilog( "Writing database to disk at block ${i}", ("i",i) );
|
||||
flush();
|
||||
ilog( "Done" );
|
||||
}
|
||||
fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i);
|
||||
if( !block.valid() )
|
||||
{
|
||||
|
|
@ -94,24 +147,27 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) );
|
||||
break;
|
||||
}
|
||||
if (_slow_replays)
|
||||
push_block(*block, skip_fork_db |
|
||||
skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
else
|
||||
if( i < undo_point && !_slow_replays)
|
||||
{
|
||||
apply_block(*block, skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
}
|
||||
else
|
||||
{
|
||||
undo.enable();
|
||||
push_block(*block, skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
}
|
||||
}
|
||||
if (!_slow_replays)
|
||||
_undo_db.enable();
|
||||
undo.enable();
|
||||
auto end = fc::time_point::now();
|
||||
ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) );
|
||||
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
|
||||
|
|
@ -129,10 +185,29 @@ void database::wipe(const fc::path& data_dir, bool include_blocks)
|
|||
|
||||
void database::open(
|
||||
const fc::path& data_dir,
|
||||
std::function<genesis_state_type()> genesis_loader)
|
||||
std::function<genesis_state_type()> genesis_loader,
|
||||
const std::string& db_version)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool wipe_object_db = false;
|
||||
if( !fc::exists( data_dir / "db_version" ) )
|
||||
wipe_object_db = true;
|
||||
else
|
||||
{
|
||||
std::string version_string;
|
||||
fc::read_file_contents( data_dir / "db_version", version_string );
|
||||
wipe_object_db = ( version_string != db_version );
|
||||
}
|
||||
if( wipe_object_db ) {
|
||||
ilog("Wiping object_database due to missing or wrong version");
|
||||
object_database::wipe( data_dir );
|
||||
std::ofstream version_file( (data_dir / "db_version").generic_string().c_str(),
|
||||
std::ios::out | std::ios::binary | std::ios::trunc );
|
||||
version_file.write( db_version.c_str(), db_version.size() );
|
||||
version_file.close();
|
||||
}
|
||||
|
||||
object_database::open(data_dir);
|
||||
|
||||
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
|
||||
|
|
@ -140,15 +215,13 @@ void database::open(
|
|||
if( !find(global_property_id_type()) )
|
||||
init_genesis(genesis_loader());
|
||||
|
||||
fc::optional<signed_block> last_block = _block_id_to_block.last();
|
||||
fc::optional<block_id_type> last_block = _block_id_to_block.last_id();
|
||||
if( last_block.valid() )
|
||||
{
|
||||
_fork_db.start_block( *last_block );
|
||||
if( last_block->id() != head_block_id() )
|
||||
{
|
||||
FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state",
|
||||
("last_block->id", last_block->id())("head_block_num",head_block_num()) );
|
||||
}
|
||||
FC_ASSERT( *last_block >= head_block_id(),
|
||||
"last block ID does not match current chain state",
|
||||
("last_block->id", last_block)("head_block_id",head_block_num()) );
|
||||
reindex( data_dir );
|
||||
}
|
||||
_opened = true;
|
||||
}
|
||||
|
|
@ -173,17 +246,9 @@ void database::close(bool rewind)
|
|||
|
||||
while( head_block_num() > cutoff )
|
||||
{
|
||||
// elog("pop");
|
||||
block_id_type popped_block_id = head_block_id();
|
||||
pop_block();
|
||||
_fork_db.remove(popped_block_id); // doesn't throw on missing
|
||||
try
|
||||
{
|
||||
_block_id_to_block.remove(popped_block_id);
|
||||
}
|
||||
catch (const fc::key_not_found_exception&)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( const fc::exception& e )
|
||||
|
|
|
|||
|
|
@ -230,8 +230,16 @@ namespace graphene { namespace chain {
|
|||
share_type settlement_fund;
|
||||
///@}
|
||||
|
||||
/// The time when @ref current_feed would expire
|
||||
time_point_sec feed_expiration_time()const
|
||||
{ return current_feed_publication_time + options.feed_lifetime_sec; }
|
||||
{
|
||||
uint32_t current_feed_seconds = current_feed_publication_time.sec_since_epoch();
|
||||
if( std::numeric_limits<uint32_t>::max() - current_feed_seconds <= options.feed_lifetime_sec )
|
||||
return time_point_sec::maximum();
|
||||
else
|
||||
return current_feed_publication_time + options.feed_lifetime_sec;
|
||||
}
|
||||
|
||||
bool feed_is_expired_before_hardfork_615(time_point_sec current_time)const
|
||||
{ return feed_expiration_time() >= current_time; }
|
||||
bool feed_is_expired(time_point_sec current_time)const
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#include <graphene/chain/protocol/block.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class index_entry;
|
||||
|
||||
class block_database
|
||||
{
|
||||
public:
|
||||
|
|
@ -44,6 +46,8 @@ namespace graphene { namespace chain {
|
|||
optional<signed_block> last()const;
|
||||
optional<block_id_type> last_id()const;
|
||||
private:
|
||||
optional<index_entry> last_index_entry()const;
|
||||
fc::path _index_filename;
|
||||
mutable std::fstream _blocks;
|
||||
mutable std::fstream _block_num_to_pos;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -91,10 +91,12 @@ namespace graphene { namespace chain {
|
|||
*
|
||||
* @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
|
||||
* @param db_version a version string that changes when the internal database format and/or logic is modified
|
||||
*/
|
||||
void open(
|
||||
const fc::path& data_dir,
|
||||
std::function<genesis_state_type()> genesis_loader );
|
||||
std::function<genesis_state_type()> genesis_loader,
|
||||
const std::string& db_version );
|
||||
|
||||
/**
|
||||
* @brief Rebuild object graph from block history and open detabase
|
||||
|
|
@ -102,7 +104,7 @@ namespace graphene { namespace chain {
|
|||
* This method may be called after or instead of @ref database::open, and will rebuild the object graph by
|
||||
* replaying blockchain history. When this method exits successfully, the database will be open.
|
||||
*/
|
||||
void reindex(fc::path data_dir, const genesis_state_type& initial_allocation = genesis_state_type());
|
||||
void reindex(fc::path data_dir);
|
||||
|
||||
/**
|
||||
* @brief wipe Delete database from disk, and potentially the raw chain as well.
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class proposal_object : public abstract_object<proposal_object>
|
|||
std::string fail_reason;
|
||||
|
||||
bool is_authorized_to_execute(database& db) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -71,14 +71,20 @@ index& object_database::get_mutable_index(uint8_t space_id, uint8_t type_id)
|
|||
void object_database::flush()
|
||||
{
|
||||
// ilog("Save object_database in ${d}", ("d", _data_dir));
|
||||
fc::create_directories( _data_dir / "object_database.tmp" / "lock" );
|
||||
for( uint32_t space = 0; space < _index.size(); ++space )
|
||||
{
|
||||
fc::create_directories( _data_dir / "object_database" / fc::to_string(space) );
|
||||
fc::create_directories( _data_dir / "object_database.tmp" / fc::to_string(space) );
|
||||
const auto types = _index[space].size();
|
||||
for( uint32_t type = 0; type < types; ++type )
|
||||
if( _index[space][type] )
|
||||
_index[space][type]->save( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) );
|
||||
_index[space][type]->save( _data_dir / "object_database.tmp" / fc::to_string(space)/fc::to_string(type) );
|
||||
}
|
||||
fc::remove_all( _data_dir / "object_database.tmp" / "lock" );
|
||||
if( fc::exists( _data_dir / "object_database" ) )
|
||||
fc::rename( _data_dir / "object_database", _data_dir / "object_database.old" );
|
||||
fc::rename( _data_dir / "object_database.tmp", _data_dir / "object_database" );
|
||||
fc::remove_all( _data_dir / "object_database.old" );
|
||||
}
|
||||
|
||||
void object_database::wipe(const fc::path& data_dir)
|
||||
|
|
@ -91,8 +97,13 @@ void object_database::wipe(const fc::path& data_dir)
|
|||
|
||||
void object_database::open(const fc::path& data_dir)
|
||||
{ try {
|
||||
ilog("Opening object database from ${d} ...", ("d", data_dir));
|
||||
_data_dir = data_dir;
|
||||
if( fc::exists( _data_dir / "object_database" / "lock" ) )
|
||||
{
|
||||
wlog("Ignoring locked object_database");
|
||||
return;
|
||||
}
|
||||
ilog("Opening object database from ${d} ...", ("d", data_dir));
|
||||
for( uint32_t space = 0; space < _index.size(); ++space )
|
||||
for( uint32_t type = 0; type < _index[space].size(); ++type )
|
||||
if( _index[space][type] )
|
||||
|
|
|
|||
|
|
@ -118,8 +118,6 @@ void undo_database::undo()
|
|||
_db.insert( std::move(*item.second) );
|
||||
|
||||
_stack.pop_back();
|
||||
if( _stack.empty() )
|
||||
_stack.emplace_back();
|
||||
enable();
|
||||
--_active_sessions;
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
|
@ -127,6 +125,12 @@ void undo_database::undo()
|
|||
void undo_database::merge()
|
||||
{
|
||||
FC_ASSERT( _active_sessions > 0 );
|
||||
if( _active_sessions == 1 && _stack.size() == 1 )
|
||||
{
|
||||
_stack.pop_back();
|
||||
--_active_sessions;
|
||||
return;
|
||||
}
|
||||
FC_ASSERT( _stack.size() >=2 );
|
||||
auto& state = _stack.back();
|
||||
auto& prev_state = _stack[_stack.size()-2];
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench )
|
|||
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), [&]{return genesis_state;});
|
||||
db.open(data_dir.path(), [&]{return genesis_state;}, "test");
|
||||
|
||||
for( int i = 11; i < account_count + 11; ++i)
|
||||
BOOST_CHECK(db.get_balance(account_id_type(i), asset_id_type()).amount == GRAPHENE_MAX_SHARE_SUPPLY / account_count);
|
||||
|
|
@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench )
|
|||
database db;
|
||||
|
||||
fc::time_point start_time = fc::time_point::now();
|
||||
db.open(data_dir.path(), [&]{return genesis_state;});
|
||||
db.open(data_dir.path(), [&]{return genesis_state;}, "test");
|
||||
ilog("Opened database in ${t} milliseconds.", ("t", (fc::time_point::now() - start_time).count() / 1000));
|
||||
|
||||
for( int i = 11; i < account_count + 11; ++i)
|
||||
|
|
@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench )
|
|||
|
||||
auto start_time = fc::time_point::now();
|
||||
wlog( "about to start reindex..." );
|
||||
db.reindex(data_dir.path(), genesis_state);
|
||||
db.open(data_dir.path(), [&]{return genesis_state;}, "force_wipe");
|
||||
ilog("Replayed database in ${t} milliseconds.", ("t", (fc::time_point::now() - start_time).count() / 1000));
|
||||
|
||||
for( int i = 0; i < blocks_to_produce; ++i )
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ void database_fixture::open_database()
|
|||
{
|
||||
if( !data_dir ) {
|
||||
data_dir = fc::temp_directory( graphene::utilities::temp_directory_path() );
|
||||
db.open(data_dir->path(), [this]{return genesis_state;});
|
||||
db.open(data_dir->path(), [this]{return genesis_state;}, "test");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ int main( int argc, char** argv )
|
|||
|
||||
database db;
|
||||
fc::path db_path = data_dir / "db";
|
||||
db.open(db_path, [&]() { return genesis; } );
|
||||
db.open(db_path, [&]() { return genesis; }, "TEST" );
|
||||
|
||||
uint32_t slot = 1;
|
||||
uint32_t missed = 0;
|
||||
|
|
|
|||
|
|
@ -521,9 +521,10 @@ BOOST_AUTO_TEST_CASE( committee_authority )
|
|||
// Should throw because the transaction is now in review.
|
||||
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
|
||||
|
||||
// generate_blocks(prop.expiration_time);
|
||||
// fails
|
||||
// BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);
|
||||
generate_blocks(prop.expiration_time);
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);
|
||||
// proposal deleted
|
||||
BOOST_CHECK_THROW( db.get<proposal_object>(prop.id), fc::exception );
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( fired_committee_members, database_fixture )
|
||||
|
|
|
|||
|
|
@ -540,4 +540,19 @@ BOOST_AUTO_TEST_CASE( merkle_root )
|
|||
BOOST_CHECK( block.calculate_merkle_root() == c(dO) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reproduces https://github.com/bitshares/bitshares-core/issues/888 and tests fix for it.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE( bitasset_feed_expiration_test )
|
||||
{
|
||||
time_point_sec now = fc::time_point::now();
|
||||
|
||||
asset_bitasset_data_object o;
|
||||
|
||||
o.current_feed_publication_time = now - fc::hours(1);
|
||||
o.options.feed_lifetime_sec = std::numeric_limits<uint32_t>::max() - 1;
|
||||
|
||||
BOOST_CHECK( !o.feed_is_expired( now ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -137,9 +137,10 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
// TODO: Don't generate this here
|
||||
auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
signed_block cutoff_block;
|
||||
uint32_t last_block;
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis );
|
||||
db.open(data_dir.path(), make_genesis, "TEST" );
|
||||
b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
|
||||
// TODO: Change this test when we correct #406
|
||||
|
|
@ -156,6 +157,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
if( cutoff_height >= 200 )
|
||||
{
|
||||
cutoff_block = *(db.fetch_block_by_number( cutoff_height ));
|
||||
last_block = db.head_block_num();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -163,8 +165,10 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
}
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), []{return genesis_state_type();});
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num() );
|
||||
db.open(data_dir.path(), []{return genesis_state_type();}, "TEST");
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), last_block );
|
||||
while( db.head_block_num() > cutoff_block.block_num() )
|
||||
db.pop_block();
|
||||
b = cutoff_block;
|
||||
for( uint32_t i = 0; i < 200; ++i )
|
||||
{
|
||||
|
|
@ -188,7 +192,7 @@ BOOST_AUTO_TEST_CASE( undo_block )
|
|||
fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis);
|
||||
db.open(data_dir.path(), make_genesis, "TEST");
|
||||
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
|
||||
std::vector< time_point_sec > time_stack;
|
||||
|
||||
|
|
@ -237,57 +241,112 @@ BOOST_AUTO_TEST_CASE( fork_blocks )
|
|||
fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() );
|
||||
|
||||
database db1;
|
||||
db1.open(data_dir1.path(), make_genesis);
|
||||
db1.open(data_dir1.path(), make_genesis, "TEST");
|
||||
database db2;
|
||||
db2.open(data_dir2.path(), make_genesis);
|
||||
db2.open(data_dir2.path(), make_genesis, "TEST");
|
||||
BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );
|
||||
|
||||
auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
for( uint32_t i = 0; i < 10; ++i )
|
||||
|
||||
BOOST_TEST_MESSAGE( "Adding blocks 1 through 10" );
|
||||
for( uint32_t i = 1; i <= 10; ++i )
|
||||
{
|
||||
auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
try {
|
||||
PUSH_BLOCK( db2, b );
|
||||
} FC_CAPTURE_AND_RETHROW( ("db2") );
|
||||
}
|
||||
for( uint32_t i = 10; i < 13; ++i )
|
||||
|
||||
for( uint32_t j = 0; j <= 4; j += 4 )
|
||||
{
|
||||
auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
}
|
||||
string db1_tip = db1.head_block_id().str();
|
||||
uint32_t next_slot = 3;
|
||||
for( uint32_t i = 13; i < 16; ++i )
|
||||
{
|
||||
auto b = db2.generate_block(db2.get_slot_time(next_slot), db2.get_scheduled_witness(next_slot), init_account_priv_key, database::skip_nothing);
|
||||
next_slot = 1;
|
||||
// notify both databases of the new block.
|
||||
// only db2 should switch to the new fork, db1 should not
|
||||
PUSH_BLOCK( db1, b );
|
||||
// add blocks 11 through 13 to db1 only
|
||||
BOOST_TEST_MESSAGE( "Adding 3 blocks to db1 only" );
|
||||
for( uint32_t i = 11 + j; i <= 13 + j; ++i )
|
||||
{
|
||||
BOOST_TEST_MESSAGE( i );
|
||||
auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
}
|
||||
string db1_tip = db1.head_block_id().str();
|
||||
|
||||
// add different blocks 11 through 13 to db2 only
|
||||
BOOST_TEST_MESSAGE( "Add 3 different blocks to db2 only" );
|
||||
uint32_t next_slot = 3;
|
||||
for( uint32_t i = 11 + j; i <= 13 + j; ++i )
|
||||
{
|
||||
BOOST_TEST_MESSAGE( i );
|
||||
auto b = db2.generate_block(db2.get_slot_time(next_slot), db2.get_scheduled_witness(next_slot), init_account_priv_key, database::skip_nothing);
|
||||
next_slot = 1;
|
||||
// notify both databases of the new block.
|
||||
// only db2 should switch to the new fork, db1 should not
|
||||
PUSH_BLOCK( db1, b );
|
||||
BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip);
|
||||
BOOST_CHECK_EQUAL(db2.head_block_id().str(), b.id().str());
|
||||
}
|
||||
|
||||
//The two databases are on distinct forks now, but at the same height.
|
||||
BOOST_CHECK_EQUAL(db1.head_block_num(), 13u + j);
|
||||
BOOST_CHECK_EQUAL(db2.head_block_num(), 13u + j);
|
||||
BOOST_CHECK( db1.head_block_id() != db2.head_block_id() );
|
||||
|
||||
//Make a block on db2, make it invalid, then
|
||||
//pass it to db1 and assert that db1 doesn't switch to the new fork.
|
||||
signed_block good_block;
|
||||
{
|
||||
auto b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
good_block = b;
|
||||
b.transactions.emplace_back(signed_transaction());
|
||||
b.transactions.back().operations.emplace_back(transfer_operation());
|
||||
b.sign( init_account_priv_key );
|
||||
BOOST_CHECK_EQUAL(b.block_num(), 14u + j);
|
||||
GRAPHENE_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception);
|
||||
|
||||
// At this point, `fetch_block_by_number` will fetch block from fork_db,
|
||||
// so unable to reproduce the issue which is fixed in PR #938
|
||||
// https://github.com/bitshares/bitshares-core/pull/938
|
||||
fc::optional<signed_block> previous_block = db1.fetch_block_by_number(1);
|
||||
BOOST_CHECK ( previous_block.valid() );
|
||||
uint32_t db1_blocks = db1.head_block_num();
|
||||
for( uint32_t curr_block_num = 2; curr_block_num <= db1_blocks; ++curr_block_num )
|
||||
{
|
||||
fc::optional<signed_block> curr_block = db1.fetch_block_by_number( curr_block_num );
|
||||
BOOST_CHECK( curr_block.valid() );
|
||||
BOOST_CHECK_EQUAL( curr_block->previous.str(), previous_block->id().str() );
|
||||
previous_block = curr_block;
|
||||
}
|
||||
}
|
||||
BOOST_CHECK_EQUAL(db1.head_block_num(), 13u + j);
|
||||
BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip);
|
||||
BOOST_CHECK_EQUAL(db2.head_block_id().str(), b.id().str());
|
||||
|
||||
if( j == 0 )
|
||||
{
|
||||
// assert that db1 switches to new fork with good block
|
||||
BOOST_CHECK_EQUAL(db2.head_block_num(), 14u + j);
|
||||
PUSH_BLOCK( db1, good_block );
|
||||
BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str());
|
||||
}
|
||||
}
|
||||
|
||||
//The two databases are on distinct forks now, but at the same height. Make a block on db2, make it invalid, then
|
||||
//pass it to db1 and assert that db1 doesn't switch to the new fork.
|
||||
signed_block good_block;
|
||||
BOOST_CHECK_EQUAL(db1.head_block_num(), 13);
|
||||
BOOST_CHECK_EQUAL(db2.head_block_num(), 13);
|
||||
// generate more blocks to push the forked blocks out of fork_db
|
||||
BOOST_TEST_MESSAGE( "Adding more blocks to db1, push the forked blocks out of fork_db" );
|
||||
for( uint32_t i = 1; i <= 50; ++i )
|
||||
{
|
||||
auto b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
good_block = b;
|
||||
b.transactions.emplace_back(signed_transaction());
|
||||
b.transactions.back().operations.emplace_back(transfer_operation());
|
||||
b.sign( init_account_priv_key );
|
||||
BOOST_CHECK_EQUAL(b.block_num(), 14);
|
||||
GRAPHENE_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception);
|
||||
db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
}
|
||||
BOOST_CHECK_EQUAL(db1.head_block_num(), 13);
|
||||
BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip);
|
||||
|
||||
// assert that db1 switches to new fork with good block
|
||||
BOOST_CHECK_EQUAL(db2.head_block_num(), 14);
|
||||
PUSH_BLOCK( db1, good_block );
|
||||
BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str());
|
||||
{
|
||||
// PR #938 make sure db is in a good state https://github.com/bitshares/bitshares-core/pull/938
|
||||
BOOST_TEST_MESSAGE( "Checking whether all blocks on disk are good" );
|
||||
fc::optional<signed_block> previous_block = db1.fetch_block_by_number(1);
|
||||
BOOST_CHECK ( previous_block.valid() );
|
||||
uint32_t db1_blocks = db1.head_block_num();
|
||||
for( uint32_t curr_block_num = 2; curr_block_num <= db1_blocks; ++curr_block_num )
|
||||
{
|
||||
fc::optional<signed_block> curr_block = db1.fetch_block_by_number( curr_block_num );
|
||||
BOOST_CHECK( curr_block.valid() );
|
||||
BOOST_CHECK_EQUAL( curr_block->previous.str(), previous_block->id().str() );
|
||||
previous_block = curr_block;
|
||||
}
|
||||
}
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
|
|
@ -382,7 +441,7 @@ BOOST_AUTO_TEST_CASE( undo_pending )
|
|||
fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis);
|
||||
db.open(data_dir.path(), make_genesis, "TEST");
|
||||
|
||||
auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
public_key_type init_account_pub_key = init_account_priv_key.get_public_key();
|
||||
|
|
@ -447,8 +506,8 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
|
|||
dir2( graphene::utilities::temp_directory_path() );
|
||||
database db1,
|
||||
db2;
|
||||
db1.open(dir1.path(), make_genesis);
|
||||
db2.open(dir2.path(), make_genesis);
|
||||
db1.open(dir1.path(), make_genesis, "TEST");
|
||||
db2.open(dir2.path(), make_genesis, "TEST");
|
||||
BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );
|
||||
|
||||
auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
|
|
@ -506,8 +565,8 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions )
|
|||
dir2( graphene::utilities::temp_directory_path() );
|
||||
database db1,
|
||||
db2;
|
||||
db1.open(dir1.path(), make_genesis);
|
||||
db2.open(dir2.path(), make_genesis);
|
||||
db1.open(dir1.path(), make_genesis, "TEST");
|
||||
db2.open(dir2.path(), make_genesis, "TEST");
|
||||
BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );
|
||||
|
||||
auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check;
|
||||
|
|
@ -556,7 +615,7 @@ BOOST_AUTO_TEST_CASE( tapos )
|
|||
try {
|
||||
fc::temp_directory dir1( graphene::utilities::temp_directory_path() );
|
||||
database db1;
|
||||
db1.open(dir1.path(), make_genesis);
|
||||
db1.open(dir1.path(), make_genesis, "TEST");
|
||||
|
||||
const account_object& init1 = *db1.get_index_type<account_index>().indices().get<by_name>().find("init1");
|
||||
|
||||
|
|
@ -1107,7 +1166,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )
|
|||
fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() );
|
||||
|
||||
database db2;
|
||||
db2.open(data_dir2.path(), make_genesis);
|
||||
db2.open(data_dir2.path(), make_genesis, "TEST");
|
||||
BOOST_CHECK( db.get_chain_id() == db2.get_chain_id() );
|
||||
|
||||
while( db2.head_block_num() < db.head_block_num() )
|
||||
|
|
@ -1270,7 +1329,7 @@ BOOST_AUTO_TEST_CASE( genesis_reserve_ids )
|
|||
genesis_state.initial_assets.push_back( usd );
|
||||
|
||||
return genesis_state;
|
||||
} );
|
||||
}, "TEST" );
|
||||
|
||||
const auto& acct_idx = db.get_index_type<account_index>().indices().get<by_name>();
|
||||
auto acct_itr = acct_idx.find("init0");
|
||||
|
|
|
|||
|
|
@ -62,6 +62,25 @@ BOOST_AUTO_TEST_CASE( undo_test )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( merge_test )
|
||||
{
|
||||
try {
|
||||
database db;
|
||||
auto ses = db._undo_db.start_undo_session();
|
||||
const auto& bal_obj1 = db.create<account_balance_object>( [&]( account_balance_object& obj ){
|
||||
obj.balance = 42;
|
||||
});
|
||||
ses.merge();
|
||||
|
||||
auto balance = db.get_balance( account_id_type(), asset_id_type() );
|
||||
BOOST_CHECK_EQUAL( 42, balance.amount.value );
|
||||
} catch ( const fc::exception& e )
|
||||
{
|
||||
edump( (e.to_detail_string()) );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( flat_index_test )
|
||||
{
|
||||
ACTORS((sam));
|
||||
|
|
|
|||
|
|
@ -1111,7 +1111,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
|
|||
auto _sign = [&]( signed_transaction& tx, const private_key_type& key )
|
||||
{ tx.sign( key, db.get_chain_id() ); };
|
||||
|
||||
db.open(td.path(), [this]{return genesis_state;});
|
||||
db.open(td.path(), [this]{return genesis_state;}, "TEST");
|
||||
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