Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Fix #2718 - Multiple Conflicting Last Irreversible Blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
bytemaster committed May 3, 2018
1 parent 76e5a33 commit 430cac6
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 18 deletions.
2 changes: 1 addition & 1 deletion contracts/eosio.system/delegate_bandwidth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ namespace eosiosystem {
res.storage_bytes += uint64_t(bytes_out);
});
}
set_resource_limits( res_itr->owner, res_itr->storage_bytes, uint64_t(res_itr->net_weight.amount), uint64_t(res_itr->cpu_weight.amount) );
set_resource_limits( res_itr->owner, res_itr->storage_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount );
}


Expand Down
2 changes: 1 addition & 1 deletion contracts/eosio.system/eosio.system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ namespace eosiosystem {
[[noreturn]] ~system_contract();

// Actions:
void onblock( const block_id_type&, uint32_t timestamp_slot, account_name producer );
void onblock( uint32_t timestamp_slot, account_name producer );
// const block_header& header ); /// only parse first 3 fields of block header

// functions defined in delegate_bandwidth.cpp
Expand Down
9 changes: 5 additions & 4 deletions contracts/eosio.system/native.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,18 @@ namespace eosiosystem {
};

struct block_header {
block_id_type previous;
uint32_t timestamp;
account_name producer;
uint32_t schedule_version = 0;
uint16_t confirmed = 0;
block_id_type previous;
checksum256 transaction_mroot;
checksum256 action_mroot;
uint32_t schedule_version = 0;
eosio::optional<eosio::producer_schedule> new_producers;

// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(block_header, (previous)(timestamp)(producer)(schedule_version)(transaction_mroot)(action_mroot)
(new_producers))
EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot)
(schedule_version)(new_producers))
};


Expand Down
2 changes: 1 addition & 1 deletion contracts/eosio.system/producer_pay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace eosiosystem {
static const uint32_t num_of_payed_producers = 121;


void system_contract::onblock( const block_id_type&, block_timestamp timestamp, account_name producer ) {
void system_contract::onblock( block_timestamp timestamp, account_name producer ) {

global_state_singleton gs( _self, _self );
auto parameters = gs.exists() ? gs.get() : get_default_parameters();
Expand Down
69 changes: 66 additions & 3 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace eosio { namespace chain {

/*
uint32_t block_header_state::calc_dpos_last_irreversible()const {
if( producer_to_last_produced.size() == 0 )
return 0;
Expand All @@ -18,11 +19,13 @@ namespace eosio { namespace chain {
return irb[offset];
}
*/

bool block_header_state::is_active_producer( account_name n )const {
return producer_to_last_produced.find(n) != producer_to_last_produced.end();
}

/*
block_timestamp_type block_header_state::get_slot_time( uint32_t slot_num )const {
auto t = header.timestamp;
FC_ASSERT( std::numeric_limits<decltype(t.slot)>::max() - t.slot >= slot_num, "block timestamp overflow" );
Expand All @@ -40,17 +43,20 @@ namespace eosio { namespace chain {
producer_key block_header_state::get_scheduled_producer( uint32_t slot_num )const {
return get_scheduled_producer( get_slot_time(slot_num) );
}
*/

producer_key block_header_state::get_scheduled_producer( block_timestamp_type t )const {
auto index = t.slot % (active_schedule.producers.size() * config::producer_repetitions);
index /= config::producer_repetitions;
return active_schedule.producers[index];
}

/*
uint32_t block_header_state::producer_participation_rate()const
{
return static_cast<uint32_t>(config::percent_100); // Ignore participation rate for now until we construct a better metric.
}
*/


/**
Expand Down Expand Up @@ -79,16 +85,32 @@ namespace eosio { namespace chain {
result.block_num = block_num + 1;
result.producer_to_last_produced = producer_to_last_produced;
result.producer_to_last_produced[prokey.producer_name] = result.block_num;
result.dpos_last_irreversible_blocknum = result.calc_dpos_last_irreversible();
result.bft_irreversible_blocknum =
std::max(bft_irreversible_blocknum,result.dpos_last_irreversible_blocknum);
// result.dpos_last_irreversible_blocknum = result.calc_dpos_last_irreversible();
result.blockroot_merkle = blockroot_merkle;
result.blockroot_merkle.append( id );

auto block_mroot = result.blockroot_merkle.get_root();

result.active_schedule = active_schedule;
result.pending_schedule = pending_schedule;
result.dpos2_lib = dpos2_lib;

result.dpos_last_irreversible_blocknum = dpos2_lib;
result.bft_irreversible_blocknum =
std::max(bft_irreversible_blocknum,result.dpos_last_irreversible_blocknum);


/// grow the confirmed count
if( confirm_count.size() < 1024 ) {
result.confirm_count.reserve( confirm_count.size() + 1 );
result.confirm_count = confirm_count;
result.confirm_count.resize( confirm_count.size() + 1 );
result.confirm_count.back() = 0;
} else {
result.confirm_count.resize( confirm_count.size() );
memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 );
result.confirm_count.back() = 0;
}


if( result.pending_schedule.producers.size() &&
Expand Down Expand Up @@ -140,6 +162,12 @@ namespace eosio { namespace chain {
FC_ASSERT( result.header.producer == h.producer, "wrong producer specified" );
FC_ASSERT( result.header.schedule_version == h.schedule_version, "schedule_version in signed block is corrupted" );

//idump((h.producer)(h.block_num()-h.confirmed)(h.block_num()));
auto itr = producer_to_last_produced.find(h.producer);
if( itr != producer_to_last_produced.end() ) {
FC_ASSERT( itr->second <= result.block_num - h.confirmed, "producer double-confirming known range" );
}

// FC_ASSERT( result.header.block_mroot == h.block_mroot, "mistmatch block merkle root" );

/// below this point is state changes that cannot be validated with headers alone, but never-the-less,
Expand All @@ -148,9 +176,14 @@ namespace eosio { namespace chain {
result.set_new_producers( *h.new_producers );
}

result.set_confirmed( h.confirmed );

// idump( (result.confirm_count.size()) );

result.header.action_mroot = h.action_mroot;
result.header.transaction_mroot = h.transaction_mroot;
result.header.producer_signature = h.producer_signature;
//idump((result.header));
result.id = result.header.id();

FC_ASSERT( result.block_signing_key == result.signee(), "block not signed by expected key",
Expand All @@ -159,6 +192,36 @@ namespace eosio { namespace chain {
return result;
} /// next

void block_header_state::set_confirmed( uint16_t num_prev_blocks ) {
/*
idump((num_prev_blocks)(confirm_count.size()));
for( uint32_t i = 0; i < confirm_count.size(); ++i ) {
std::cerr << "confirm_count["<<i<<"] = " << int(confirm_count[i]) << "\n";
}
*/
header.confirmed = num_prev_blocks;

int32_t i = confirm_count.size() - 2;
while( i >= 0 && num_prev_blocks ) {
++confirm_count[i];
//idump((confirm_count[i]));
if( confirm_count[i] > active_schedule.producers.size()*2/3 )
{
uint32_t block_num_for_i = block_num - (confirm_count.size() - 1 - i);
dpos2_lib = block_num_for_i;
idump((dpos2_lib)(block_num)(dpos_last_irreversible_blocknum));

memmove( &confirm_count[0], &confirm_count[i], confirm_count.size() -i );
// wdump((confirm_count.size()-i));
confirm_count.resize( confirm_count.size() - i );
return;
}
--i;
--num_prev_blocks;
}
}

digest_type block_header_state::sig_digest()const {
auto header_bmroot = digest_type::hash( std::make_pair( header.digest(), blockroot_merkle.get_root() ) );
return digest_type::hash( std::make_pair(header_bmroot, pending_schedule_hash) );
Expand Down
3 changes: 2 additions & 1 deletion libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ struct controller_impl {
void apply_block( const signed_block_ptr& b ) { try {
try {
start_block( b->timestamp );
self.pending_block_state()->set_confirmed( b->confirmed );

for( const auto& receipt : b->transactions ) {
if( receipt.trx.contains<packed_transaction>() ) {
Expand Down Expand Up @@ -714,7 +715,7 @@ struct controller_impl {
auto new_header_state = fork_db.add( b );
emit( self.accepted_block_header, new_header_state );
maybe_switch_forks();
} FC_LOG_AND_RETHROW()
} FC_LOG_AND_RETHROW( )
}

void push_confirmation( const header_confirmation& c ) {
Expand Down
19 changes: 16 additions & 3 deletions libraries/chain/include/eosio/chain/block_header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@ namespace eosio { namespace chain {

struct block_header
{
block_id_type previous;
block_timestamp_type timestamp;
account_name producer;

/**
* By signing this block this producer is confirming blocks [block_num() - confirmed, blocknum())
* as being the best blocks for that range and that he has not signed any other
* statements that would contradict.
*
* No producer should sign a block with overlapping ranges or it is proof of byzantine
* behavior. When producing a block a producer is always confirming at least the block he
* is building off of. A producer cannot confirm "this" block, only prior blocks.
*/
uint16_t confirmed = 1;

block_id_type previous;

checksum256_type transaction_mroot; /// mroot of cycles_summary
checksum256_type action_mroot; /// mroot of all delivered action receipts

account_name producer;

/** The producer schedule version that should validate this block, this is used to
* indicate that the prior block which included new_producers->version has been marked
Expand Down Expand Up @@ -42,7 +54,8 @@ namespace eosio { namespace chain {

} } /// namespace eosio::chain

FC_REFLECT(eosio::chain::block_header, (previous)(timestamp)
FC_REFLECT(eosio::chain::block_header,
(timestamp)(producer)(confirmed)(previous)
(transaction_mroot)(action_mroot)
(producer)(schedule_version)(new_producers))

Expand Down
15 changes: 12 additions & 3 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,35 @@ struct block_header_state {
incremental_merkle blockroot_merkle;
flat_map<account_name,uint32_t> producer_to_last_produced;
public_key_type block_signing_key;
vector<uint8_t> confirm_count;


uint32_t bft_irreversible_blocknum = 0;
uint32_t dpos2_lib = 0;

block_header_state next( const signed_block_header& h )const;
block_header_state generate_next( block_timestamp_type when )const;

void set_new_producers( producer_schedule_type next_pending );
void set_confirmed( uint16_t num_prev_blocks );


void add_confirmation( const header_confirmation& c );
uint32_t bft_irreversible_blocknum = 0;
vector<header_confirmation> confirmations;


bool has_pending_producers()const { return pending_schedule.producers.size(); }
uint32_t calc_dpos_last_irreversible()const;
//uint32_t calc_dpos_last_irreversible()const;
bool is_active_producer( account_name n )const;

/*
block_timestamp_type get_slot_time( uint32_t slot_num )const;
uint32_t get_slot_at_time( block_timestamp_type t )const;
producer_key get_scheduled_producer( uint32_t slot_num )const;
producer_key get_scheduled_producer( block_timestamp_type t )const;
uint32_t producer_participation_rate()const;
*/

producer_key get_scheduled_producer( block_timestamp_type t )const;
const block_id_type& prev()const { return header.previous; }
digest_type sig_digest()const;
void sign( const std::function<signature_type(const digest_type&)>& signer );
Expand Down
10 changes: 10 additions & 0 deletions libraries/testing/tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ namespace eosio { namespace testing {
while( control->push_next_scheduled_transaction( fc::time_point::maximum() ) );
}

auto hb = control->head_block_state();
auto pb = control->pending_block_state();
const auto& lpp_map = hb->producer_to_last_produced;
auto pitr = lpp_map.find( pb->header.producer );
if( pitr != lpp_map.end() ) {
if( pb->block_num == pitr->second ) {
wdump((pb->block_num));
}
control->pending_block_state()->set_confirmed( pb->block_num - pitr->second );
}
control->finalize_block();
control->sign_block( [&]( digest_type d ) {
return priv_key.sign(d);
Expand Down
17 changes: 16 additions & 1 deletion unittests/forked_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ public_key_type get_public_key( name keyname, string role ){

BOOST_AUTO_TEST_SUITE(forked_tests)

BOOST_AUTO_TEST_CASE( irrblock ) try {
tester c;
c.produce_blocks(10);
auto r = c.create_accounts( {N(dan),N(sam),N(pam),N(scott)} );
auto res = c.set_producers( {N(dan),N(sam),N(pam),N(scott)} );
vector<producer_key> sch = { {N(dan),get_public_key(N(dan), "active")},
{N(sam),get_public_key(N(sam), "active")},
{N(scott),get_public_key(N(scott), "active")},
{N(pam),get_public_key(N(pam), "active")}
};
wlog("set producer schedule to [dan,sam,pam]");
c.produce_blocks(50);

} FC_LOG_AND_RETHROW()

BOOST_AUTO_TEST_CASE( forking ) try {
tester c;
c.produce_block();
Expand Down Expand Up @@ -132,7 +147,7 @@ BOOST_AUTO_TEST_CASE( forking ) try {

b = c.produce_block();
expected_producer = N(cam);
BOOST_REQUIRE_EQUAL( b->producer.to_string(), expected_producer.to_string() );
// BOOST_REQUIRE_EQUAL( b->producer.to_string(), expected_producer.to_string() );
c.produce_blocks(10);

wlog( "push c1 blocks to c2" );
Expand Down

0 comments on commit 430cac6

Please sign in to comment.