diff --git a/libraries/chain/block.cpp b/libraries/chain/block.cpp index ad731b84..47ab8d37 100644 --- a/libraries/chain/block.cpp +++ b/libraries/chain/block.cpp @@ -53,6 +53,7 @@ namespace graphene { namespace chain { checksum_type signed_block::calculate_merkle_root()const { if( transactions.size() == 0 ) return checksum_type(); + wdump((transactions.size())); vector ids; ids.resize( ((transactions.size() + 1)/2)*2 ); diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp index 94389571..16cf76db 100644 --- a/libraries/chain/block_database.cpp +++ b/libraries/chain/block_database.cpp @@ -8,12 +8,29 @@ struct index_entry uint32_t block_size = 0; block_id_type block_id; }; + }} +FC_REFLECT( graphene::chain::index_entry, (block_pos)(block_size)(block_id) ); + +namespace graphene { namespace chain { void block_database::open( const fc::path& dbdir ) -{ - _block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary ); - _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary ); -} +{ try { + idump((sizeof(index_entry)) ); + fc::create_directories(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" ) ) + { + _block_num_to_pos.open( (dbdir/"index").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 ); + _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out ); + } +} FC_CAPTURE_AND_RETHROW( (dbdir) ) } bool block_database::is_open()const @@ -51,7 +68,7 @@ void block_database::store( const block_id_type& id, const signed_block& b ) void block_database::remove( const block_id_type& id ) -{ +{ try { 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 ); @@ -60,12 +77,13 @@ void block_database::remove( const block_id_type& id ) _block_num_to_pos.seekg( index_pos ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); - FC_ASSERT( e.block_id == id ); - - e.block_size = 0; - _block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) ); - _block_num_to_pos.write( (char*)&e, sizeof(e) ); -} + if( e.block_id == id ) + { + e.block_size = 0; + _block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) ); + _block_num_to_pos.write( (char*)&e, sizeof(e) ); + } +} FC_CAPTURE_AND_RETHROW( (id) ) } @@ -100,7 +118,7 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const optional block_database::fetch_optional( const block_id_type& id )const -{ +{ try { 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 ); @@ -114,25 +132,31 @@ optional block_database::fetch_optional( const block_id_type& id ) vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); - return fc::raw::unpack(data); -} + auto result = fc::raw::unpack(data); + FC_ASSERT( result.id() == e.block_id ); + return result; +} FC_CAPTURE_AND_RETHROW( (id) ) } optional block_database::fetch_by_number( uint32_t block_num )const -{ +{ try { index_entry e; auto index_pos = sizeof(e)*block_num; _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); - _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); + wdump((int64_t(_block_num_to_pos.tellg())) ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); + wdump((block_num)(e)); vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); - return fc::raw::unpack(data); -} + auto result = fc::raw::unpack(data); + FC_ASSERT( result.id() == e.block_id ); + return result; +} FC_CAPTURE_AND_RETHROW( (block_num) ) } optional block_database::last()const @@ -145,14 +169,21 @@ optional block_database::last()const _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); - + while( e.block_size == 0 && _blocks.tellg() > 0 ) + { + _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.cur ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + } + if( e.block_size == 0 ) return optional(); vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); - return fc::raw::unpack(data); + auto result = fc::raw::unpack(data); + wdump((result)); + return result; } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e8493820..d82dde79 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -84,7 +84,6 @@ bool database::push_block( const signed_block& new_block, uint32_t skip ) { try { if( !(skip&skip_fork_db) ) { - wdump((new_block.id())(new_block.previous)); auto new_head = _fork_db.push_block( new_block ); //If the head block from the longest chain does not build off of the current head, we need to switch forks. if( new_head->data.previous != head_block_id() ) @@ -115,12 +114,13 @@ bool database::push_block( const signed_block& new_block, uint32_t skip ) try { auto session = _undo_db.start_undo_session(); apply_block( (*ritr)->data, skip ); - _block_id_to_block.store( new_block.id(), (*ritr)->data ); + _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); session.commit(); } catch ( const fc::exception& e ) { except = e; } if( except ) { + wdump((except->to_detail_string())); elog( "Encountered error when switching to a longer fork at id ${id}. Going back.", ("id", (*ritr)->id) ); // remove the rest of branches.first from the fork_db, those blocks are invalid @@ -325,7 +325,7 @@ void database::apply_block( const signed_block& next_block, uint32_t skip ) { try { _applied_ops.clear(); - FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root() ); + FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) ); const witness_object& signing_witness = validate_block_header(skip, next_block); const auto& global_props = get_global_properties(); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 5651fe1e..5ab3c837 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -41,7 +41,10 @@ void database::open( const fc::path& data_dir, const genesis_allocation& initial _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(); @@ -96,6 +99,7 @@ void database::close(uint32_t blocks_to_rewind) for(uint32_t i = 0; i < blocks_to_rewind && head_block_num() > 0; ++i) pop_block(); + object_database::flush(); object_database::close(); if( _block_id_to_block.is_open() ) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index af95414d..84c4103a 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -177,7 +177,7 @@ namespace graphene { namespace db { virtual void set_next_id( object_id_type id )override { _next_id = id; } virtual void open( const path& db )override - { + { if( !fc::exists( db ) ) return; fc::file_mapping fm( db.generic_string().c_str(), fc::read_only ); fc::mapped_region mr( fm, fc::read_only, 0, fc::file_size(db) ); @@ -195,7 +195,9 @@ namespace graphene { namespace db { virtual void save( const path& db ) override { - std::ofstream out( db.generic_string(), std::ofstream::binary ); + std::ofstream out( db.generic_string(), + std::ofstream::binary | std::ofstream::out | std::ofstream::trunc ); + FC_ASSERT( out ); out.write( (char*)&_next_id, sizeof(_next_id) ); this->inspect_all_objects( [&]( const object& o ) { auto vec = fc::raw::pack( static_cast(o) ); diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index 9b649c81..dc5cba58 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -68,6 +68,7 @@ void object_database::flush() ilog("Save object_database in ${d}", ("d", _data_dir)); for( uint32_t space = 0; space < _index.size(); ++space ) { + fc::create_directories( _data_dir / "object_database" / fc::to_string(space) ); const auto types = _index[space].size(); for( uint32_t type = 0; type < types; ++type ) if( _index[space][type] ) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index c74e182d..3ff4ea90 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -37,6 +37,73 @@ using namespace graphene::chain; BOOST_AUTO_TEST_SUITE(block_tests) +BOOST_AUTO_TEST_CASE( block_database_test ) +{ + try { + fc::temp_directory data_dir; + + block_database bdb; + bdb.open( data_dir.path() ); + FC_ASSERT( bdb.is_open() ); + bdb.close(); + FC_ASSERT( !bdb.is_open() ); + bdb.open( data_dir.path() ); + + signed_block b; + for( uint32_t i = 0; i < 5; ++i ) + { + if( i > 0 ) b.previous = b.id(); + b.witness = witness_id_type(i+1); + edump((b)); + bdb.store( b.id(), b ); + + auto fetch = bdb.fetch_by_number( b.block_num() ); + idump((fetch)); + FC_ASSERT( fetch.valid() ); + FC_ASSERT( fetch->witness == b.witness ); + fetch = bdb.fetch_by_number( i+1 ); + idump((fetch)); + FC_ASSERT( fetch.valid() ); + FC_ASSERT( fetch->witness == b.witness ); + fetch = bdb.fetch_optional( b.id() ); + idump((fetch)); + FC_ASSERT( fetch.valid() ); + FC_ASSERT( fetch->witness == b.witness ); + } + ilog("-----------" ); + + for( uint32_t i = 1; i < 5; ++i ) + { + auto blk = bdb.fetch_by_number( i ); + FC_ASSERT( blk.valid() ); + idump((blk)(i)); + FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); + } + + auto last = bdb.last(); + FC_ASSERT( last ); + FC_ASSERT( last->id() == b.id() ); + + bdb.close(); + bdb.open( data_dir.path() ); + last = bdb.last(); + FC_ASSERT( last ); + FC_ASSERT( last->id() == b.id() ); + + for( uint32_t i = 0; i < 5; ++i ) + { + auto blk = bdb.fetch_by_number( i+1 ); + FC_ASSERT( blk.valid() ); + idump((blk)(i)); + FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); + } + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( generate_empty_blocks ) { try { @@ -142,7 +209,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) db2.open( data_dir2.path(), genesis_allocation() ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); - for( uint32_t i = 0; i < 20; ++i ) + for( uint32_t i = 0; i < 10; ++i ) { now += db1.block_interval(); auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); @@ -150,20 +217,19 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) db2.push_block(b); } FC_CAPTURE_AND_RETHROW( ("db2") ); } - for( uint32_t i = 20; i < 23; ++i ) + for( uint32_t i = 10; i < 13; ++i ) { now += db1.block_interval(); auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); } string db1_tip = db1.head_block_id().str(); - for( uint32_t i = 23; i < 26; ++i ) + for( uint32_t i = 13; i < 16; ++i ) { now += db2.block_interval(); auto b = db2.generate_block( now, db2.get_scheduled_witness( db2.get_slot_at_time( now ) ).first, delegate_priv_key ); // notify both databases of the new block. // only db2 should switch to the new fork, db1 should not db1.push_block(b); - db2.push_block(b); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip); BOOST_CHECK_EQUAL(db2.head_block_id().str(), b.id().str()); } @@ -171,8 +237,8 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) //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(), 23); - BOOST_CHECK_EQUAL(db2.head_block_num(), 23); + BOOST_CHECK_EQUAL(db1.head_block_num(), 13); + BOOST_CHECK_EQUAL(db2.head_block_num(), 13); { now += db2.block_interval(); auto b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); @@ -180,14 +246,14 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) b.transactions.emplace_back(signed_transaction()); b.transactions.back().operations.emplace_back(transfer_operation()); b.sign(delegate_priv_key); - BOOST_CHECK_EQUAL(b.block_num(), 24); + BOOST_CHECK_EQUAL(b.block_num(), 14); BOOST_CHECK_THROW(db1.push_block(b), fc::exception); } - BOOST_CHECK_EQUAL(db1.head_block_num(), 23); + 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(), 24); + BOOST_CHECK_EQUAL(db2.head_block_num(), 14); db1.push_block(good_block); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str()); } catch (fc::exception& e) {