diff --git a/libraries/chain/block.cpp b/libraries/chain/block.cpp index 6a7252fe48e587ca5c2d435e81372627708f0505..6704ac14674723eea13e9cd27e7a539e26842ec1 100644 --- a/libraries/chain/block.cpp +++ b/libraries/chain/block.cpp @@ -28,19 +28,26 @@ namespace eosio { namespace chain { return result; } - public_key_type signed_block_header::signee()const + public_key_type signed_block_header::signee( const digest_type& schedule_digest )const { - return fc::crypto::public_key(producer_signature, digest(), true/*enforce canonical*/); + return fc::crypto::public_key(producer_signature, signed_digest( schedule_digest ), true/*enforce canonical*/); } - void signed_block_header::sign(const private_key_type& signer) + digest_type signed_block_header::signed_digest( const digest_type& schedule_digest )const { + schedule_digest::encoder enc; + fc::raw::pack( enc, schedule_digest ); + fc::raw::pack( enc, digest() ); + return enc.result(); + } + + void signed_block_header::sign(const private_key_type& signer, const fc::sha256& schedule_digest ) { - producer_signature = signer.sign(digest()); + producer_signature = signer.sign( signed_digest( schedule_digest ) ); } - bool signed_block_header::validate_signee(const public_key_type& expected_signee)const + bool signed_block_header::validate_signee(const public_key_type& expected_signee, const fc::sha256& schedule_digest )const { - return signee() == expected_signee; + return signee( schedule_digest ) == expected_signee; } } } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 1d4824ef1c4dfee66a4a3e374d0351088cc5f803..9f64a8c27fc82abc6a2d989205bd56c861caef04 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -35,23 +35,83 @@ void fork_database::start_block(signed_block b) * Pushes the block into the fork database and caches it if it doesn't link * */ -shared_ptr fork_database::push_block(const signed_block& b, const producer_schedule_type& sch ) +shared_ptr fork_database::push_block( const signed_block& b ) { - auto item = std::make_shared(b); - try { - _push_block(item, sch); - } - catch ( const unlinkable_block_exception& e ) - { + auto item = push_block_header( b ); + item->data = std::make_shared(b); + return item; + +shared_ptr fork_database::push_block_header( const signed_block_header& b ) { try { + const auto& by_id_idx = _index.get(); + auto existing = by_id_idx.find( b.id() ); + + if( existing != by_id_idx.end() ) return *existing; + + auto previous = by_id_idx.find( b.previous ); + if( previous == by_id_idx.end() ) { wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) ); wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) ); - throw; - _unlinked_index.insert( item ); + EOS_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain"); + } + + FC_ASSERT( !previous->invalid, unlinkable_block_exception, "unable to link to a known invalid block" ); + + auto next = std::make_shared( *previous ); + next->confirmations.clear(); + next->data.reset(); + + next->previous = previous; + next->header = b; + next->id = b.id(); + next->invalid = false; + next->num = previous->num + 1; + next->last_block_per_producer[b.producer] = next->num; + + FC_ASSERT( b.timestamp > previous->header.timestamp, "block must advance time" ); + + // next->last_irreversible_block = next->calculate_last_irr + + if( next->last_irreversible_block >= next->pending_schedule_block ) + next->active_schedule = std::move(next->pending_schedule ); + + if( b.new_producers ) { + FC_ASSERT( !next->pending_schedule, "there is already an unconfirmed pending schedule" ); + next->pending_schedule = std::make_shared( *b.new_producers ); } + + FC_ASSERT( b.schedule_version == next->active_schedule->version, "wrong schedule version provided" ); + + auto schedule_hash = fc::sha256::hash( *next->active_schedule ); + auto signee = b.signee( schedule_digest ); + + auto num_producers = next->active_schedule->producers.size(); + vector ibb; + flat_map new_block_per_producer; + ibb.reserve( num_producers ); + + size_t offset = EOS_PERCENT(ibb.size(), config::percent_100- config::irreversible_threshold_percent); + + for( const auto& item : next->active_schedule->producers ) { + ibb.push_back( last_block_per_producer[item.producer_name] ); + new_block_per_producer[item.producer_name] = ibb.back(); + } + next->last_block_per_producer = move(new_block_per_producer); + + std::nth_element( ibb.begin(), ibb.begin() + offset, ibb.end() ); + next->last_irreversible_block = ibb[offset]; + + auto index = b.timestamp.slot % (num_producers * config::producer_repetitions); + index /= config::producer_repetitions; + + auto prod = next->active_schedule->producers[index]; + FC_ASSERT( prod.producer_name == b.producer, "unexpected producer specified" ); + FC_ASSERT( prod.block_signing_key == signee, "block not signed by expected key" ); + + _push_block( next ); return _head; -} +} FC_CAPTURE_AND_RETHROW( (b) ) } -void fork_database::_push_block(const item_ptr& item, const producer_schedule_type& sch) +void fork_database::_push_block(const item_ptr& item ) { if( _head ) // make sure the block is within the range that we are caching { @@ -67,10 +127,6 @@ void fork_database::_push_block(const item_ptr& item, const producer_schedule_t EOS_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain"); FC_ASSERT(!(*itr)->invalid); - if( (*itr)->schedule && *(*itr)->schedule == sch ) - item->schedule = (*itr)->schedule; - else - item->schedule = std::make_shared( sch ); item->prev = *itr; } @@ -82,13 +138,12 @@ void fork_database::_push_block(const item_ptr& item, const producer_schedule_t if (delta > 1) wlog("Number of missed blocks: ${num}", ("num", delta-1)); _head = item; - uint32_t min_num = _head->num - std::min( _max_size, _head->num ); + + uint32_t min_num = _head->last_irreversible_block - 1; //_head->num - std::min( _max_size, _head->num ); // ilog( "min block in fork DB ${n}, max_size: ${m}", ("n",min_num)("m",_max_size) ); auto& num_idx = _index.get(); while( num_idx.size() && (*num_idx.begin())->num < min_num ) num_idx.erase( num_idx.begin() ); - - _unlinked_index.get().erase(_head->num - _max_size); } } diff --git a/libraries/chain/include/eosio/chain/block.hpp b/libraries/chain/include/eosio/chain/block.hpp index 8cd087b7d00a890e7447a4e9953a56a23f3512e3..26c91c49f8e87830931c9c346bddadf688999e3b 100644 --- a/libraries/chain/include/eosio/chain/block.hpp +++ b/libraries/chain/include/eosio/chain/block.hpp @@ -46,9 +46,10 @@ namespace eosio { namespace chain { struct signed_block_header : public block_header { block_id_type id() const; - public_key_type signee() const; - void sign(const private_key_type& signer); - bool validate_signee(const public_key_type& expected_signee) const; + public_key_type signee( const digest_type& schedule_digest ) const; + void sign(const private_key_type& signer, const digest_type& schedule_digest ); + bool validate_signee(const public_key_type& expected_signee, const digest_type& schedule_digest ) const; + digest_type signed_digest( const digest_type& schedule_digest )const; signature_type producer_signature; }; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 312b0ccc1a1c4511915808635aa2e82da2879e07..a15c272db0fb4176b72f8365e4756707f5002a72 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -28,18 +28,26 @@ namespace eosio { namespace chain { return (schedule->producers.size() * 2) / 3 < confirmations.size(); } - weak_ptr< fork_item > prev; - shared_ptr< producer_schedule_type > schedule; - uint32_t num; // initialized in ctor + weak_ptr< fork_item > prev; + shared_ptr< producer_schedule_type > active_schedule; + shared_ptr< producer_schedule_type > pending_schedule; + uint32_t pending_schedule_block; + uint32_t last_irreversible_block = 0; + vector last_block_per_producer; + + + uint32_t num; // initialized in ctor /** * Used to flag a block as invalid and prevent other blocks from * building on top of it. */ bool invalid = false; block_id_type id; - signed_block data; + block_header header; + shared_ptr data; vector confirmations; }; + typedef shared_ptr item_ptr; @@ -79,7 +87,9 @@ namespace eosio { namespace chain { /** * @return the new head block ( the longest fork ) */ - shared_ptr push_block(const signed_block& b, const producer_schedule_type& s = producer_schedule_type() ); + shared_ptr push_block( const signed_block& b ); + shared_ptr push_block_header( const signed_block_header& b ); + shared_ptr head()const { return _head; } void pop_block(); @@ -90,15 +100,15 @@ namespace eosio { namespace chain { pair< branch_type, branch_type > fetch_branch_from(block_id_type first, block_id_type second)const; - struct block_id; - struct block_num; + struct by_block_id; + struct by_block_num; struct by_previous; typedef multi_index_container< item_ptr, indexed_by< - hashed_unique, member, std::hash>, + hashed_unique, member, std::hash>, hashed_non_unique, const_mem_fun, std::hash>, - ordered_non_unique, member> + ordered_non_unique, member> > > fork_multi_index_type; @@ -110,7 +120,6 @@ namespace eosio { namespace chain { uint32_t _max_size = 1024; - fork_multi_index_type _unlinked_index; fork_multi_index_type _index; shared_ptr _head; };