Skip to content

Commit

Permalink
Merge pull request #1602 from AntelopeIO/GH-1523-finalizer-set-transi…
Browse files Browse the repository at this point in the history
…tion-3

IF: Add support to producer_plugin to load BLS finalizer keys
  • Loading branch information
heifner authored Sep 7, 2023
2 parents a24b4b4 + 01f462d commit 5ede17a
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 43 deletions.
5 changes: 3 additions & 2 deletions libraries/chain/fork_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/hotstuff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace eosio::chain {

using hs_bitset = boost::dynamic_bitset<uint32_t>;
using bls_key_map_t = std::map<fc::crypto::blslib::bls_public_key, fc::crypto::blslib::bls_private_key>;

inline uint64_t compute_height(uint32_t block_height, uint32_t phase_counter) {
return (uint64_t{block_height} << 32) | phase_counter;
Expand Down
7 changes: 5 additions & 2 deletions libraries/hotstuff/chain_pacemaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,12 @@ namespace eosio { namespace hotstuff {
#endif
//===============================================================================================

chain_pacemaker::chain_pacemaker(controller* chain, std::set<account_name> my_producers, fc::logger& logger)
chain_pacemaker::chain_pacemaker(controller* chain,
std::set<account_name> 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 ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ namespace eosio::hotstuff {

//class-specific functions

chain_pacemaker(controller* chain, std::set<account_name> my_producers, fc::logger& logger);
chain_pacemaker(controller* chain,
std::set<account_name> my_producers,
chain::bls_key_map_t finalizer_keys,
fc::logger& logger);
void register_bcast_function(std::function<void(const chain::hs_message&)> broadcast_hs_message);

void beat();
Expand Down
6 changes: 5 additions & 1 deletion libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ namespace eosio::hotstuff {

qc_chain() = delete;

qc_chain(name id, base_pacemaker* pacemaker, std::set<name> my_producers, fc::logger& logger);
qc_chain(name id, base_pacemaker* pacemaker,
std::set<name> 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

Expand Down Expand Up @@ -195,6 +198,7 @@ namespace eosio::hotstuff {
eosio::chain::extended_schedule _schedule;
base_pacemaker* _pacemaker = nullptr;
std::set<name> _my_producers;
chain::bls_key_map_t _my_finalizer_keys;
name _id;

mutable std::atomic<uint64_t> _state_version = 1;
Expand Down
6 changes: 5 additions & 1 deletion libraries/hotstuff/qc_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,13 @@ namespace eosio::hotstuff {
}


qc_chain::qc_chain(name id, base_pacemaker* pacemaker, std::set<name> my_producers, fc::logger& logger)
qc_chain::qc_chain(name id, base_pacemaker* pacemaker,
std::set<name> 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)
{
Expand Down
2 changes: 1 addition & 1 deletion libraries/hotstuff/test/test_hotstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<qc_chain> qcc_shared_ptr(qcc_ptr);

_qc_chains.push_back( std::make_pair(r, qcc_shared_ptr) );
Expand Down
4 changes: 2 additions & 2 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<chain::account_name> my_producers) {
void chain_plugin::create_pacemaker(std::set<chain::account_name> 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<void(const chain::hs_message&)> bcast_hs_message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ class chain_plugin : public plugin<chain_plugin> {
// Only call this after plugin_initialize()!
const controller& chain() const;

void create_pacemaker(std::set<chain::account_name> my_producers);
void create_pacemaker(std::set<chain::account_name> my_producers, chain::bls_key_map_t finalizer_keys);
void register_pacemaker_bcast_function(std::function<void(const chain::hs_message&)> bcast_hs_message);
void notify_hs_message( const chain::hs_message& msg );
void notify_hs_block_produced();
Expand Down
16 changes: 13 additions & 3 deletions plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin

using signature_provider_type = signature_provider_plugin::signature_provider_type;
std::map<chain::public_key_type, signature_provider_type> _signature_providers;
bls_key_map_t _finalizer_keys;
std::set<chain::account_name> _producers;
boost::asio::deadline_timer _timer;
block_timing_util::producer_watermarks _producer_watermarks;
Expand Down Expand Up @@ -1136,8 +1137,16 @@ void producer_plugin_impl::plugin_initialize(const boost::program_options::varia
const std::vector<std::string> key_spec_pairs = options["signature-provider"].as<std::vector<std::string>>();
for (const auto& key_spec_pair : key_spec_pairs) {
try {
const auto& [pubkey, provider] = app().get_plugin<signature_provider_plugin>().signature_provider_for_specification(key_spec_pair);
_signature_providers[pubkey] = provider;
const auto v = app().get_plugin<signature_provider_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<signature_provider_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) {
Expand Down Expand Up @@ -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); }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include <eosio/chain/application.hpp>
#include <eosio/http_client_plugin/http_client_plugin.hpp>
#include <eosio/chain/types.hpp>
#include <fc/crypto/bls_private_key.hpp>
#include <fc/crypto/bls_public_key.hpp>

namespace eosio {

Expand All @@ -23,8 +25,12 @@ class signature_provider_plugin : public appbase::plugin<signature_provider_plug

using signature_provider_type = std::function<chain::signature_type(chain::digest_type)>;

std::pair<chain::public_key_type,signature_provider_type> 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<std::pair<chain::public_key_type,signature_provider_type>> 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<std::pair<fc::crypto::blslib::bls_public_key, fc::crypto::blslib::bls_private_key>> bls_public_key_for_specification(const std::string& spec) const;

private:
std::unique_ptr<class signature_provider_plugin_impl> my;
Expand Down
77 changes: 50 additions & 27 deletions plugins/signature_provider_plugin/signature_provider_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,38 @@ class signature_provider_plugin_impl {
return app().get_plugin<http_client_plugin>().get_client().post_sync(keosd_url, params, deadline).as<chain::signature_type>();
};
}

// public_key spec_type spec_data
std::tuple<std::string, std::string, std::string> 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<std::pair<chain::public_key_type,signature_provider_plugin::signature_provider_type>>
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()){}
Expand All @@ -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 <public-key>=<provider-spec>\n"
"Where:\n"
" <public-key> \tis a string form of a vaild EOSIO public key\n\n"
" <provider-spec> \tis a string in the form <provider-type>:<data>\n\n"
" <provider-type> \tis KEY, KEOSD, or SE\n\n"
" KEY:<data> \tis a string form of a valid EOSIO private key which maps to the provided public key\n\n"
" KEOSD:<data> \tis the URL where keosd is available and the approptiate wallet(s) are unlocked\n\n"
" <public-key> \tis a string form of a valid Antelope public key, including BLS finalizer key\n"
" <provider-spec> \tis a string in the form <provider-type>:<data>\n"
" <provider-type> \tis KEY, KEOSD, or SE\n"
" KEY:<data> \tis a string form of a valid Antelope private key which maps to the provided public key\n"
" KEOSD:<data> \tis the URL where keosd is available and the appropriate wallet(s) are unlocked\n\n"
;

}
Expand All @@ -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<int32_t>() );
}

std::pair<chain::public_key_type,signature_provider_plugin::signature_provider_type>

std::optional<std::pair<chain::public_key_type,signature_provider_plugin::signature_provider_type>>
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<std::pair<fc::crypto::blslib::bls_public_key, fc::crypto::blslib::bls_private_key>>
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

0 comments on commit 5ede17a

Please sign in to comment.