Skip to content

Commit

Permalink
Merge pull request EOSIO#178 from enumivo/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
Enumivo authored Jun 2, 2018
2 parents c7f037a + 856b9af commit 4c3d0f8
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 10 deletions.
11 changes: 6 additions & 5 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,13 +871,14 @@ struct controller_impl {
if (except) {
elog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));

while (ritr != branches.first.rend() ) {
fork_db.set_validity( *ritr, false );
++ritr;
}
// ritr currently points to the block that threw
// if we mark it invalid it will automatically remove all forks built off it.
fork_db.set_validity( *ritr, false );

// pop all blocks from the bad fork
for( auto itr = (ritr + 1).base(); itr != branches.second.end(); ++itr ) {
// ritr base is a forward itr to the last block successfully applied
auto applied_itr = ritr.base();
for( auto itr = applied_itr; itr != branches.first.end(); ++itr ) {
fork_db.mark_in_current_chain( *itr , false );
pop_block();
}
Expand Down
8 changes: 4 additions & 4 deletions libraries/chain/fork_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,14 @@ namespace enumivo { namespace chain {
my->index.erase(itr);

auto& previdx = my->index.get<by_prev>();
auto previtr = previdx.find(id);
while( previtr != previdx.end() ) {
auto previtr = previdx.lower_bound(remove_queue[i]);
while( previtr != previdx.end() && (*previtr)->header.previous == remove_queue[i] ) {
remove_queue.push_back( (*previtr)->id );
previdx.erase(previtr);
previtr = previdx.find(id);
++previtr;
}
}
//wdump((my->index.size()));
my->head = *my->index.get<by_lib_block_num>().begin();
}

void fork_database::set_validity( const block_state_ptr& h, bool valid ) {
Expand Down
7 changes: 6 additions & 1 deletion libraries/chain/include/enumivo/chain/incremental_merkle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ class incremental_merkle_impl {
:_node_count(0)
{}

template<typename Allocator>
incremental_merkle_impl( const incremental_merkle_impl& ) = default;
incremental_merkle_impl( incremental_merkle_impl&& ) = default;
incremental_merkle_impl& operator= (const incremental_merkle_impl& ) = default;
incremental_merkle_impl& operator= ( incremental_merkle_impl&& ) = default;

template<typename Allocator, std::enable_if_t<!std::is_same<std::decay_t<Allocator>, incremental_merkle_impl>::value, int> = 0>
incremental_merkle_impl( Allocator&& alloc ):_active_nodes(forward<Allocator>(alloc)){}

/*
Expand Down
99 changes: 99 additions & 0 deletions unittests/forked_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,105 @@ BOOST_AUTO_TEST_CASE( irrblock ) try {

} FC_LOG_AND_RETHROW()

struct fork_tracker {
vector<signed_block_ptr> blocks;
incremental_merkle block_merkle;
};

BOOST_AUTO_TEST_CASE( fork_with_bad_block ) try {
tester bios;
bios.produce_block();
bios.produce_block();
bios.create_accounts( {N(a),N(b),N(c),N(d),N(e)} );

bios.produce_block();
auto res = bios.set_producers( {N(a),N(b),N(c),N(d),N(e)} );

// run until the producers are installed and its the start of "a's" round
while( bios.control->pending_block_state()->header.producer.to_string() != "a" || bios.control->head_block_state()->header.producer.to_string() != "e") {
bios.produce_block();
}

// sync remote node
tester remote;
while( remote.control->head_block_num() < bios.control->head_block_num() ) {
auto fb = bios.control->fetch_block_by_number( remote.control->head_block_num()+1 );
remote.push_block( fb );
}

// produce 6 blocks on bios
for (int i = 0; i < 6; i ++) {
bios.produce_block();
BOOST_REQUIRE_EQUAL( bios.control->head_block_state()->header.producer.to_string(), "a" );
}

vector<fork_tracker> forks(7);
// enough to skip A's blocks
auto offset = fc::milliseconds(config::block_interval_ms * 13);

// skip a's blocks on remote
// create 7 forks of 7 blocks so this fork is longer where the ith block is corrupted
for (size_t i = 0; i < 7; i ++) {
auto b = remote.produce_block(offset);
BOOST_REQUIRE_EQUAL( b->producer.to_string(), "b" );

for (size_t j = 0; j < 7; j ++) {
auto& fork = forks.at(j);

if (j <= i) {
auto copy_b = std::make_shared<signed_block>(*b);
if (j == i) {
// corrupt this block
fork.block_merkle = remote.control->head_block_state()->blockroot_merkle;
copy_b->action_mroot._hash[0] ^= 0x1ULL;
} else if (j < i) {
// link to a corrupted chain
copy_b->previous = fork.blocks.back()->id();
}

// re-sign the block
auto header_bmroot = digest_type::hash( std::make_pair( copy_b->digest(), fork.block_merkle.get_root() ) );
auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, remote.control->head_block_state()->pending_schedule_hash) );
copy_b->producer_signature = remote.get_private_key(N(b), "active").sign(sig_digest);

// add this new block to our corrupted block merkle
fork.block_merkle.append(copy_b->id());
fork.blocks.emplace_back(copy_b);
} else {
fork.blocks.emplace_back(b);
}
}

offset = fc::milliseconds(config::block_interval_ms);
}

// go from most corrupted fork to least
for (size_t i = 0; i < forks.size(); i++) {
BOOST_TEST_CONTEXT("Testing Fork: " << i) {
const auto& fork = forks.at(i);
// push the fork to the original node
for (int fidx = 0; fidx < fork.blocks.size() - 1; fidx++) {
const auto& b = fork.blocks.at(fidx);
// push the block only if its not known already
if (!bios.control->fetch_block_by_id(b->id())) {
bios.push_block(b);
}
}

// push the block which should attempt the corrupted fork and fail
BOOST_REQUIRE_THROW(bios.push_block(fork.blocks.back()), fc::exception);
}
}

// make sure we can still produce a blocks until irreversibility moves
auto lib = bios.control->head_block_state()->dpos_irreversible_blocknum;
size_t tries = 0;
while (bios.control->head_block_state()->dpos_irreversible_blocknum == lib && ++tries < 10000) {
bios.produce_block();
}

} FC_LOG_AND_RETHROW();

BOOST_AUTO_TEST_CASE( forking ) try {
tester c;
c.produce_block();
Expand Down

0 comments on commit 4c3d0f8

Please sign in to comment.