From d558816027890f399af66e3b0b088b1837a39d79 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 17 Feb 2024 09:42:34 -0600 Subject: [PATCH 1/5] GH-2142 Sign proposed blocks with producer signature and verify signed blocks. Also validate new protocol features. --- libraries/chain/block_header_state.cpp | 10 +-- libraries/chain/block_state.cpp | 71 ++++++++++++++++++- libraries/chain/controller.cpp | 2 +- .../eosio/chain/block_header_state.hpp | 11 +-- .../chain/include/eosio/chain/block_state.hpp | 8 ++- 5 files changed, 84 insertions(+), 18 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index f1a257a67a..30cde09eb5 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -71,7 +71,8 @@ block_header_state block_header_state::next(block_header_state_input& input) con // header // ------ - result.header = block_header { + result.header = signed_block_header { + block_header { .timestamp = input.timestamp, // [greg todo] do we have to do the slot++ stuff from the legacy version? .producer = input.producer, .confirmed = 0, @@ -79,7 +80,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con .transaction_mroot = input.transaction_mroot, .action_mroot = input.action_mroot, .schedule_version = header.schedule_version - }; + }}; // activated protocol features // --------------------------- @@ -180,13 +181,13 @@ block_header_state block_header_state::next(block_header_state_input& input) con * * If the header specifies new_producers then apply them accordingly. */ -block_header_state block_header_state::next(const signed_block_header& h, const protocol_feature_set& pfs, - validator_t& validator) const { +block_header_state block_header_state::next(const signed_block_header& h, validator_t& validator) const { auto producer = detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, h.timestamp).producer_name; EOS_ASSERT( h.previous == block_id, unlinkable_block_exception, "previous mismatch" ); EOS_ASSERT( h.producer == producer, wrong_producer, "wrong producer specified" ); EOS_ASSERT( h.confirmed == 0, block_validate_exception, "invalid confirmed ${c}", ("c", h.confirmed) ); + EOS_ASSERT( !h.new_producers, producer_schedule_exception, "Block header contains legacy producer schedule outdated by activation of WTMsig Block Signatures" ); auto exts = h.validate_and_extract_header_extensions(); @@ -197,6 +198,7 @@ block_header_state block_header_state::next(const signed_block_header& h, const auto pfa_entry = exts.lower_bound(protocol_feature_activation::extension_id()); auto& pfa_ext = std::get(pfa_entry->second); new_protocol_feature_activations = std::move(pfa_ext.protocol_features); + validator( timestamp(), activated_protocol_features->protocol_features, new_protocol_feature_activations ); } // retrieve instant_finality_extension data from block header extension diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 13c8979036..c2f0968c5c 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -10,15 +10,22 @@ namespace eosio::chain { block_state::block_state(const block_header_state& prev, signed_block_ptr b, const protocol_feature_set& pfs, const validator_t& validator, bool skip_validate_signee) - : block_header_state(prev.next(*b, pfs, validator)) + : block_header_state(prev.next(*b, validator)) , block(std::move(b)) , strong_digest(compute_finalizer_digest()) , weak_digest(create_weak_digest(strong_digest)) , pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->threshold, prev.active_finalizer_policy->max_weak_sum_before_weak_final()) -{} +{ + // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here + if( !skip_validate_signee ) { + auto sigs = detail::extract_additional_signatures(b); + verify_signee(sigs); + } +} block_state::block_state(const block_header_state& bhs, deque&& trx_metas, - deque&& trx_receipts, const std::optional& qc) + deque&& trx_receipts, const std::optional& qc, + const signer_callback_type& signer) : block_header_state(bhs) , block(std::make_shared(signed_block_header{bhs.header})) // [greg todo] do we need signatures? , strong_digest(compute_finalizer_digest()) @@ -32,6 +39,8 @@ block_state::block_state(const block_header_state& bhs, dequeblock_extensions, quorum_certificate_extension::extension_id(), fc::raw::pack( *qc )); } + + sign(signer); } // Used for transition from dpos to instant-finality @@ -193,4 +202,60 @@ std::optional block_state::get_best_qc() const { return quorum_certificate{ block_num(), best_qc }; } +void inject_additional_signatures( signed_block& b, const std::vector& additional_signatures) +{ + if (!additional_signatures.empty()) { + // as an optimization we don't copy this out into the legitimate extension structure as it serializes + // the same way as the vector of signatures + static_assert(fc::reflector::total_member_count == 1); + static_assert(std::is_same_v>); + + emplace_extension(b.block_extensions, additional_block_signatures_extension::extension_id(), fc::raw::pack( additional_signatures )); + } +} + +void block_state::sign( const signer_callback_type& signer ) { + auto sigs = signer( block_id ); + + EOS_ASSERT(!sigs.empty(), no_block_signatures, "Signer returned no signatures"); + header.producer_signature = sigs.back(); + sigs.pop_back(); + + verify_signee(sigs); + inject_additional_signatures(*block, sigs); +} + +void block_state::verify_signee(const std::vector& additional_signatures) const { + auto valid_block_signing_authority = get_scheduled_producer(timestamp()).authority; + + auto num_keys_in_authority = std::visit([](const auto &a){ return a.keys.size(); }, valid_block_signing_authority); + EOS_ASSERT(1 + additional_signatures.size() <= num_keys_in_authority, wrong_signing_key, + "number of block signatures (${num_block_signatures}) exceeds number of keys in block signing authority (${num_keys})", + ("num_block_signatures", 1 + additional_signatures.size()) + ("num_keys", num_keys_in_authority) + ("authority", valid_block_signing_authority) + ); + + std::set keys; + keys.emplace(fc::crypto::public_key( header.producer_signature, block_id, true )); + + for (const auto& s: additional_signatures) { + auto res = keys.emplace(s, block_id, true); + EOS_ASSERT(res.second, wrong_signing_key, "block signed by same key twice", ("key", *res.first)); + } + + bool is_satisfied = false; + size_t relevant_sig_count = 0; + + std::tie(is_satisfied, relevant_sig_count) = producer_authority::keys_satisfy_and_relevant(keys, valid_block_signing_authority); + + EOS_ASSERT(relevant_sig_count == keys.size(), wrong_signing_key, + "block signed by unexpected key", + ("signing_keys", keys)("authority", valid_block_signing_authority)); + + EOS_ASSERT(is_satisfied, wrong_signing_key, + "block signatures do not satisfy the block signing authority", + ("signing_keys", keys)("authority", valid_block_signing_authority)); +} + } /// eosio::chain diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 6b233f7d55..5722ec37d4 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -332,7 +332,7 @@ struct assembled_block { }, [&](assembled_block_if& ab) { auto bsp = std::make_shared(ab.bhs, std::move(ab.trx_metas), - std::move(ab.trx_receipts), ab.qc); + std::move(ab.trx_receipts), ab.qc, signer); return completed_block{std::move(bsp)}; }}, v); diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 5fb0d4ef45..d71265ae3b 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -42,7 +42,7 @@ struct block_header_state_core { struct block_header_state { // ------ data members ------------------------------------------------------------ block_id_type block_id; - block_header header; + signed_block_header header; protocol_feature_activation_set_ptr activated_protocol_features; block_header_state_core core; @@ -73,8 +73,7 @@ struct block_header_state { const producer_authority_schedule& active_schedule_auth() const { return active_proposer_policy->proposer_schedule; } block_header_state next(block_header_state_input& data) const; - - block_header_state next(const signed_block_header& h, const protocol_feature_set& pfs, validator_t& validator) const; + block_header_state next(const signed_block_header& h, validator_t& validator) const; // block descending from this need the provided qc in the block extension bool is_needed(const quorum_certificate& qc) const { @@ -84,12 +83,6 @@ struct block_header_state { flat_set get_activated_protocol_features() const { return activated_protocol_features->protocol_features; } const vector& get_new_protocol_feature_activations() const; producer_authority get_scheduled_producer(block_timestamp_type t) const; - uint32_t active_schedule_version() const; - signed_block_header make_block_header(const checksum256_type& transaction_mroot, - const checksum256_type& action_mroot, - const std::optional& new_producers, - vector&& new_protocol_feature_activations, - const protocol_feature_set& pfs) const; }; using block_header_state_ptr = std::shared_ptr; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index c628741fae..bb462fdb1a 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -7,6 +7,8 @@ namespace eosio::chain { +using signer_callback_type = std::function(const digest_type&)>; + constexpr std::array weak_bls_sig_postfix = { 'W', 'E', 'A', 'K' }; using weak_digest_t = std::array; @@ -63,9 +65,13 @@ struct block_state : public block_header_state { // block_header_state provi const validator_t& validator, bool skip_validate_signee); block_state(const block_header_state& bhs, deque&& trx_metas, - deque&& trx_receipts, const std::optional& qc); + deque&& trx_receipts, const std::optional& qc, + const signer_callback_type& signer); explicit block_state(const block_state_legacy& bsp); + + void sign(const signer_callback_type& signer); + void verify_signee(const std::vector& additional_signatures) const; }; using block_state_ptr = std::shared_ptr; From 89c65ab2ecd7f18cde5375aa6588d1109e996fcb Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 17 Feb 2024 10:22:42 -0600 Subject: [PATCH 2/5] GH-2142 Sign the correct block and store in the correct signed_block_header --- libraries/chain/block_header_state.cpp | 5 ++--- libraries/chain/block_state.cpp | 6 +++--- libraries/chain/include/eosio/chain/block_header_state.hpp | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 30cde09eb5..4209e6ac20 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -71,8 +71,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con // header // ------ - result.header = signed_block_header { - block_header { + result.header = { .timestamp = input.timestamp, // [greg todo] do we have to do the slot++ stuff from the legacy version? .producer = input.producer, .confirmed = 0, @@ -80,7 +79,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con .transaction_mroot = input.transaction_mroot, .action_mroot = input.action_mroot, .schedule_version = header.schedule_version - }}; + }; // activated protocol features // --------------------------- diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index c2f0968c5c..35c77e78d1 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -18,7 +18,7 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con { // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here if( !skip_validate_signee ) { - auto sigs = detail::extract_additional_signatures(b); + auto sigs = detail::extract_additional_signatures(block); verify_signee(sigs); } } @@ -218,7 +218,7 @@ void block_state::sign( const signer_callback_type& signer ) { auto sigs = signer( block_id ); EOS_ASSERT(!sigs.empty(), no_block_signatures, "Signer returned no signatures"); - header.producer_signature = sigs.back(); + block->producer_signature = sigs.back(); sigs.pop_back(); verify_signee(sigs); @@ -237,7 +237,7 @@ void block_state::verify_signee(const std::vector& additional_si ); std::set keys; - keys.emplace(fc::crypto::public_key( header.producer_signature, block_id, true )); + keys.emplace(fc::crypto::public_key( block->producer_signature, block_id, true )); for (const auto& s: additional_signatures) { auto res = keys.emplace(s, block_id, true); diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index d71265ae3b..62cba6ee03 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -42,7 +42,7 @@ struct block_header_state_core { struct block_header_state { // ------ data members ------------------------------------------------------------ block_id_type block_id; - signed_block_header header; + block_header header; protocol_feature_activation_set_ptr activated_protocol_features; block_header_state_core core; From 83ec46fb979808a9cf9596a25e3ea7ec57aae001 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 17 Feb 2024 20:21:54 -0600 Subject: [PATCH 3/5] GH-2142 Use the correct producer schedule for finding block signing authorization. --- libraries/chain/block_header_state.cpp | 2 +- libraries/chain/block_header_state_legacy.cpp | 4 ++-- libraries/chain/block_state.cpp | 15 +++++++-------- libraries/chain/controller.cpp | 9 ++++++--- .../include/eosio/chain/block_header_state.hpp | 2 +- .../eosio/chain/block_header_state_legacy.hpp | 2 +- .../eosio/chain/block_header_state_utils.hpp | 2 +- .../chain/include/eosio/chain/block_state.hpp | 6 +++--- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 4209e6ac20..78922138f6 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -12,7 +12,7 @@ namespace eosio::chain { // digest_type compute_finalizer_digest() const { return id; }; -producer_authority block_header_state::get_scheduled_producer(block_timestamp_type t) const { +const producer_authority& block_header_state::get_scheduled_producer(block_timestamp_type t) const { return detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, t); } diff --git a/libraries/chain/block_header_state_legacy.cpp b/libraries/chain/block_header_state_legacy.cpp index 0266ec75f6..958bd4f01e 100644 --- a/libraries/chain/block_header_state_legacy.cpp +++ b/libraries/chain/block_header_state_legacy.cpp @@ -19,7 +19,7 @@ namespace eosio::chain { return blocknums[ index ]; } - producer_authority block_header_state_legacy::get_scheduled_producer( block_timestamp_type t ) const { + const producer_authority& block_header_state_legacy::get_scheduled_producer( block_timestamp_type t ) const { return detail::get_scheduled_producer(active_schedule.producers, t); } @@ -34,7 +34,7 @@ namespace eosio::chain { (when = header.timestamp).slot++; } - auto proauth = get_scheduled_producer(when); + const auto& proauth = get_scheduled_producer(when); auto itr = producer_to_last_produced.find( proauth.producer_name ); if( itr != producer_to_last_produced.end() ) { diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 35c77e78d1..275a8fdd95 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -19,13 +19,14 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here if( !skip_validate_signee ) { auto sigs = detail::extract_additional_signatures(block); - verify_signee(sigs); + const auto& valid_block_signing_authority = prev.get_scheduled_producer(timestamp()).authority; + verify_signee(sigs, valid_block_signing_authority); } } block_state::block_state(const block_header_state& bhs, deque&& trx_metas, deque&& trx_receipts, const std::optional& qc, - const signer_callback_type& signer) + const signer_callback_type& signer, const block_signing_authority& valid_block_signing_authority) : block_header_state(bhs) , block(std::make_shared(signed_block_header{bhs.header})) // [greg todo] do we need signatures? , strong_digest(compute_finalizer_digest()) @@ -40,7 +41,7 @@ block_state::block_state(const block_header_state& bhs, dequeblock_extensions, quorum_certificate_extension::extension_id(), fc::raw::pack( *qc )); } - sign(signer); + sign(signer, valid_block_signing_authority); } // Used for transition from dpos to instant-finality @@ -214,20 +215,18 @@ void inject_additional_signatures( signed_block& b, const std::vectorproducer_signature = sigs.back(); sigs.pop_back(); - verify_signee(sigs); + verify_signee(sigs, valid_block_signing_authority); inject_additional_signatures(*block, sigs); } -void block_state::verify_signee(const std::vector& additional_signatures) const { - auto valid_block_signing_authority = get_scheduled_producer(timestamp()).authority; - +void block_state::verify_signee(const std::vector& additional_signatures, const block_signing_authority& valid_block_signing_authority) const { auto num_keys_in_authority = std::visit([](const auto &a){ return a.keys.size(); }, valid_block_signing_authority); EOS_ASSERT(1 + additional_signatures.size() <= num_keys_in_authority, wrong_signing_key, "number of block signatures (${num_block_signatures}) exceeds number of keys in block signing authority (${num_keys})", diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 5722ec37d4..f04bd31622 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -322,7 +322,7 @@ struct assembled_block { } completed_block complete_block(const protocol_feature_set& pfs, validator_t validator, - const signer_callback_type& signer) { + const signer_callback_type& signer, const block_signing_authority& valid_block_signing_authority) { return std::visit(overloaded{[&](assembled_block_legacy& ab) { auto bsp = std::make_shared( std::move(ab.pending_block_header_state), std::move(ab.unsigned_block), @@ -332,7 +332,8 @@ struct assembled_block { }, [&](assembled_block_if& ab) { auto bsp = std::make_shared(ab.bhs, std::move(ab.trx_metas), - std::move(ab.trx_receipts), ab.qc, signer); + std::move(ab.trx_receipts), ab.qc, signer, + valid_block_signing_authority); return completed_block{std::move(bsp)}; }}, v); @@ -4205,10 +4206,12 @@ void controller::assemble_and_complete_block( block_report& br, const signer_cal my->assemble_block(); auto& ab = std::get(my->pending->_block_stage); + const auto& valid_block_signing_authority = my->head_active_schedule_auth().get_scheduled_producer(ab.timestamp()).authority; my->pending->_block_stage = ab.complete_block( my->protocol_features.get_protocol_feature_set(), [](block_timestamp_type timestamp, const flat_set& cur_features, const vector& new_features) {}, - signer_callback); + signer_callback, + valid_block_signing_authority); br = my->pending->_block_report; } diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 62cba6ee03..dcb8d91366 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -82,7 +82,7 @@ struct block_header_state { flat_set get_activated_protocol_features() const { return activated_protocol_features->protocol_features; } const vector& get_new_protocol_feature_activations() const; - producer_authority get_scheduled_producer(block_timestamp_type t) const; + const producer_authority& get_scheduled_producer(block_timestamp_type t) const; }; using block_header_state_ptr = std::shared_ptr; diff --git a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp index 964ac9ecb6..ee534a4954 100644 --- a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp @@ -170,7 +170,7 @@ struct block_header_state_legacy : public detail::block_header_state_legacy_comm uint32_t calc_dpos_last_irreversible( account_name producer_of_next_block )const; - producer_authority get_scheduled_producer( block_timestamp_type t )const; + const producer_authority& get_scheduled_producer( block_timestamp_type t )const; const block_id_type& previous()const { return header.previous; } digest_type sig_digest()const; void sign( const signer_callback_type& signer ); diff --git a/libraries/chain/include/eosio/chain/block_header_state_utils.hpp b/libraries/chain/include/eosio/chain/block_header_state_utils.hpp index 72c700bca9..1f4deeef65 100644 --- a/libraries/chain/include/eosio/chain/block_header_state_utils.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state_utils.hpp @@ -20,7 +20,7 @@ namespace eosio::chain::detail { return block_timestamp_type{t.slot + (config::producer_repetitions - index) + config::producer_repetitions}; } - inline producer_authority get_scheduled_producer(const vector& producers, block_timestamp_type t) { + inline const producer_authority& get_scheduled_producer(const vector& producers, block_timestamp_type t) { auto index = t.slot % (producers.size() * config::producer_repetitions); index /= config::producer_repetitions; return producers[index]; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index bb462fdb1a..575d419447 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -66,12 +66,12 @@ struct block_state : public block_header_state { // block_header_state provi block_state(const block_header_state& bhs, deque&& trx_metas, deque&& trx_receipts, const std::optional& qc, - const signer_callback_type& signer); + const signer_callback_type& signer, const block_signing_authority& valid_block_signing_authority); explicit block_state(const block_state_legacy& bsp); - void sign(const signer_callback_type& signer); - void verify_signee(const std::vector& additional_signatures) const; + void sign(const signer_callback_type& signer, const block_signing_authority& valid_block_signing_authority); + void verify_signee(const std::vector& additional_signatures, const block_signing_authority& valid_block_signing_authority) const; }; using block_state_ptr = std::shared_ptr; From fa55806131fb2ba3e7fafc96a7d190d8666231ae Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 17 Feb 2024 21:27:17 -0600 Subject: [PATCH 4/5] GH-2142 Add test that protocol feature activation works after instant_finality enabled. --- unittests/protocol_feature_tests.cpp | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/unittests/protocol_feature_tests.cpp b/unittests/protocol_feature_tests.cpp index a013de368d..be69db4bfe 100644 --- a/unittests/protocol_feature_tests.cpp +++ b/unittests/protocol_feature_tests.cpp @@ -1071,6 +1071,106 @@ BOOST_AUTO_TEST_CASE( get_sender_test ) { try { ); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( protocol_activatation_works_after_transition_to_savanna ) { try { + validating_tester c({}, {}, setup_policy::preactivate_feature_and_new_bios ); + + const auto& pfm = c.control->get_protocol_feature_manager(); + const auto& d = pfm.get_builtin_digest(builtin_protocol_feature_t::instant_finality); + // needed for bios contract + const auto& dp = pfm.get_builtin_digest(builtin_protocol_feature_t::bls_primitives); + const auto& dw = pfm.get_builtin_digest(builtin_protocol_feature_t::wtmsig_block_signatures); + const auto& dwk = pfm.get_builtin_digest(builtin_protocol_feature_t::webauthn_key); + c.preactivate_protocol_features( {*d, *dp, *dw, *dwk} ); + c.produce_block(); + + c.set_bios_contract(); + c.produce_block(); + + uint32_t lib = 0; + c.control->irreversible_block().connect([&](const block_signal_params& t) { + const auto& [ block, id ] = t; + lib = block->block_num(); + }); + + c.produce_block(); + + vector accounts = { + "alice"_n, "bob"_n, "carol"_n + }; + + base_tester::finalizer_policy_input policy_input = { + .finalizers = { {.name = "alice"_n, .weight = 1}, + {.name = "bob"_n, .weight = 3}, + {.name = "carol"_n, .weight = 5} }, + .threshold = 5, + .local_finalizers = {"carol"_n} + }; + + // Create finalizer accounts + c.create_accounts(accounts); + c.produce_block(); + + // activate savanna + c.set_finalizers(policy_input); + auto block = c.produce_block(); // this block contains the header extension for the instant finality + + std::optional ext = block->extract_header_extension(instant_finality_extension::extension_id()); + BOOST_TEST(!!ext); + std::optional fin_policy = std::get(*ext).new_finalizer_policy; + BOOST_TEST(!!fin_policy); + BOOST_TEST(fin_policy->finalizers.size() == accounts.size()); + + block = c.produce_block(); // savanna now active + auto fb = c.control->fetch_block_by_id(block->calculate_id()); + BOOST_REQUIRE(!!fb); + BOOST_TEST(fb == block); + ext = fb->extract_header_extension(instant_finality_extension::extension_id()); + BOOST_REQUIRE(ext); + + auto lib_after_transition = lib; + + c.produce_blocks(4); + BOOST_CHECK_GT(lib, lib_after_transition); + + // verify protocol feature activation works under savanna + + const auto& tester1_account = account_name("tester1"); + const auto& tester2_account = account_name("tester2"); + c.create_accounts( {tester1_account, tester2_account} ); + c.produce_block(); + + BOOST_CHECK_EXCEPTION( c.set_code( tester1_account, test_contracts::get_sender_test_wasm() ), + wasm_exception, + fc_exception_message_is( "env.get_sender unresolveable" ) ); + + const auto& d2 = pfm.get_builtin_digest( builtin_protocol_feature_t::get_sender ); + BOOST_REQUIRE( d2 ); + + c.preactivate_protocol_features( {*d2} ); + c.produce_block(); + + c.set_code( tester1_account, test_contracts::get_sender_test_wasm() ); + c.set_abi( tester1_account, test_contracts::get_sender_test_abi() ); + c.set_code( tester2_account, test_contracts::get_sender_test_wasm() ); + c.set_abi( tester2_account, test_contracts::get_sender_test_abi() ); + c.produce_block(); + + BOOST_CHECK_EXCEPTION( c.push_action( tester1_account, "sendinline"_n, tester1_account, mutable_variant_object() + ("to", tester2_account.to_string()) + ("expected_sender", account_name{}) ), + eosio_assert_message_exception, + eosio_assert_message_is( "sender did not match" ) ); + + c.push_action( tester1_account, "sendinline"_n, tester1_account, mutable_variant_object() + ("to", tester2_account.to_string()) + ("expected_sender", tester1_account.to_string()) + ); + + c.push_action( tester1_account, "assertsender"_n, tester1_account, mutable_variant_object() + ("expected_sender", account_name{}) + ); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( ram_restrictions_test ) { try { tester c( setup_policy::preactivate_feature_and_new_bios ); From 1ae22df200aa56de96afeb6b14863951aac0ac6b Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 19 Feb 2024 07:51:07 -0600 Subject: [PATCH 5/5] GH-2142 Update comments and provide better error logs on failure --- libraries/chain/block_header_state.cpp | 2 -- libraries/chain/block_state.cpp | 14 +++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 78922138f6..58a39cb5d3 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -177,8 +177,6 @@ block_header_state block_header_state::next(block_header_state_input& input) con * * Given a signed block header, generate the expected template based upon the header time, * then validate that the provided header matches the template. - * - * If the header specifies new_producers then apply them accordingly. */ block_header_state block_header_state::next(const signed_block_header& h, validator_t& validator) const { auto producer = detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, h.timestamp).producer_name; diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 275a8fdd95..03ef13fcb8 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -28,7 +28,7 @@ block_state::block_state(const block_header_state& bhs, deque&& trx_receipts, const std::optional& qc, const signer_callback_type& signer, const block_signing_authority& valid_block_signing_authority) : block_header_state(bhs) - , block(std::make_shared(signed_block_header{bhs.header})) // [greg todo] do we need signatures? + , block(std::make_shared(signed_block_header{bhs.header})) , strong_digest(compute_finalizer_digest()) , weak_digest(create_weak_digest(strong_digest)) , pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) @@ -219,7 +219,7 @@ void block_state::sign(const signer_callback_type& signer, const block_signing_a auto sigs = signer( block_id ); EOS_ASSERT(!sigs.empty(), no_block_signatures, "Signer returned no signatures"); - block->producer_signature = sigs.back(); + block->producer_signature = sigs.back(); // last is producer signature, rest are additional signatures to inject in the block extension sigs.pop_back(); verify_signee(sigs, valid_block_signing_authority); @@ -229,7 +229,7 @@ void block_state::sign(const signer_callback_type& signer, const block_signing_a void block_state::verify_signee(const std::vector& additional_signatures, const block_signing_authority& valid_block_signing_authority) const { auto num_keys_in_authority = std::visit([](const auto &a){ return a.keys.size(); }, valid_block_signing_authority); EOS_ASSERT(1 + additional_signatures.size() <= num_keys_in_authority, wrong_signing_key, - "number of block signatures (${num_block_signatures}) exceeds number of keys in block signing authority (${num_keys})", + "number of block signatures (${num_block_signatures}) exceeds number of keys (${num_keys}) in block signing authority: ${authority}", ("num_block_signatures", 1 + additional_signatures.size()) ("num_keys", num_keys_in_authority) ("authority", valid_block_signing_authority) @@ -240,7 +240,7 @@ void block_state::verify_signee(const std::vector& additional_si for (const auto& s: additional_signatures) { auto res = keys.emplace(s, block_id, true); - EOS_ASSERT(res.second, wrong_signing_key, "block signed by same key twice", ("key", *res.first)); + EOS_ASSERT(res.second, wrong_signing_key, "block signed by same key twice: ${key}", ("key", *res.first)); } bool is_satisfied = false; @@ -249,11 +249,11 @@ void block_state::verify_signee(const std::vector& additional_si std::tie(is_satisfied, relevant_sig_count) = producer_authority::keys_satisfy_and_relevant(keys, valid_block_signing_authority); EOS_ASSERT(relevant_sig_count == keys.size(), wrong_signing_key, - "block signed by unexpected key", - ("signing_keys", keys)("authority", valid_block_signing_authority)); + "block signed by unexpected key: ${signing_keys}, expected: ${authority}. ${c} != ${s}", + ("signing_keys", keys)("authority", valid_block_signing_authority)("c", relevant_sig_count)("s", keys.size())); EOS_ASSERT(is_satisfied, wrong_signing_key, - "block signatures do not satisfy the block signing authority", + "block signatures ${signing_keys} do not satisfy the block signing authority: ${authority}", ("signing_keys", keys)("authority", valid_block_signing_authority)); }