Skip to content

Commit

Permalink
Merge pull request #78 from DaoCasino/fix-randpa-signatures-CYB-569
Browse files Browse the repository at this point in the history
CYB-569 RANDPA: sign messages with only active BP's keys
  • Loading branch information
adrianopol authored Nov 28, 2020
2 parents c685aa9 + 50b3f18 commit b9e1723
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 52 deletions.
5 changes: 4 additions & 1 deletion cicd/docker-run
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ validate_build_type() {
esac
}
validate_timeout() {
local to="${1:?}"
local to="${1:-}"
if [[ -z "$to" ]]; then
return
fi
[[ "$to" -gt 0 ]] && [[ "$to" =~ ^[0-9]+$ ]] ||
die "Invalid argument to --timeout option: $to. See ./$PROGNAME help."
}
Expand Down
97 changes: 66 additions & 31 deletions plugins/randpa_plugin/include/eosio/randpa_plugin/randpa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ struct randpa_net_msg {
struct on_accepted_block_event {
block_id_type block_id;
block_id_type prev_block_id;
public_key_type creator_key;
std::set<public_key_type> active_bp_keys;
public_key_type creator_key; ///< block creator's key
std::set<public_key_type> active_bp_keys; ///< keys of BPs, that signed this block
bool sync;
};

Expand Down Expand Up @@ -221,41 +221,55 @@ class randpa {
return *this;
}

/// Set signature providers.
randpa& set_signature_providers(const std::vector<signature_provider_type>& signature_providers,
const std::vector<public_key_type>& public_keys) {
FC_ASSERT(_is_block_producer, "failed adding signature provider to the full node; use --producer-name option");
if (!_is_bp_key_provided) {
// no need to explicitly clear _signature_providers and _public_keys,
// as they are reassigned in the following lines
_is_bp_key_provided = true;
}
FC_ASSERT(signature_providers.size() == public_keys.size(), "number of signature providers and number of public keys differ");

_signature_providers = signature_providers;
_public_keys = public_keys;

randpa_dlog("set signature providers for ${p}", ("p", public_keys));
_sig_provs_by_key.clear();
for (size_t i = 0; i < _public_keys.size(); i++) {
_sig_provs_by_key[_public_keys[i]] = _signature_providers[i];
}

randpa_dlog("set signature providers for producers ${p}", ("p", public_keys));
return *this;
}

void add_signature_provider(const signature_provider_type& signature_provider, const public_key_type& public_key) {
/// Add signature provider.
// XXX: don't use!
// TODO: remove after fixing simulator (CYB-573)
randpa& add_signature_provider(const signature_provider_type& signature_provider, const public_key_type& public_key) {
FC_ASSERT(_is_block_producer, "failed adding signature provider to the full node; use --producer-name option");
if (!_is_bp_key_provided) {
FC_ASSERT(_signature_providers.size() == 1 && _public_keys.size() == 1,
"changing from full-node case was expected");
// remove default values, stored for full-node case
_signature_providers.clear();
_public_keys.clear();
_is_bp_key_provided = true;
}

//~if (!_is_bp_key_provided) {
//~ FC_ASSERT(_signature_providers.size() == 1 && _public_keys.size() == 1, "changing from full-node case was expected");
//~ // remove default values, stored for full-node case
//~ _signature_providers.clear();
//~ _public_keys.clear();
//~ _sig_provs_by_key.clear();
//~ _is_bp_key_provided = true;
//~}

_signature_providers.push_back(signature_provider);
_public_keys.push_back(public_key);

randpa_dlog("added signature provider for ${p}", ("p", public_key));
_sig_provs_by_key[public_key] = signature_provider;

randpa_dlog("set signature provider for producer ${p}", ("p", public_key));
return *this;
}

void start(prefix_tree_ptr tree) {
FC_ASSERT(_in_net_channel && _in_event_channel, "input channels should be initialized");
FC_ASSERT(_out_net_channel, "output channel should be initialized");
FC_ASSERT(_finality_channel, "finality channel should be initialized");
if (_is_block_producer) {
FC_ASSERT(!_signature_providers.empty());
}

_prefix_tree = tree;
_lib = tree->get_root()->block_id;
Expand Down Expand Up @@ -309,23 +323,27 @@ class randpa {
static constexpr int32_t _max_finality_lag_blocks = 69 * 12 * 2 * 2;

std::unique_ptr<std::thread> _thread_ptr;
std::atomic<bool> _done { false };
std::atomic<bool> _done = false;
std::vector<signature_provider_type> _signature_providers;
std::vector<public_key_type> _public_keys;
bool _is_bp_key_provided { false }; ///< true if user explicitly set at least one sig provider
bool _is_block_producer { false }; ///< node is a block producer if run with --producer-name option
/// this hash map allows effectively filter only active BPs among all listed in configuration file
/// XXX: cannot use unordered_map until fc::crypto::public_key is hashable
std::map<public_key_type, signature_provider_type> _sig_provs_by_key;
// TODO: remove after fixing simulator (CYB-573)
//~bool _is_bp_key_provided = false; ///< true if user explicitly set at least one sig provider
bool _is_block_producer = false; ///< node is a block producer if run with at least one --producer-name option
prefix_tree_ptr _prefix_tree;
randpa_round_ptr _round;
block_id_type _lib; ///< last irreversible block
uint32_t _last_prooved_block_num { 0 };
block_id_type _lib; ///< last irreversible block
uint32_t _last_prooved_block_num = 0;
std::map<public_key_type, uint32_t> _peers;
lru_cache_type _peer_messages;
lru_cache_type _self_messages;
/// Proof data is invalidated after each round is finished, but other nodes will want to request
/// proofs for that round; this cache holds some proofs to reply such requests.
boost::circular_buffer<proof_type> _last_proofs;
bool _is_syncing { false }; ///< syncing blocks from peers
bool _is_frozen { false }; ///< freeze if dpos finality stops working
bool _is_syncing = false; ///< syncing blocks from peers
bool _is_frozen = false; ///< freeze if dpos finality stops working

#ifndef SYNC_RANDPA
message_queue<randpa_message> _message_queue;
Expand Down Expand Up @@ -399,6 +417,19 @@ class randpa {
return true;
}

/// Get intersection of two sets: _signature_providers and active_bp_keys.
std::vector<signature_provider_type> get_active_signature_providers(const std::set<public_key_type>& active_bp_keys) const {
std::vector<signature_provider_type> active_sig_provs;
active_sig_provs.reserve(_signature_providers.size());
for (const auto& key : active_bp_keys) {
const auto it = _sig_provs_by_key.find(key);
if (it != _sig_provs_by_key.end()) {
active_sig_provs.push_back(it->second);
}
}
return active_sig_provs;
}

// need handle all messages
void process_msg(randpa_message_ptr msg_ptr) {
const auto msg = *msg_ptr;
Expand Down Expand Up @@ -557,7 +588,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_providers});
send(ses_id, finality_req_proof_msg{{data.round_num}, _signature_providers}); // TODO: filter _signature_providers???
}

void on(uint32_t ses_id, const finality_req_proof_msg& msg) {
Expand All @@ -566,7 +597,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_providers});
send(ses_id, proof_msg{proof, _signature_providers}); // TODO: see above
break;
}
}
Expand Down Expand Up @@ -676,7 +707,7 @@ class randpa {
randpa_dlog("current round removed");

if (is_active_bp(event.block_id)) {
new_round(round_num(event.block_id), event.creator_key);
new_round(round_num(event.block_id), event.creator_key, event.active_bp_keys);
randpa_dlog("new round (${n}) started", ("n", _round->get_num()));
}
}
Expand Down Expand Up @@ -774,7 +805,7 @@ class randpa {
}

bool is_active_bp(const block_id_type& block_id) const {
if (!_is_bp_key_provided) {
if (!_is_block_producer) {
return false;
}

Expand Down Expand Up @@ -818,8 +849,12 @@ class randpa {
randpa_dlog("round ${r} finished", ("r", _round->get_num()));
}

void new_round(uint32_t round_num, const public_key_type& primary) {
_round.reset(new randpa_round(round_num, primary, _prefix_tree, _signature_providers,
void new_round(uint32_t round_num, const public_key_type& primary, const std::set<public_key_type>& active_bp_keys) {
_round.reset(new randpa_round(
round_num,
primary,
_prefix_tree,
get_active_signature_providers(active_bp_keys),
[this](const prevote_msg& msg) { bcast(msg); },
[this](const precommit_msg& msg) { bcast(msg); },
[this]() { finish_round(); }
Expand Down
9 changes: 5 additions & 4 deletions plugins/randpa_plugin/include/eosio/randpa_plugin/round.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ class randpa_round {
}

if (!node->active_bp_keys.count(key)) {
randpa_dlog("Randpa received prevote for block from not active producer, id : ${id}",
("id", node->block_id)
randpa_dlog("Randpa received prevote for block ${b} from not active producer ${p}",
("b", node->block_id)("p", key)
);
return false;
}
Expand All @@ -237,7 +237,7 @@ class randpa_round {
}

if (precommited_keys.count(key)) {
randpa_dlog("Randpa received precommit second time for key");
randpa_dlog("Randpa received precommit second time for key ${k}", ("k", key));
return false;
}

Expand All @@ -250,7 +250,8 @@ class randpa_round {
}

if (!best_node->has_confirmation(key)) {
randpa_dlog("Randpa received precommit from not prevoted peer: ${k}", ("k", key));
randpa_dlog("Randpa received precommit for block ${b} from not prevoted peer: ${k}",
("b", best_node->block_id)("k", key));
return false;
}

Expand Down
13 changes: 8 additions & 5 deletions plugins/randpa_plugin/randpa_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,13 @@ void randpa_plugin::plugin_initialize(const variables_map& options) {
// @see plugins/producer_plugin/producer_plugin.cpp
return;
}
FC_ASSERT(options.count("signature-provider") > 0, "no 'signature-provider' options for a block producer node");

// parse --signature-provider options
if (!options.count("signature-provider")) {
return;
}
const auto key_spec_pair_vector = options["signature-provider"].as<vector<std::string>>();
std::vector<signature_provider_type> sig_provs;
std::vector<public_key_type> pub_keys;

for (const std::string& key_spec_pair : key_spec_pair_vector) {
try {
auto delim = key_spec_pair.find("=");
Expand All @@ -306,16 +307,18 @@ void randpa_plugin::plugin_initialize(const variables_map& options) {

auto pubkey = public_key_type(pub_key_str);

pub_keys.push_back(pubkey);
if (spec_type_str == "KEY") {
my->_randpa.add_signature_provider(make_key_signature_provider(private_key_type(spec_data)), pubkey);
sig_provs.push_back(make_key_signature_provider(private_key_type(spec_data)));
} else if (spec_type_str == "KEOSD") {
my->_randpa.add_signature_provider(make_keosd_signature_provider(spec_data, pubkey), pubkey);
sig_provs.push_back(make_keosd_signature_provider(spec_data, pubkey));
}
} catch (const fc::exception &) {
randpa_elog("Malformed signature provider ${val}", ("val", key_spec_pair));
return;
}
}
my->_randpa.set_signature_providers(sig_provs, pub_keys);
}

void randpa_plugin::plugin_startup() {
Expand Down
74 changes: 63 additions & 11 deletions simulator/tests/randpa_finality_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,59 @@ TEST(randpa_finality, bp_over_horizontal_lines_fn) {
}
}

TEST(randpa_finality, basic_multisig_BP_only) {
constexpr size_t n_nodes = 2;
constexpr size_t sig_provs_per_node = 2;

auto runner = TestRunner(n_nodes);
runner.load_nodetypes({ BP, BP });

// topology: segment
graph_type g;
g.push_back({{ 1, 10 }});

runner.load_graph(g);
runner.add_stop_task(4 * runner.get_slot_ms());

// initialize nodes
runner.init_nodes<RandpaNode>(runner.get_instances());

// set miltiple signature providers for each node
for (const auto& node : runner.get_nodes()) {
const auto node_ptr = std::dynamic_pointer_cast<RandpaNode>(node);

// TODO: use set_signature_providers() after fixing simulator (CYB-573)
//~std::vector<signature_provider_type> sig_provs;
//~std::vector<public_key_type> pub_keys;

for (size_t i = 0; i < sig_provs_per_node; i++) {
const auto priv_key = ::get_priv_key();

node_ptr->get_randpa().add_signature_provider(
[priv_key](const digest_type& digest) {
return priv_key.sign(digest);
},
priv_key.get_public_key()
);

//~sig_provs.push_back(
//~ [priv_key](const digest_type& digest) {
//~ return priv_key.sign(digest);
//~ }
//~);
//~pub_keys.push_back(priv_key.get_public_key());
}
//~node_ptr->get_randpa().set_signature_providers(sig_provs, pub_keys);
}

// run
runner.run_initialized_nodes();

for (size_t i = 0; i < n_nodes; i++) {
EXPECT_EQ(get_block_height(runner.get_db(i).last_irreversible_block_id()), 3);
}
}

TEST(randpa_finality, large_multisig_BP_only) {
constexpr size_t n_nodes = 6;
constexpr size_t sig_provs_per_node = 10;
Expand Down Expand Up @@ -574,7 +627,7 @@ TEST(randpa_finality, large_multisig_BP_only) {
//
// +-- [0] -- [1] -- [2] --+
// | |
// +-- [3] -- [4] -- [5] --+
// +-- [5] -- [4] -- [3] --+
graph_type g;
// TODO: results are somehow unstable on the following set:
// n_nodes = 6; sig_provs_per_node = 15; deltas = {555, 555, 90, 90, 90, 200}; (i < 12) in the inner loop
Expand All @@ -585,7 +638,6 @@ TEST(randpa_finality, large_multisig_BP_only) {
g.push_back({{ 5, 40 }});
g.push_back({{ 0, 40 }});


runner.load_graph(g);
runner.add_stop_task(24 * runner.get_slot_ms());

Expand All @@ -594,31 +646,31 @@ TEST(randpa_finality, large_multisig_BP_only) {
//~ASSERT_EQ(runner.get_nodes().size(), sig_provs.size());

// set miltiple signature providers for each node
//~size_t sig_index = 0;
for (const auto node : runner.get_nodes()) {
for (const auto& node : runner.get_nodes()) {
const auto node_ptr = std::dynamic_pointer_cast<RandpaNode>(node);

//~const auto& sps = sig_provs[sig_index];
//~const auto& keys = pub_keys[sig_index];
//~sig_index++;
// TODO: use set_signature_providers() after fixing simulator (CYB-573)
//~std::vector<signature_provider_type> sig_provs;
//~std::vector<public_key_type> pub_keys;

for (size_t i = 1; i < sig_provs_per_node; i++) {
const auto priv_key = ::get_priv_key();

node_ptr->get_randpa().add_signature_provider(
[priv_key](const digest_type& digest) {
return priv_key.sign(digest);
},
// some bad publick keys => invalid signature providers
(i < 3 ? ::get_priv_key().get_public_key() : priv_key.get_public_key())
// some bad public keys => invalid signature providers
i < 3 ? ::get_priv_key().get_public_key() : priv_key.get_public_key()
);
}
//~node_ptr->get_randpa().set_signature_providers(sps, keys);
//~node_ptr->get_randpa().set_signature_providers(sig_provs, pub_keys);
}

// run
runner.run_initialized_nodes();

for (size_t i = 0; i < n_nodes; i++) {
EXPECT_EQ(get_block_height(runner.get_db(0).last_irreversible_block_id()), 23);
EXPECT_EQ(get_block_height(runner.get_db(i).last_irreversible_block_id()), 23);
}
}

0 comments on commit b9e1723

Please sign in to comment.