diff --git a/plugins/randpa_plugin/include/eosio/randpa_plugin/network_messages.hpp b/plugins/randpa_plugin/include/eosio/randpa_plugin/network_messages.hpp index a99f07b3889..8ec8d4d4e88 100644 --- a/plugins/randpa_plugin/include/eosio/randpa_plugin/network_messages.hpp +++ b/plugins/randpa_plugin/include/eosio/randpa_plugin/network_messages.hpp @@ -12,13 +12,15 @@ template class network_msg { public: T data; - signature_type signature; + std::vector signatures; network_msg() = default; - network_msg(const T& data_, const signature_type& signature_): data(data_), signature(signature_) {} - network_msg(const T& data_, signature_type&& signature_): data(data_), signature(signature_) {} - network_msg(const T& data_, const signature_provider_type& signature_provider) { + network_msg(const T& data_, const std::vector& signatures_): data(data_), signatures(signatures_) {} + network_msg(const T& data_, std::vector&& signatures_): data(data_), signatures(signatures_) {} + network_msg(const T& data_, const std::vector& signature_providers) { data = data_; - signature = signature_provider(hash()); + for(const auto &sig_prov : signature_providers) { + signatures.push_back(sig_prov(hash())); + } } digest_type hash() const { @@ -28,12 +30,16 @@ class network_msg { return e.result(); } - public_key_type public_key() const { - return public_key_type(signature, hash()); + std::vector public_keys() const { + std::vector public_keys; + for (const auto& sign : signatures) { + public_keys.push_back(public_key_type(sign, hash())); + } + return public_keys; } - bool validate(const public_key_type& pub_key) const { - return public_key() == pub_key; + bool validate(const std::vector& pub_keys) const { + return pub_keys == public_keys(); } }; @@ -103,4 +109,4 @@ FC_REFLECT(randpa_finality::proof_type, (round_num)(best_block)(prevotes)(precom FC_REFLECT(randpa_finality::finality_notice_type, (round_num)(best_block)) FC_REFLECT(randpa_finality::finality_req_proof_type, (round_num)) -FC_REFLECT_TEMPLATE((typename T), randpa_finality::network_msg, (data)(signature)) +FC_REFLECT_TEMPLATE((typename T), randpa_finality::network_msg, (data)(signatures)) diff --git a/plugins/randpa_plugin/include/eosio/randpa_plugin/randpa.hpp b/plugins/randpa_plugin/include/eosio/randpa_plugin/randpa.hpp index d1060262fa8..357760b131a 100644 --- a/plugins/randpa_plugin/include/eosio/randpa_plugin/randpa.hpp +++ b/plugins/randpa_plugin/include/eosio/randpa_plugin/randpa.hpp @@ -198,10 +198,6 @@ class randpa { : _peer_messages{_messages_cache_size}, _self_messages{_messages_cache_size}, _last_proofs{_proofs_cache_size} { - auto private_key = private_key_type::generate(); - _signature_provider = [private_key](const digest_type& digest) { - return private_key.sign(digest); - }; } randpa& set_in_net_channel(const net_channel_ptr& ptr) { @@ -224,15 +220,23 @@ class randpa { return *this; } - randpa& set_signature_provider(const signature_provider_type& signature_provider, - const public_key_type& public_key) { - _signature_provider = signature_provider; - _public_key = public_key; + randpa& set_signature_providers(const vector& signature_providers, + const vector& public_keys) { + _signature_providers = signature_providers; + _public_keys = public_keys; _provided_bp_key = true; - randpa_dlog("set signature provider: ${p}", ("p", public_key)); + randpa_dlog("set signature providers for ${p}", ("p", public_keys)); return *this; } + void add_signature_provider(const signature_provider_type& signature_provider, + const public_key_type& public_key) { + _signature_providers.push_back(signature_provider); + _public_keys.push_back(public_key); + _provided_bp_key = true; + randpa_dlog("added signature provider for ${p}", ("p", public_key)); + } + void start(prefix_tree_ptr tree) { FC_ASSERT(_in_net_channel && _in_event_channel, "in channels should be inited"); FC_ASSERT(_out_net_channel, "out channels should be inited"); @@ -287,8 +291,8 @@ class randpa { std::unique_ptr _thread_ptr; std::atomic _done { false }; - signature_provider_type _signature_provider; - public_key_type _public_key; + std::vector _signature_providers; + std::vector _public_keys; prefix_tree_ptr _prefix_tree; randpa_round_ptr _round; block_id_type _lib; // last irreversible block @@ -371,7 +375,7 @@ class randpa { template bool check_is_valid_msg(const T& msg) const { try { - msg.public_key(); + msg.public_keys(); } catch (const fc::exception&) { return false; } @@ -487,28 +491,30 @@ class randpa { const auto& bp_keys = node->active_bp_keys; for (const auto& prevote : proof.prevotes) { - const auto& prevoter_pub_key = prevote.public_key(); - if (!validate_prevote(prevote.data, prevoter_pub_key, best_block, bp_keys)) { - randpa_dlog("Prevote validation failed, base_block: ${id}, blocks: ${blocks}", - ("id", prevote.data.base_block) - ("blocks", prevote.data.blocks)); - return false; + for (const auto& prevoter_pub_key : prevote.public_keys()) { + if (!validate_prevote(prevote.data, prevoter_pub_key, best_block, bp_keys)) { + randpa_dlog("Prevote validation failed, base_block: ${id}, blocks: ${blocks}", + ("id", prevote.data.base_block) + ("blocks", prevote.data.blocks)); + return false; + } + prevoted_keys.insert(prevoter_pub_key); } - prevoted_keys.insert(prevoter_pub_key); } for (const auto& precommit : proof.precommits) { - const auto& precommiter_pub_key = precommit.public_key(); - if (!prevoted_keys.count(precommiter_pub_key)) { - randpa_dlog("Precommiter has not prevoted, pub_key: ${pub_key}", ("pub_key", precommiter_pub_key)); - return false; + for (const auto& precommiter_pub_key : precommit.public_keys()) { + if (!prevoted_keys.count(precommiter_pub_key)) { + randpa_dlog("Precommiter has not prevoted, pub_key: ${pub_key}", ("pub_key", precommiter_pub_key)); + return false; + } + + if (!validate_precommit(precommit.data, precommiter_pub_key, best_block, bp_keys)) { + randpa_dlog("Precommit validation failed for ${id}", ("id", precommit.data.block_id)); + return false; + } + precommited_keys.insert(precommiter_pub_key); } - - if (!validate_precommit(precommit.data, precommiter_pub_key, best_block, bp_keys)) { - randpa_dlog("Precommit validation failed for ${id}", ("id", precommit.data.block_id)); - return false; - } - precommited_keys.insert(precommiter_pub_key); } return precommited_keys.size() > node->active_bp_keys.size() * 2 / 3; } @@ -528,7 +534,7 @@ class randpa { randpa_dlog("no need to get finality proof for block producer node"); return; } - send(ses_id, finality_req_proof_msg{{data.round_num}, _signature_provider}); + send(ses_id, finality_req_proof_msg{{data.round_num}, _signature_providers}); } void on(uint32_t ses_id, const finality_req_proof_msg& msg) { @@ -537,7 +543,7 @@ class randpa { for (const auto& proof : _last_proofs) { if (proof.round_num == data.round_num) { randpa_dlog("proof found; sending it"); - send(ses_id, proof_msg{proof, _signature_provider}); + send(ses_id, proof_msg{proof, _signature_providers}); break; } } @@ -569,7 +575,9 @@ class randpa { } if (!validate_proof(proof)) { - randpa_ilog("Invalid proof from ${peer}", ("peer", msg.public_key())); + for (const auto& public_key : msg.public_keys()) { + randpa_ilog("Invalid proof among ${peer}", ("peer", public_key)); + } randpa_dlog("Proof msg: ${msg}", ("msg", msg)); return; } @@ -584,22 +592,26 @@ class randpa { } void on(uint32_t ses_id, const handshake_msg& msg) { - randpa_ilog("Randpa handshake_msg received, ses_id: ${ses_id}, from: ${pk}", ("ses_id", ses_id)("pk", msg.public_key())); - try { - _peers[msg.public_key()] = ses_id; - - send(ses_id, handshake_ans_msg(handshake_ans_type { _lib }, _signature_provider)); - } catch (const fc::exception& e) { - randpa_elog("Randpa handshake_msg handler error, reason: ${e}", ("e", e.what())); + for (const auto& public_key : msg.public_keys()) { + randpa_ilog("Randpa handshake_msg received, ses_id: ${ses_id}, from: ${pk}", ("ses_id", ses_id)("pk", public_key)); + try { + _peers[public_key] = ses_id; + + send(ses_id, handshake_ans_msg(handshake_ans_type { _lib }, _signature_providers)); + } catch (const fc::exception& e) { + randpa_elog("Randpa handshake_msg handler error, reason: ${e}", ("e", e.what())); + } } } void on(uint32_t ses_id, const handshake_ans_msg& msg) { - randpa_ilog("Randpa handshake_ans_msg received, ses_id: ${ses_id}, from: ${pk}", ("ses_id", ses_id)("pk", msg.public_key())); - try { - _peers[msg.public_key()] = ses_id; - } catch (const fc::exception& e) { - randpa_elog("Randpa handshake_ans_msg handler error, reason: ${e}", ("e", e.what())); + for (const auto& public_key : msg.public_keys()) { + randpa_ilog("Randpa handshake_ans_msg received, ses_id: ${ses_id}, from: ${pk}", ("ses_id", ses_id)("pk", public_key)); + try { + _peers[public_key] = ses_id; + } catch (const fc::exception& e) { + randpa_elog("Randpa handshake_ans_msg handler error, reason: ${e}", ("e", e.what())); + } } } @@ -663,7 +675,7 @@ class randpa { void on(const on_new_peer_event& event) { randpa_dlog("Randpa on_new_peer_event event handled, ses_id: ${ses_id}", ("ses_id", event.ses_id)); - auto msg = handshake_msg(handshake_type{_lib}, _signature_provider); + auto msg = handshake_msg(handshake_type{_lib}, _signature_providers); send(event.ses_id, msg); } @@ -673,7 +685,7 @@ class randpa { _last_prooved_block_num = get_block_num(proof.best_block); _finality_channel->send(proof.best_block); - bcast(finality_notice_msg{{proof.round_num, proof.best_block}, _signature_provider}); + bcast(finality_notice_msg{{proof.round_num, proof.best_block}, _signature_providers}); } template @@ -744,7 +756,12 @@ class randpa { return false; } - return node_ptr->active_bp_keys.count(_public_key); + for (const auto& public_key : _public_keys) { + if (node_ptr->active_bp_keys.count(public_key)) { + return true; + } + } + return false; } void finish_round() { @@ -771,7 +788,7 @@ class randpa { } void new_round(uint32_t round_num, const public_key_type& primary) { - _round.reset(new randpa_round(round_num, primary, _prefix_tree, _signature_provider, + _round.reset(new randpa_round(round_num, primary, _prefix_tree, _signature_providers, [this](const prevote_msg& msg) { bcast(msg); }, diff --git a/plugins/randpa_plugin/include/eosio/randpa_plugin/round.hpp b/plugins/randpa_plugin/include/eosio/randpa_plugin/round.hpp index dbddf23c9e5..78cf3f57828 100644 --- a/plugins/randpa_plugin/include/eosio/randpa_plugin/round.hpp +++ b/plugins/randpa_plugin/include/eosio/randpa_plugin/round.hpp @@ -34,7 +34,7 @@ class randpa_round { randpa_round(uint32_t num, const public_key_type& primary, const prefix_tree_ptr& tree, - const signature_provider_type& signature_provider, + const std::vector& signature_providers, prevote_bcaster_type && prevote_bcaster, precommit_bcaster_type && precommit_bcaster, done_cb_type && done_cb @@ -42,7 +42,7 @@ class randpa_round { num(num), primary(primary), tree(tree), - signature_provider(signature_provider), + signature_providers(signature_providers), prevote_bcaster(std::move(prevote_bcaster)), precommit_bcaster(std::move(precommit_bcaster)), done_cb(std::move(done_cb)) @@ -147,7 +147,7 @@ class randpa_round { auto chain = tree->get_branch(last_node->block_id); auto prevote = prevote_type { num, chain.base_block, std::move(chain.blocks) }; - auto msg = prevote_msg(prevote, signature_provider); + auto msg = prevote_msg(prevote, signature_providers); add_prevote(msg); prevote_bcaster(msg); } @@ -157,7 +157,7 @@ class randpa_round { state = state::precommit; auto precommit = precommit_type { num, best_node->block_id }; - auto msg = precommit_msg(precommit, signature_provider); + auto msg = precommit_msg(precommit, signature_providers); add_precommit(msg); @@ -173,9 +173,11 @@ class randpa_round { return false; } - if (prevoted_keys.count(msg.public_key())) { - dlog("Randpa received prevote second time for key"); - return false; + for (const auto& public_key : msg.public_keys()) { + if (prevoted_keys.count(public_key)) { + dlog("Randpa received prevote second time for key"); + return false; + } } auto node = find_last_node(msg.data.base_block, msg.data.blocks); @@ -185,11 +187,13 @@ class randpa_round { return false; } - if (!node->active_bp_keys.count(msg.public_key())) { - dlog("Randpa received prevote for block from not active producer, id : ${id}", - ("id", node->block_id) - ); - return false; + for (const auto& public_key : msg.public_keys()) { + if (!node->active_bp_keys.count(public_key)) { + dlog("Randpa received prevote for block from not active producer, id : ${id}", + ("id", node->block_id) + ); + return false; + } } return true; @@ -204,9 +208,11 @@ class randpa_round { return false; } - if (precommited_keys.count(msg.public_key())) { - dlog("Randpa received precommit second time for key"); - return false; + for (const auto& public_key : msg.public_keys()) { + if (precommited_keys.count(public_key)) { + dlog("Randpa received precommit second time for key"); + return false; + } } if (msg.data.block_id != best_node->block_id) { @@ -217,49 +223,57 @@ class randpa_round { return false; } - if (!best_node->has_confirmation(msg.public_key())) { - dlog("Randpa received precommit from not prevoted peer"); - return false; + for (const auto& public_key : msg.public_keys()) { + if (!best_node->has_confirmation(public_key)) { + dlog("Randpa received precommit from not prevoted peer"); + return false; + } } return true; } void add_prevote(const prevote_msg& msg) { - auto max_prevote_node = tree->add_confirmations({ msg.data.base_block, msg.data.blocks }, - msg.public_key(), std::make_shared(msg)); - - FC_ASSERT(max_prevote_node, "confirmation should be insertable"); + auto public_keys = msg.public_keys(); + for (const auto& public_key : public_keys) { + auto max_prevote_node = tree->add_confirmations({ msg.data.base_block, msg.data.blocks }, + public_key, std::make_shared(msg)); - prevoted_keys.insert(msg.public_key()); - dlog("Prevote inserted, round: ${r}, from: ${f}, max_confs: ${c}", - ("r", num) - ("f", msg.public_key()) - ("c", max_prevote_node->confirmation_number()) - ); + FC_ASSERT(max_prevote_node, "confirmation should be insertable"); - if (has_threshold_prevotes(max_prevote_node)) { - state = state::ready_to_precommit; - best_node = max_prevote_node; - dlog("Prevote threshold reached, round: ${r}, best block: ${b}", + prevoted_keys.insert(public_key); + dlog("Prevote inserted, round: ${r}, from: ${f}, max_confs: ${c}", ("r", num) - ("b", best_node->block_id) + ("f", public_key) + ("c", max_prevote_node->confirmation_number()) ); - return; + + if (has_threshold_prevotes(max_prevote_node)) { + state = state::ready_to_precommit; + best_node = max_prevote_node; + dlog("Prevote threshold reached, round: ${r}, best block: ${b}", + ("r", num) + ("b", best_node->block_id) + ); + return; + } } } void add_precommit(const precommit_msg& msg) { - precommited_keys.insert(msg.public_key()); - proof.precommits.push_back(msg); - - if (proof.precommits.size() > 2 * best_node->active_bp_keys.size() / 3) { - dlog("Precommit threshold reached, round: ${r}, best block: ${b}", - ("r", num) - ("b", best_node->block_id) - ); - state = state::done; - done_cb(); + for (const auto& public_key : msg.public_keys()) { + precommited_keys.insert(public_key); + proof.precommits.push_back(msg); + + if (proof.precommits.size() > 2 * best_node->active_bp_keys.size() / 3) { + dlog("Precommit threshold reached, round: ${r}, best block: ${b}", + ("r", num) + ("b", best_node->block_id) + ); + state = state::done; + done_cb(); + return; + } } } @@ -286,7 +300,7 @@ class randpa_round { state state { state::init }; proof_type proof; tree_node_ptr best_node; - signature_provider_type signature_provider; + std::vector signature_providers; prevote_bcaster_type prevote_bcaster; precommit_bcaster_type precommit_bcaster; done_cb_type done_cb; diff --git a/plugins/randpa_plugin/randpa_plugin.cpp b/plugins/randpa_plugin/randpa_plugin.cpp index 26dea32b945..bda7011caf3 100644 --- a/plugins/randpa_plugin/randpa_plugin.cpp +++ b/plugins/randpa_plugin/randpa_plugin.cpp @@ -298,32 +298,32 @@ void randpa_plugin::plugin_initialize(const variables_map& options) { } const auto key_spec_pair_vector = options["signature-provider"].as>(); - if (key_spec_pair_vector.empty()) { - return; - } - const std::string key_spec_pair = key_spec_pair_vector[0]; - - try { - auto delim = key_spec_pair.find("="); - EOS_ASSERT(delim != std::string::npos, plugin_config_exception, "Missing \"=\" in the key spec pair"); - auto pub_key_str = key_spec_pair.substr(0, delim); - auto spec_str = key_spec_pair.substr(delim + 1); - - auto spec_delim = spec_str.find(":"); - EOS_ASSERT(spec_delim != std::string::npos, 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 = public_key_type(pub_key_str); - - if (spec_type_str == "KEY") { - my->_randpa.set_signature_provider(make_key_signature_provider(private_key_type(spec_data)), pubkey); - } else if (spec_type_str == "KEOSD") { - my->_randpa.set_signature_provider(make_keosd_signature_provider(spec_data, pubkey), pubkey); + for (const std::string& key_spec_pair : key_spec_pair_vector) { + try { + auto delim = key_spec_pair.find("="); + EOS_ASSERT(delim != std::string::npos, plugin_config_exception, "Missing \"=\" in the key spec pair"); + auto pub_key_str = key_spec_pair.substr(0, delim); + auto spec_str = key_spec_pair.substr(delim + 1); + + auto spec_delim = spec_str.find(":"); + EOS_ASSERT(spec_delim != std::string::npos, 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 = public_key_type(pub_key_str); + + if (spec_type_str == "KEY") { + my->_randpa.add_signature_provider(make_key_signature_provider(private_key_type(spec_data)), + pubkey); + } else if (spec_type_str == "KEOSD") { + my->_randpa.add_signature_provider(make_keosd_signature_provider(spec_data, pubkey), pubkey); + } + } catch (const fc::exception &) { + randpa_elog("Malformed signature provider ${val}", ("val", key_spec_pair)); + return; } - - } catch (...) { - randpa_elog("Malformed signature provider: \"${val}\", ignoring!", ("val", key_spec_pair)); } } diff --git a/plugins/randpa_plugin/tests/randpa_plugin_tests.cpp b/plugins/randpa_plugin/tests/randpa_plugin_tests.cpp index bd6e4f119f3..d8eac3ba5fd 100644 --- a/plugins/randpa_plugin/tests/randpa_plugin_tests.cpp +++ b/plugins/randpa_plugin/tests/randpa_plugin_tests.cpp @@ -125,6 +125,7 @@ BOOST_AUTO_TEST_SUITE(prevote_tests) BOOST_AUTO_TEST_CASE(prevote_validate_success) try { auto priv_key = private_key::generate(); auto pub_key = priv_key.get_public_key(); + std::vector pub_keys {pub_key}; auto prevote = prevote_type { 0, @@ -132,12 +133,13 @@ BOOST_AUTO_TEST_CASE(prevote_validate_success) try { { fc::sha256("b"), fc::sha256("c"), fc::sha256("d") } }; - auto msg = prevote_msg(prevote, make_key_signature_provider(priv_key)); + std::vector key_signature_providers {make_key_signature_provider(priv_key)}; + auto msg = prevote_msg(prevote, key_signature_providers); BOOST_TEST(prevote.round_num == msg.data.round_num); BOOST_TEST(prevote.base_block == msg.data.base_block); BOOST_TEST(prevote.blocks == msg.data.blocks); - BOOST_TEST(true == msg.validate(pub_key)); + BOOST_TEST(true == msg.validate(pub_keys)); } FC_LOG_AND_RETHROW() @@ -151,15 +153,17 @@ BOOST_AUTO_TEST_CASE(prevote_validate_fail) try { { fc::sha256("b"), fc::sha256("c"), fc::sha256("d") } }; - auto msg = prevote_msg(prevote, make_key_signature_provider(priv_key)); + std::vector key_signature_providers {make_key_signature_provider(priv_key)}; + auto msg = prevote_msg(prevote, key_signature_providers); auto priv_key_2 = private_key::generate(); auto pub_key_2 = priv_key_2.get_public_key(); + std::vector pub_keys_2 {pub_key_2}; BOOST_TEST(prevote.round_num == msg.data.round_num); BOOST_TEST(prevote.base_block == msg.data.base_block); BOOST_TEST(prevote.blocks == msg.data.blocks); - BOOST_TEST(false == msg.validate(pub_key_2)); + BOOST_TEST(false == msg.validate(pub_keys_2)); } FC_LOG_AND_RETHROW() @@ -171,17 +175,19 @@ BOOST_AUTO_TEST_SUITE(precommit_tests) BOOST_AUTO_TEST_CASE(precommit_validate_success) try { auto priv_key = private_key::generate(); auto pub_key = priv_key.get_public_key(); + std::vector pub_keys {pub_key}; auto precommit = precommit_type { 0, fc::sha256("a") }; - auto msg = precommit_msg(precommit, make_key_signature_provider(priv_key)); + std::vector key_signature_providers {make_key_signature_provider(priv_key)}; + auto msg = precommit_msg(precommit, key_signature_providers); BOOST_TEST(precommit.round_num == msg.data.round_num); BOOST_TEST(precommit.block_id == msg.data.block_id); - BOOST_TEST(true == msg.validate(pub_key)); + BOOST_TEST(true == msg.validate(pub_keys)); } FC_LOG_AND_RETHROW() @@ -194,14 +200,16 @@ BOOST_AUTO_TEST_CASE(precommit_validate_fail) try { fc::sha256("a") }; - auto msg = precommit_msg(precommit, make_key_signature_provider(priv_key)); + std::vector key_signature_providers {make_key_signature_provider(priv_key)}; + auto msg = precommit_msg(precommit, key_signature_providers); auto priv_key_2 = private_key::generate(); auto pub_key_2 = priv_key_2.get_public_key(); + std::vector pub_keys_2 {pub_key_2}; BOOST_TEST(precommit.round_num == msg.data.round_num); BOOST_TEST(precommit.block_id == msg.data.block_id); - BOOST_TEST(false == msg.validate(pub_key_2)); + BOOST_TEST(false == msg.validate(pub_keys_2)); } FC_LOG_AND_RETHROW() diff --git a/simulator/include/randpa.hpp b/simulator/include/randpa.hpp index 57843f1f9cd..e42d9b36be2 100644 --- a/simulator/include/randpa.hpp +++ b/simulator/include/randpa.hpp @@ -127,7 +127,7 @@ class RandpaNode: public Node { .set_in_net_channel(in_net_ch) .set_out_net_channel(out_net_ch) .set_finality_channel(finality_ch) - .set_signature_provider(make_key_signature_provider(private_key), private_key.get_public_key()); + .set_signature_providers({make_key_signature_provider(private_key)}, {private_key.get_public_key()}); } net_channel_ptr in_net_ch;