diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 9a7f863914..57bab920b2 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -17,8 +17,8 @@ namespace eosio { namespace chain { const uint32_t fork_database::magic_number = 0x30510FDB; - const uint32_t fork_database::min_supported_version = 1; - const uint32_t fork_database::max_supported_version = 1; + const uint32_t fork_database::min_supported_version = 2; + const uint32_t fork_database::max_supported_version = 2; // work around block_state::is_valid being private inline bool block_state_is_valid( const block_state& bs ) { @@ -28,6 +28,7 @@ namespace eosio { namespace chain { /** * History: * Version 1: initial version of the new refactored fork database portable format + * Version 2: New format for block_state for hotstuff/instant-finality */ struct by_block_id; diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 2db473827d..b580387d51 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -11,6 +11,7 @@ namespace eosio::chain { using hs_bitset = boost::dynamic_bitset; + using bls_key_map_t = std::map; inline uint64_t compute_height(uint32_t block_height, uint32_t phase_counter) { return (uint64_t{block_height} << 32) | phase_counter; diff --git a/libraries/hotstuff/chain_pacemaker.cpp b/libraries/hotstuff/chain_pacemaker.cpp index 3110458214..2940f08cc8 100644 --- a/libraries/hotstuff/chain_pacemaker.cpp +++ b/libraries/hotstuff/chain_pacemaker.cpp @@ -101,9 +101,12 @@ namespace eosio { namespace hotstuff { #endif //=============================================================================================== - chain_pacemaker::chain_pacemaker(controller* chain, std::set my_producers, fc::logger& logger) + chain_pacemaker::chain_pacemaker(controller* chain, + std::set my_producers, + bls_key_map_t finalizer_keys, + fc::logger& logger) : _chain(chain), - _qc_chain("default"_n, this, std::move(my_producers), logger), + _qc_chain("default"_n, this, std::move(my_producers), std::move(finalizer_keys), logger), _logger(logger) { _accepted_block_connection = chain->accepted_block.connect( [this]( const block_state_ptr& blk ) { diff --git a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp index 70320adc11..c2c2331278 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp @@ -20,7 +20,10 @@ namespace eosio::hotstuff { //class-specific functions - chain_pacemaker(controller* chain, std::set my_producers, fc::logger& logger); + chain_pacemaker(controller* chain, + std::set my_producers, + chain::bls_key_map_t finalizer_keys, + fc::logger& logger); void register_bcast_function(std::function broadcast_hs_message); void beat(); diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index c410d7d769..e696280009 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -95,7 +95,10 @@ namespace eosio::hotstuff { qc_chain() = delete; - qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger); + qc_chain(name id, base_pacemaker* pacemaker, + std::set my_producers, + chain::bls_key_map_t finalizer_keys, + fc::logger& logger); uint64_t get_state_version() const { return _state_version; } // calling this w/ thread sync is optional @@ -195,6 +198,7 @@ namespace eosio::hotstuff { eosio::chain::extended_schedule _schedule; base_pacemaker* _pacemaker = nullptr; std::set _my_producers; + chain::bls_key_map_t _my_finalizer_keys; name _id; mutable std::atomic _state_version = 1; diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 2ee77ad3b1..e62381fb0e 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -235,9 +235,13 @@ namespace eosio::hotstuff { } - qc_chain::qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger) + qc_chain::qc_chain(name id, base_pacemaker* pacemaker, + std::set my_producers, + bls_key_map_t finalizer_keys, + fc::logger& logger) : _pacemaker(pacemaker), _my_producers(std::move(my_producers)), + _my_finalizer_keys(std::move(finalizer_keys)), _id(id), _logger(logger) { diff --git a/libraries/hotstuff/test/test_hotstuff.cpp b/libraries/hotstuff/test/test_hotstuff.cpp index cba5789a39..997708fe91 100644 --- a/libraries/hotstuff/test/test_hotstuff.cpp +++ b/libraries/hotstuff/test/test_hotstuff.cpp @@ -53,7 +53,7 @@ class hotstuff_test_handler { //_qc_chains.reserve( replicas.size() ); for (name r : replicas) { - qc_chain *qcc_ptr = new qc_chain(r, &tpm, {r}, hotstuff_logger); + qc_chain *qcc_ptr = new qc_chain(r, &tpm, {r}, {}, hotstuff_logger); std::shared_ptr qcc_shared_ptr(qcc_ptr); _qc_chains.push_back( std::make_pair(r, qcc_shared_ptr) ); diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 2743d01a90..c64962b468 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1117,9 +1117,9 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { } FC_LOG_AND_RETHROW() } -void chain_plugin::create_pacemaker(std::set my_producers) { +void chain_plugin::create_pacemaker(std::set my_producers, chain::bls_key_map_t finalizer_keys) { EOS_ASSERT( !my->_chain_pacemaker, plugin_config_exception, "duplicate chain_pacemaker initialization" ); - my->_chain_pacemaker.emplace(&chain(), std::move(my_producers), hotstuff_logger); + my->_chain_pacemaker.emplace(&chain(), std::move(my_producers), std::move(finalizer_keys), hotstuff_logger); } void chain_plugin::register_pacemaker_bcast_function(std::function bcast_hs_message) { diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index e09b6d6aab..e4d96b2a4a 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -1031,7 +1031,7 @@ class chain_plugin : public plugin { // Only call this after plugin_initialize()! const controller& chain() const; - void create_pacemaker(std::set my_producers); + void create_pacemaker(std::set my_producers, chain::bls_key_map_t finalizer_keys); void register_pacemaker_bcast_function(std::function bcast_hs_message); void notify_hs_message( const chain::hs_message& msg ); void notify_hs_block_produced(); diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index fba71dcf6c..e1c13ea350 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -519,6 +519,7 @@ class producer_plugin_impl : public std::enable_shared_from_this _signature_providers; + bls_key_map_t _finalizer_keys; std::set _producers; boost::asio::deadline_timer _timer; block_timing_util::producer_watermarks _producer_watermarks; @@ -1136,8 +1137,16 @@ void producer_plugin_impl::plugin_initialize(const boost::program_options::varia const std::vector key_spec_pairs = options["signature-provider"].as>(); for (const auto& key_spec_pair : key_spec_pairs) { try { - const auto& [pubkey, provider] = app().get_plugin().signature_provider_for_specification(key_spec_pair); - _signature_providers[pubkey] = provider; + const auto v = app().get_plugin().signature_provider_for_specification(key_spec_pair); + if (v) { + const auto& [pubkey, provider] = *v; + _signature_providers[pubkey] = provider; + } + const auto bls = app().get_plugin().bls_public_key_for_specification(key_spec_pair); + if (bls) { + const auto& [pubkey, privkey] = *bls; + _finalizer_keys[pubkey] = privkey; + } } catch(secure_enclave_exception& e) { elog("Error with Secure Enclave signature provider: ${e}; ignoring ${val}", ("e", e.top_message())("val", key_spec_pair)); } catch (fc::exception& e) { @@ -1358,7 +1367,8 @@ void producer_plugin_impl::plugin_startup() { EOS_ASSERT(_producers.empty() || chain_plug->accept_transactions(), plugin_config_exception, "node cannot have any producer-name configured because no block production is possible with no [api|p2p]-accepted-transactions"); - chain_plug->create_pacemaker(_producers); + chain_plug->create_pacemaker(_producers, std::move(_finalizer_keys)); + _finalizer_keys.clear(); _accepted_block_connection.emplace(chain.accepted_block.connect([this](const auto& bsp) { on_block(bsp); })); _accepted_block_header_connection.emplace(chain.accepted_block_header.connect([this](const auto& bsp) { on_block_header(bsp); })); diff --git a/plugins/signature_provider_plugin/include/eosio/signature_provider_plugin/signature_provider_plugin.hpp b/plugins/signature_provider_plugin/include/eosio/signature_provider_plugin/signature_provider_plugin.hpp index 14fbc6f6e6..5378bb4cdd 100644 --- a/plugins/signature_provider_plugin/include/eosio/signature_provider_plugin/signature_provider_plugin.hpp +++ b/plugins/signature_provider_plugin/include/eosio/signature_provider_plugin/signature_provider_plugin.hpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include namespace eosio { @@ -23,8 +25,12 @@ class signature_provider_plugin : public appbase::plugin; - std::pair signature_provider_for_specification(const std::string& spec) const; - signature_provider_type signature_provider_for_private_key(const chain::private_key_type priv) const; + // @return empty optional for BLS specs + std::optional> signature_provider_for_specification(const std::string& spec) const; + signature_provider_type signature_provider_for_private_key(const chain::private_key_type& priv) const; + + // @return empty optional for non-BLS specs + std::optional> bls_public_key_for_specification(const std::string& spec) const; private: std::unique_ptr my; diff --git a/plugins/signature_provider_plugin/signature_provider_plugin.cpp b/plugins/signature_provider_plugin/signature_provider_plugin.cpp index 2d4e31d9be..7cd9eec57d 100644 --- a/plugins/signature_provider_plugin/signature_provider_plugin.cpp +++ b/plugins/signature_provider_plugin/signature_provider_plugin.cpp @@ -37,6 +37,38 @@ class signature_provider_plugin_impl { return app().get_plugin().get_client().post_sync(keosd_url, params, deadline).as(); }; } + + // public_key spec_type spec_data + std::tuple parse_spec(const std::string& spec) const { + auto delim = spec.find("="); + EOS_ASSERT(delim != std::string::npos, chain::plugin_config_exception, "Missing \"=\" in the key spec pair"); + auto pub_key_str = spec.substr(0, delim); + auto spec_str = spec.substr(delim + 1); + + auto spec_delim = spec_str.find(":"); + EOS_ASSERT(spec_delim != std::string::npos, chain::plugin_config_exception, "Missing \":\" in the key spec pair"); + auto spec_type_str = spec_str.substr(0, spec_delim); + auto spec_data = spec_str.substr(spec_delim + 1); + return {std::move(pub_key_str), std::move(spec_type_str), std::move(spec_data)}; + } + + std::optional> + signature_provider_for_specification(const std::string& spec) const { + auto [pub_key_str, spec_type_str, spec_data] = parse_spec(spec); + if( pub_key_str.starts_with("PUB_BLS") && spec_type_str == "KEY" ) + return {}; + + auto pubkey = chain::public_key_type(pub_key_str); + + if(spec_type_str == "KEY") { + chain::private_key_type priv(spec_data); + EOS_ASSERT(pubkey == priv.get_public_key(), chain::plugin_config_exception, "Private key does not match given public key for ${pub}", ("pub", pubkey)); + return std::make_pair(pubkey, make_key_signature_provider(priv)); + } + else if(spec_type_str == "KEOSD") + return std::make_pair(pubkey, make_keosd_signature_provider(spec_data, pubkey)); + EOS_THROW(chain::plugin_config_exception, "Unsupported key provider type \"${t}\"", ("t", spec_type_str)); + } }; signature_provider_plugin::signature_provider_plugin():my(new signature_provider_plugin_impl()){} @@ -52,11 +84,11 @@ void signature_provider_plugin::set_program_options(options_description&, option const char* const signature_provider_plugin::signature_provider_help_text() const { return "Key=Value pairs in the form =\n" "Where:\n" - " \tis a string form of a vaild EOSIO public key\n\n" - " \tis a string in the form :\n\n" - " \tis KEY, KEOSD, or SE\n\n" - " KEY: \tis a string form of a valid EOSIO private key which maps to the provided public key\n\n" - " KEOSD: \tis the URL where keosd is available and the approptiate wallet(s) are unlocked\n\n" + " \tis a string form of a valid Antelope public key, including BLS finalizer key\n" + " \tis a string in the form :\n" + " \tis KEY, KEOSD, or SE\n" + " KEY: \tis a string form of a valid Antelope private key which maps to the provided public key\n" + " KEOSD: \tis the URL where keosd is available and the appropriate wallet(s) are unlocked\n\n" ; } @@ -65,33 +97,24 @@ void signature_provider_plugin::plugin_initialize(const variables_map& options) my->_keosd_provider_timeout_us = fc::milliseconds( options.at("keosd-provider-timeout").as() ); } -std::pair + +std::optional> signature_provider_plugin::signature_provider_for_specification(const std::string& spec) const { - auto delim = spec.find("="); - EOS_ASSERT(delim != std::string::npos, chain::plugin_config_exception, "Missing \"=\" in the key spec pair"); - auto pub_key_str = spec.substr(0, delim); - auto spec_str = spec.substr(delim + 1); - - auto spec_delim = spec_str.find(":"); - EOS_ASSERT(spec_delim != std::string::npos, chain::plugin_config_exception, "Missing \":\" in the key spec pair"); - auto spec_type_str = spec_str.substr(0, spec_delim); - auto spec_data = spec_str.substr(spec_delim + 1); - - auto pubkey = chain::public_key_type(pub_key_str); - - if(spec_type_str == "KEY") { - chain::private_key_type priv(spec_data); - EOS_ASSERT(pubkey == priv.get_public_key(), chain::plugin_config_exception, "Private key does not match given public key for ${pub}", ("pub", pubkey)); - return std::make_pair(pubkey, my->make_key_signature_provider(priv)); - } - else if(spec_type_str == "KEOSD") - return std::make_pair(pubkey, my->make_keosd_signature_provider(spec_data, pubkey)); - EOS_THROW(chain::plugin_config_exception, "Unsupported key provider type \"${t}\"", ("t", spec_type_str)); + return my->signature_provider_for_specification(spec); } signature_provider_plugin::signature_provider_type -signature_provider_plugin::signature_provider_for_private_key(const chain::private_key_type priv) const { +signature_provider_plugin::signature_provider_for_private_key(const chain::private_key_type& priv) const { return my->make_key_signature_provider(priv); } +std::optional> +signature_provider_plugin::bls_public_key_for_specification(const std::string& spec) const { + auto [pub_key_str, spec_type_str, spec_data] = my->parse_spec(spec); + if( pub_key_str.starts_with("PUB_BLS") && spec_type_str == "KEY" ) { + return std::make_pair(fc::crypto::blslib::bls_public_key{pub_key_str}, fc::crypto::blslib::bls_private_key{spec_data}); + } + return {}; } + +} // namespace eosio