/* * Copyright (c) 2015 Cryptonomex, Inc., and contributors. * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "../common/database_fixture.hpp" using namespace graphene::chain; using namespace graphene::chain::test; genesis_state_type make_genesis() { genesis_state_type genesis_state; genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); genesis_state.initial_active_witnesses = 10; for( int i = 0; i < genesis_state.initial_active_witnesses; ++i ) { auto name = "init"+fc::to_string(i); genesis_state.initial_accounts.emplace_back(name, init_account_priv_key.get_public_key(), init_account_priv_key.get_public_key(), true); genesis_state.initial_committee_candidates.push_back({name}); genesis_state.initial_witness_candidates.push_back({name, init_account_priv_key.get_public_key()}); } genesis_state.initial_parameters.current_fees->zero_all_fees(); return genesis_state; } BOOST_AUTO_TEST_SUITE(block_tests) BOOST_AUTO_TEST_CASE( block_database_test ) { try { fc::temp_directory data_dir( graphene::utilities::temp_directory_path() ); 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); bdb.store( b.id(), b ); auto fetch = bdb.fetch_by_number( b.block_num() ); FC_ASSERT( fetch.valid() ); FC_ASSERT( fetch->witness == b.witness ); fetch = bdb.fetch_by_number( i+1 ); FC_ASSERT( fetch.valid() ); FC_ASSERT( fetch->witness == b.witness ); fetch = bdb.fetch_optional( b.id() ); FC_ASSERT( fetch.valid() ); FC_ASSERT( fetch->witness == b.witness ); } for( uint32_t i = 1; i < 5; ++i ) { auto blk = bdb.fetch_by_number( i ); FC_ASSERT( blk.valid() ); 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() ); 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 { fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); fc::temp_directory data_dir( graphene::utilities::temp_directory_path() ); signed_block b; // 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; { database db; db.open(data_dir.path(), make_genesis ); 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 // n.b. we generate GRAPHENE_MIN_UNDO_HISTORY+1 extra blocks which will be discarded on save for( uint32_t i = 1; ; ++i ) { BOOST_CHECK( db.head_block_id() == b.id() ); //witness_id_type prev_witness = b.witness; witness_id_type cur_witness = db.get_scheduled_witness(1); //BOOST_CHECK( cur_witness != prev_witness ); b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing); BOOST_CHECK( b.witness == cur_witness ); uint32_t cutoff_height = db.get_dynamic_global_properties().last_irreversible_block_num; if( cutoff_height >= 200 ) { cutoff_block = *(db.fetch_block_by_number( cutoff_height )); break; } } db.close(); } { database db; db.open(data_dir.path(), []{return genesis_state_type();}); BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num() ); b = cutoff_block; for( uint32_t i = 0; i < 200; ++i ) { BOOST_CHECK( db.head_block_id() == b.id() ); //witness_id_type prev_witness = b.witness; witness_id_type cur_witness = db.get_scheduled_witness(1); //BOOST_CHECK( cur_witness != prev_witness ); b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing); } BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num()+200 ); } } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( undo_block ) { try { fc::temp_directory data_dir( graphene::utilities::temp_directory_path() ); { database db; db.open(data_dir.path(), make_genesis); fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); std::vector< time_point_sec > time_stack; auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); for( uint32_t i = 0; i < 5; ++i ) { now = db.get_slot_time(1); time_stack.push_back( now ); auto b = db.generate_block( now, db.get_scheduled_witness( 1 ), init_account_priv_key, database::skip_nothing ); } BOOST_CHECK( db.head_block_num() == 5 ); BOOST_CHECK( db.head_block_time() == now ); db.pop_block(); time_stack.pop_back(); now = time_stack.back(); BOOST_CHECK( db.head_block_num() == 4 ); BOOST_CHECK( db.head_block_time() == now ); db.pop_block(); time_stack.pop_back(); now = time_stack.back(); BOOST_CHECK( db.head_block_num() == 3 ); BOOST_CHECK( db.head_block_time() == now ); db.pop_block(); time_stack.pop_back(); now = time_stack.back(); BOOST_CHECK( db.head_block_num() == 2 ); BOOST_CHECK( db.head_block_time() == now ); for( uint32_t i = 0; i < 5; ++i ) { now = db.get_slot_time(1); time_stack.push_back( now ); auto b = db.generate_block( now, db.get_scheduled_witness( 1 ), init_account_priv_key, database::skip_nothing ); } BOOST_CHECK( db.head_block_num() == 7 ); } } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( fork_blocks ) { try { fc::temp_directory data_dir1( graphene::utilities::temp_directory_path() ); fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); database db1; db1.open(data_dir1.path(), make_genesis); database db2; db2.open(data_dir2.path(), make_genesis); 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 ) { 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 ) { 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 ); 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. 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); { 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); } 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()); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } /** * These test has been disabled, out of order blocks should result in the node getting disconnected. * BOOST_AUTO_TEST_CASE( fork_db_tests ) { try { fork_database fdb; signed_block prev; signed_block skipped_block; for( uint32_t i = 0; i < 2000; ++i ) { signed_block b; b.previous = prev.id(); if( b.block_num() == 1800 ) skipped_block = b; else fdb.push_block( b ); prev = b; } auto head = fdb.head(); FC_ASSERT( head && head->data.block_num() == 1799 ); fdb.push_block(skipped_block); head = fdb.head(); FC_ASSERT( head && head->data.block_num() == 2001, "", ("head",head->data.block_num()) ); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( out_of_order_blocks ) { try { fc::temp_directory data_dir1( graphene::utilities::temp_directory_path() ); fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); database db1; db1.open(data_dir1.path(), make_genesis); database db2; db2.open(data_dir2.path(), make_genesis); 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")) ); auto b1 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b2 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b3 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b4 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b5 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b6 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b7 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b8 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b9 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b10 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b11 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); auto b12 = 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(), 12); BOOST_CHECK_EQUAL(db2.head_block_num(), 0); PUSH_BLOCK( db2, b1 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 1); PUSH_BLOCK( db2, b3 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 1); PUSH_BLOCK( db2, b2 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 3); PUSH_BLOCK( db2, b5 ); PUSH_BLOCK( db2, b6 ); PUSH_BLOCK( db2, b7 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 3); PUSH_BLOCK( db2, b4 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 7); PUSH_BLOCK( db2, b8 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 8); PUSH_BLOCK( db2, b11 ); PUSH_BLOCK( db2, b10 ); PUSH_BLOCK( db2, b12 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 8); PUSH_BLOCK( db2, b9 ); BOOST_CHECK_EQUAL(db2.head_block_num(), 12); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } */ BOOST_AUTO_TEST_CASE( undo_pending ) { try { fc::temp_directory data_dir( graphene::utilities::temp_directory_path() ); { database db; db.open(data_dir.path(), make_genesis); 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(); const graphene::db::index& account_idx = db.get_index(protocol_ids, account_object_type); transfer_operation t; t.to = account_id_type(1); t.amount = asset( 10000000 ); { signed_transaction trx; set_expiration( db, trx ); trx.operations.push_back(t); PUSH_TX( db, trx, ~0 ); auto b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, ~0); } signed_transaction trx; set_expiration( db, trx ); account_id_type nathan_id = account_idx.get_next_id(); account_create_operation cop; cop.registrar = GRAPHENE_TEMP_ACCOUNT; cop.name = "nathan"; cop.owner = authority(1, init_account_pub_key, 1); cop.active = cop.owner; trx.operations.push_back(cop); //sign( trx, init_account_priv_key ); PUSH_TX( db, trx ); auto b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); BOOST_CHECK(nathan_id(db).name == "nathan"); trx.clear(); set_expiration( db, trx ); t.fee = asset(1); t.from = account_id_type(1); t.to = nathan_id; t.amount = asset(5000); trx.operations.push_back(t); db.push_transaction(trx, ~0); trx.clear(); set_expiration( db, trx ); trx.operations.push_back(t); db.push_transaction(trx, ~0); BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 10000); db.clear_pending(); BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 0); } } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) { try { fc::temp_directory dir1( graphene::utilities::temp_directory_path() ), dir2( graphene::utilities::temp_directory_path() ); database db1, db2; db1.open(dir1.path(), make_genesis); db2.open(dir2.path(), make_genesis); 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")) ); public_key_type init_account_pub_key = init_account_priv_key.get_public_key(); const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); signed_transaction trx; set_expiration( db1, trx ); account_id_type nathan_id = account_idx.get_next_id(); account_create_operation cop; cop.registrar = GRAPHENE_TEMP_ACCOUNT; cop.name = "nathan"; cop.owner = authority(1, init_account_pub_key, 1); cop.active = cop.owner; trx.operations.push_back(cop); PUSH_TX( db1, trx ); // generate blocks // db1 : A // db2 : B C D auto aw = db1.get_global_properties().active_witnesses; auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); BOOST_CHECK(nathan_id(db1).name == "nathan"); b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); db1.push_block(b); aw = db2.get_global_properties().active_witnesses; b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); db1.push_block(b); GRAPHENE_REQUIRE_THROW(nathan_id(db2), fc::exception); nathan_id(db1); /// it should be included in the pending state db1.clear_pending(); // clear it so that we can verify it was properly removed from pending state. GRAPHENE_REQUIRE_THROW(nathan_id(db1), fc::exception); PUSH_TX( db2, trx ); aw = db2.get_global_properties().active_witnesses; b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); db1.push_block(b); BOOST_CHECK(nathan_id(db1).name == "nathan"); BOOST_CHECK(nathan_id(db2).name == "nathan"); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( duplicate_transactions ) { try { fc::temp_directory dir1( graphene::utilities::temp_directory_path() ), dir2( graphene::utilities::temp_directory_path() ); database db1, db2; db1.open(dir1.path(), make_genesis); db2.open(dir2.path(), make_genesis); BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() ); auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check; 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(); const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); signed_transaction trx; set_expiration( db1, trx ); account_id_type nathan_id = account_idx.get_next_id(); account_create_operation cop; cop.name = "nathan"; cop.owner = authority(1, init_account_pub_key, 1); cop.active = cop.owner; trx.operations.push_back(cop); trx.sign( init_account_priv_key, db1.get_chain_id() ); PUSH_TX( db1, trx, skip_sigs ); trx = decltype(trx)(); set_expiration( db1, trx ); transfer_operation t; t.to = nathan_id; t.amount = asset(500); trx.operations.push_back(t); trx.sign( init_account_priv_key, db1.get_chain_id() ); PUSH_TX( db1, trx, skip_sigs ); GRAPHENE_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception); auto b = db1.generate_block( db1.get_slot_time(1), db1.get_scheduled_witness( 1 ), init_account_priv_key, skip_sigs ); PUSH_BLOCK( db2, b, skip_sigs ); GRAPHENE_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception); GRAPHENE_CHECK_THROW(PUSH_TX( db2, trx, skip_sigs ), fc::exception); BOOST_CHECK_EQUAL(db1.get_balance(nathan_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db2.get_balance(nathan_id, asset_id_type()).amount.value, 500); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( tapos ) { try { fc::temp_directory dir1( graphene::utilities::temp_directory_path() ); database db1; db1.open(dir1.path(), make_genesis); const account_object& init1 = *db1.get_index_type().indices().get().find("init1"); 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(); const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); auto b = db1.generate_block( db1.get_slot_time(1), db1.get_scheduled_witness( 1 ), init_account_priv_key, database::skip_nothing); signed_transaction trx; //This transaction must be in the next block after its reference, or it is invalid. trx.set_expiration( db1.head_block_time() ); //db1.get_slot_time(1) ); trx.set_reference_block( db1.head_block_id() ); account_id_type nathan_id = account_idx.get_next_id(); account_create_operation cop; cop.registrar = init1.id; cop.name = "nathan"; cop.owner = authority(1, init_account_pub_key, 1); cop.active = cop.owner; trx.operations.push_back(cop); trx.sign( init_account_priv_key, db1.get_chain_id() ); db1.push_transaction(trx); b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); trx.clear(); transfer_operation t; t.to = nathan_id; t.amount = asset(50); trx.operations.push_back(t); trx.sign( init_account_priv_key, db1.get_chain_id() ); //relative_expiration is 1, but ref block is 2 blocks old, so this should fail. GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ), fc::exception); set_expiration( db1, trx ); trx.signatures.clear(); trx.sign( init_account_priv_key, db1.get_chain_id() ); db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_FIXTURE_TEST_CASE( optional_tapos, database_fixture ) { try { ACTORS( (alice)(bob) ); generate_block(); BOOST_TEST_MESSAGE( "Create transaction" ); transfer( account_id_type(), alice_id, asset( 1000000 ) ); transfer_operation op; op.from = alice_id; op.to = bob_id; op.amount = asset( 1000 ); signed_transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); BOOST_TEST_MESSAGE( "ref_block_num=0, ref_block_prefix=0" ); tx.ref_block_num = 0; tx.ref_block_prefix = 0; tx.signatures.clear(); sign( tx, alice_private_key ); PUSH_TX( db, tx ); BOOST_TEST_MESSAGE( "proper ref_block_num, ref_block_prefix" ); set_expiration( db, tx ); tx.signatures.clear(); sign( tx, alice_private_key ); PUSH_TX( db, tx ); BOOST_TEST_MESSAGE( "ref_block_num=0, ref_block_prefix=12345678" ); tx.ref_block_num = 0; tx.ref_block_prefix = 0x12345678; tx.signatures.clear(); sign( tx, alice_private_key ); GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception ); BOOST_TEST_MESSAGE( "ref_block_num=1, ref_block_prefix=12345678" ); tx.ref_block_num = 1; tx.ref_block_prefix = 0x12345678; tx.signatures.clear(); sign( tx, alice_private_key ); GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception ); BOOST_TEST_MESSAGE( "ref_block_num=9999, ref_block_prefix=12345678" ); tx.ref_block_num = 9999; tx.ref_block_prefix = 0x12345678; tx.signatures.clear(); sign( tx, alice_private_key ); GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception ); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) { try { generate_block(); BOOST_CHECK_EQUAL(db.head_block_num(), 2); fc::time_point_sec maintenence_time = db.get_dynamic_global_properties().next_maintenance_time; BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch()); auto initial_properties = db.get_global_properties(); const account_object& nathan = create_account("nathan"); upgrade_to_lifetime_member(nathan); const committee_member_object nathans_committee_member = create_committee_member(nathan); { account_update_operation op; op.account = nathan.id; op.new_options = nathan.options; op.new_options->votes.insert(nathans_committee_member.vote_id); trx.operations.push_back(op); PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } transfer(account_id_type()(db), nathan, asset(5000)); generate_blocks(maintenence_time - initial_properties.parameters.block_interval); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_transaction_size, initial_properties.parameters.maximum_transaction_size); BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().next_maintenance_time.sec_since_epoch(), db.head_block_time().sec_since_epoch() + db.get_global_properties().parameters.block_interval); BOOST_CHECK(db.get_global_properties().active_witnesses == initial_properties.active_witnesses); BOOST_CHECK(db.get_global_properties().active_committee_members == initial_properties.active_committee_members); generate_block(); auto new_properties = db.get_global_properties(); BOOST_CHECK(new_properties.active_committee_members != initial_properties.active_committee_members); BOOST_CHECK(std::find(new_properties.active_committee_members.begin(), new_properties.active_committee_members.end(), nathans_committee_member.id) != new_properties.active_committee_members.end()); BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().next_maintenance_time.sec_since_epoch(), maintenence_time.sec_since_epoch() + new_properties.parameters.maintenance_interval); maintenence_time = db.get_dynamic_global_properties().next_maintenance_time; BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch()); db.close(); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_FIXTURE_TEST_CASE( limit_order_expiration, database_fixture ) { try { //Get a sane head block time generate_block(); auto* test = &create_bitasset("TEST"); auto* core = &asset_id_type()(db); auto* nathan = &create_account("nathan"); auto* committee = &account_id_type()(db); transfer(*committee, *nathan, core->amount(50000)); BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 50000 ); limit_order_create_operation op; op.seller = nathan->id; op.amount_to_sell = core->amount(500); op.min_to_receive = test->amount(500); op.expiration = db.head_block_time() + fc::seconds(10); trx.operations.push_back(op); auto ptrx = PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); auto ptrx_id = ptrx.operation_results.back().get(); auto limit_index = db.get_index_type().indices(); auto limit_itr = limit_index.begin(); BOOST_REQUIRE( limit_itr != limit_index.end() ); BOOST_REQUIRE( limit_itr->id == ptrx_id ); BOOST_REQUIRE( db.find_object(limit_itr->id) ); BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); auto id = limit_itr->id; generate_blocks(op.expiration, false); test = &get_asset("TEST"); core = &asset_id_type()(db); nathan = &get_account("nathan"); committee = &account_id_type()(db); BOOST_CHECK(db.find_object(id) == nullptr); BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 50000 ); } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture ) { try { generate_block(); const auto& alice = account_id_type()(db); ACTOR(bob); asset amount(1000); set_expiration( db, trx ); transfer_operation t; t.from = alice.id; t.to = bob.id; t.amount = amount; trx.operations.push_back(t); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); db.push_transaction(trx, ~0); trx.operations.clear(); t.from = bob.id; t.to = alice.id; t.amount = amount; trx.operations.push_back(t); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); BOOST_TEST_MESSAGE( "Verify that not-signing causes an exception" ); GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception ); BOOST_TEST_MESSAGE( "Verify that double-signing causes an exception" ); sign( trx, bob_private_key ); sign( trx, bob_private_key ); GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), tx_duplicate_sig ); BOOST_TEST_MESSAGE( "Verify that signing with an extra, unused key fails" ); trx.signatures.pop_back(); sign( trx, generate_private_key("bogus" )); GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), tx_irrelevant_sig ); BOOST_TEST_MESSAGE( "Verify that signing once with the proper key passes" ); trx.signatures.pop_back(); db.push_transaction(trx, 0); sign( trx, bob_private_key ); } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) { try { generate_block(); db.modify(db.get_global_properties(), [](global_property_object& p) { p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds(); }); BOOST_TEST_MESSAGE( "Creating a proposal to change the block_interval to 1 second" ); { proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; committee_member_update_global_parameters_operation uop; uop.new_parameters.block_interval = 1; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); db.push_transaction(trx); } BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { proposal_update_operation uop; uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), get_account("init2").get_id(), get_account("init3").get_id(), get_account("init4").get_id(), get_account("init5").get_id(), get_account("init6").get_id(), get_account("init7").get_id()}; trx.operations.push_back(uop); sign( trx, init_account_priv_key ); /* sign( trx, get_account("init1" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init2" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init3" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init4" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init5" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init6" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init7" ).active.get_keys().front(),init_account_priv_key); */ db.push_transaction(trx); BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); } BOOST_TEST_MESSAGE( "Verifying that the interval didn't change immediately" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.block_interval, 5); auto past_time = db.head_block_time().sec_since_epoch(); generate_block(); BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 5); generate_block(); BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 10); BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); generate_blocks(proposal_id_type()(db).expiration_time + 5); BOOST_TEST_MESSAGE( "Verify that the block interval is still 5 seconds" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.block_interval, 5); BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); // get the maintenance skip slots out of the way BOOST_TEST_MESSAGE( "Verify that the new block interval is 1 second" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.block_interval, 1); past_time = db.head_block_time().sec_since_epoch(); generate_block(); BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 1); generate_block(); BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 2); } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) { try { uint32_t skip_flags = ( database::skip_witness_signature | database::skip_transaction_signatures | database::skip_authority_check ); const asset_object& core = asset_id_type()(db); // Sam is the creator of accounts private_key_type committee_key = init_account_priv_key; private_key_type sam_key = generate_private_key("sam"); account_object sam_account_object = create_account("sam", sam_key); //Get a sane head block time generate_block( skip_flags ); db.modify(db.get_global_properties(), [](global_property_object& p) { p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds(); }); transaction tx; processed_transaction ptx; account_object committee_account_object = committee_account(db); // transfer from committee account to Sam account transfer(committee_account_object, sam_account_object, core.amount(100000)); generate_block(skip_flags); create_account("alice"); generate_block(skip_flags); create_account("bob"); generate_block(skip_flags); db.pop_block(); db.pop_block(); } catch(const fc::exception& e) { edump( (e.to_detail_string()) ); throw; } } BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture ) { try { uint8_t witness_schedule_algorithm = db.get_global_properties().parameters.witness_schedule_algorithm; if (witness_schedule_algorithm != GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) db.modify(db.get_global_properties(), [](global_property_object& p) { p.parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM; }); db.get_near_witness_schedule(); generate_block(); auto near_schedule = db.get_near_witness_schedule(); std::for_each(near_schedule.begin(), near_schedule.end(), [&](witness_id_type id) { generate_block(0); BOOST_CHECK(db.get_dynamic_global_properties().current_witness == id); }); near_schedule = db.get_near_witness_schedule(); generate_block(0, init_account_priv_key, 2); BOOST_CHECK(db.get_dynamic_global_properties().current_witness == near_schedule[2]); near_schedule.erase(near_schedule.begin(), near_schedule.begin() + 3); auto new_schedule = db.get_near_witness_schedule(); new_schedule.erase(new_schedule.end() - 3, new_schedule.end()); BOOST_CHECK(new_schedule == near_schedule); std::for_each(near_schedule.begin(), near_schedule.end(), [&](witness_id_type id) { generate_block(0); //witness_id_type wid = db.get_dynamic_global_properties().current_witness; BOOST_CHECK(db.get_dynamic_global_properties().current_witness == id); }); if (db.get_global_properties().parameters.witness_schedule_algorithm != witness_schedule_algorithm) db.modify(db.get_global_properties(), [&witness_schedule_algorithm](global_property_object& p) { p.parameters.witness_schedule_algorithm = witness_schedule_algorithm; }); } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture ) { try { generate_block(); auto rsf = [&]() -> string { fc::uint128 rsf; if (db.get_global_properties().parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) rsf = db.get(witness_schedule_id_type()).recent_slots_filled; else rsf = db.get_dynamic_global_properties().recent_slots_filled; string result = ""; result.reserve(128); for( int i=0; i<128; i++ ) { result += ((rsf.lo & 1) == 0) ? '0' : '1'; rsf >>= 1; } return result; }; auto pct = []( uint32_t x ) -> uint32_t { return uint64_t( GRAPHENE_100_PERCENT ) * x / 128; }; BOOST_CHECK_EQUAL( rsf(), "1111111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), GRAPHENE_100_PERCENT ); generate_block( ~0, init_account_priv_key, 1 ); BOOST_CHECK_EQUAL( rsf(), "0111111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(127) ); generate_block( ~0, init_account_priv_key, 1 ); BOOST_CHECK_EQUAL( rsf(), "0101111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(126) ); generate_block( ~0, init_account_priv_key, 2 ); BOOST_CHECK_EQUAL( rsf(), "0010101111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(124) ); generate_block( ~0, init_account_priv_key, 3 ); BOOST_CHECK_EQUAL( rsf(), "0001001010111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(121) ); generate_block( ~0, init_account_priv_key, 5 ); BOOST_CHECK_EQUAL( rsf(), "0000010001001010111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(116) ); generate_block( ~0, init_account_priv_key, 8 ); BOOST_CHECK_EQUAL( rsf(), "0000000010000010001001010111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(108) ); generate_block( ~0, init_account_priv_key, 13 ); BOOST_CHECK_EQUAL( rsf(), "0000000000000100000000100000100010010101111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1000000000000010000000010000010001001010111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1100000000000001000000001000001000100101011111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1110000000000000100000000100000100010010101111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1111000000000000010000000010000010001001010111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block( ~0, init_account_priv_key, 64 ); BOOST_CHECK_EQUAL( rsf(), "0000000000000000000000000000000000000000000000000000000000000000" "1111100000000000001000000001000001000100101011111111111111111111" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(31) ); generate_block( ~0, init_account_priv_key, 32 ); BOOST_CHECK_EQUAL( rsf(), "0000000000000000000000000000000010000000000000000000000000000000" "0000000000000000000000000000000001111100000000000001000000001000" ); BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(8) ); } FC_LOG_AND_RETHROW() } // the test written in 2015 should be revised, currently it is not possible to push block to db2 // without skip_witness_signature | skip_witness_schedule_check | skip_authority_check BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) { try { ACTORS( (alice)(bob) ); auto generate_block = [&]( database& d, uint32_t skip ) -> signed_block { return d.generate_block(d.get_slot_time(1), d.get_scheduled_witness(1), init_account_priv_key, skip); }; // tx's created by ACTORS() have bogus authority, so we need to // skip_authority_check in the block where they're included signed_block b1 = generate_block(db, database::skip_authority_check); fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); database db2; db2.open(data_dir2.path(), make_genesis); BOOST_CHECK( db.get_chain_id() == db2.get_chain_id() ); while( db2.head_block_num() < db.head_block_num() ) { optional< signed_block > b = db.fetch_block_by_number( db2.head_block_num()+1 ); db2.push_block(*b, database::skip_witness_signature); } BOOST_CHECK( db2.get( alice_id ).name == "alice" ); BOOST_CHECK( db2.get( bob_id ).name == "bob" ); db2.push_block(generate_block(db, database::skip_nothing)); transfer( account_id_type(), alice_id, asset( 1000 ) ); transfer( account_id_type(), bob_id, asset( 1000 ) ); // need to skip authority check here as well for same reason as above db2.push_block(generate_block(db, database::skip_authority_check), database::skip_authority_check); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db2.get_balance( bob_id, asset_id_type()).amount.value, 1000); auto generate_and_send = [&]( int n ) { for( int i=0; i signed_transaction { signed_transaction tx; transfer_operation xfer_op; xfer_op.from = from; xfer_op.to = to; xfer_op.amount = asset( amount, asset_id_type() ); xfer_op.fee = asset( 0, asset_id_type() ); tx.operations.push_back( xfer_op ); tx.set_expiration( db.head_block_time() + blocks_to_expire * db.get_global_properties().parameters.block_interval ); if( from == alice_id ) sign( tx, alice_private_key ); else sign( tx, bob_private_key ); return tx; }; signed_transaction tx = generate_xfer_tx( alice_id, bob_id, 1000, 2 ); tx.set_expiration( db.head_block_time() + 2 * db.get_global_properties().parameters.block_interval ); tx.signatures.clear(); sign( tx, alice_private_key ); // put the tx in db tx cache PUSH_TX( db, tx ); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 0); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 2000); // generate some blocks with db2, make tx expire in db's cache generate_and_send(3); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000); // generate a block with db and ensure we don't somehow apply it PUSH_BLOCK(db2, generate_block(db, database::skip_nothing)); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000); // now the tricky part... // (A) Bob sends 1000 to Alice // (B) Alice sends 2000 to Bob // (C) Alice sends 500 to Bob // // We push AB, then receive a block containing C. // we need to apply the block, then invalidate B in the cache. // AB results in Alice having 0, Bob having 2000. // C results in Alice having 500, Bob having 1500. // // This needs to occur while switching to a fork. // signed_transaction tx_a = generate_xfer_tx( bob_id, alice_id, 1000, 2 ); signed_transaction tx_b = generate_xfer_tx( alice_id, bob_id, 2000, 10 ); signed_transaction tx_c = generate_xfer_tx( alice_id, bob_id, 500, 10 ); generate_block( db, database::skip_nothing ); PUSH_TX( db, tx_a ); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 2000); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 0); PUSH_TX( db, tx_b ); PUSH_TX( db2, tx_c ); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 0); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 2000); BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db2.get_balance( bob_id, asset_id_type()).amount.value, 1500); // generate enough blocks on db2 to cause db to switch forks generate_and_send(2); // db should invalidate B, but still be applying A, so the states don't agree BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1500); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db2.get_balance( bob_id, asset_id_type()).amount.value, 1500); // This will cause A to expire in db generate_and_send(1); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1500); BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db2.get_balance( bob_id, asset_id_type()).amount.value, 1500); // Make sure we can generate and accept a plain old empty block on top of all this! generate_and_send(1); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( genesis_reserve_ids ) { try { fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); fc::temp_directory data_dir( graphene::utilities::temp_directory_path() ); uint32_t num_special_accounts = 100; uint32_t num_special_assets = 30; database db; db.open( data_dir.path(), [&]() -> genesis_state_type { genesis_state_type genesis_state = make_genesis(); genesis_state_type::initial_asset_type usd; usd.symbol = "USD"; usd.issuer_name = "init0"; usd.description = "federally floated"; usd.precision = 4; usd.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; usd.accumulated_fees = 0; usd.is_bitasset = true; genesis_state.immutable_parameters.num_special_accounts = num_special_accounts; genesis_state.immutable_parameters.num_special_assets = num_special_assets; genesis_state.initial_assets.push_back( usd ); return genesis_state; } ); const auto& acct_idx = db.get_index_type().indices().get(); auto acct_itr = acct_idx.find("init0"); BOOST_REQUIRE( acct_itr != acct_idx.end() ); BOOST_CHECK( acct_itr->id == account_id_type( num_special_accounts ) ); const auto& asset_idx = db.get_index_type().indices().get(); auto asset_itr = asset_idx.find("USD"); BOOST_REQUIRE( asset_itr != asset_idx.end() ); BOOST_CHECK( asset_itr->id == asset_id_type( num_special_assets ) ); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_FIXTURE_TEST_CASE( miss_many_blocks, database_fixture ) { try { generate_block(); generate_block(); generate_block(); // miss 10 maintenance intervals generate_blocks( db.get_dynamic_global_properties().next_maintenance_time + db.get_global_properties().parameters.maintenance_interval * 10, true ); generate_block(); generate_block(); generate_block(); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_SUITE_END()