Subject unl list to consensus. (#186)

* Unl hash in consensus proposals.
* Unl syncing and sync request serving.
* Adding unl hash to the ledger block.
This commit is contained in:
Savinda Senevirathne
2020-12-08 15:05:38 +05:30
committed by GitHub
parent a87e8a0c7e
commit 7bf0475b6f
15 changed files with 740 additions and 65 deletions

View File

@@ -120,6 +120,7 @@ namespace consensus
std::string lcl = ledger::ctx.get_lcl();
const uint64_t lcl_seq_no = ledger::ctx.get_seq_no();
const size_t unl_count = unl::count();
std::string unl_hash = unl::get_hash();
hpfs::h32 state = state_common::ctx.get_state();
vote_counter votes;
@@ -130,24 +131,24 @@ namespace consensus
if (verify_and_populate_candidate_user_inputs(lcl_seq_no) == -1)
return -1;
const p2p::proposal new_round_prop = create_stage0_proposal(lcl, state);
const p2p::proposal new_round_prop = create_stage0_proposal(lcl, state, unl_hash);
broadcast_proposal(new_round_prop);
}
else if (ctx.stage == 1)
{
if (is_in_sync(lcl, unl_count, votes))
if (is_in_sync(lcl, unl_hash, unl_count, votes))
{
// If we are in sync, vote and broadcast the winning votes to next stage.
const p2p::proposal p = create_stage123_proposal(STAGE1_THRESHOLD, votes, lcl, unl_count, state);
const p2p::proposal p = create_stage123_proposal(STAGE1_THRESHOLD, votes, lcl, unl_count, state, unl_hash);
broadcast_proposal(p);
}
}
else if (ctx.stage == 2)
{
if (is_in_sync(lcl, unl_count, votes))
if (is_in_sync(lcl, unl_hash, unl_count, votes))
{
// If we are in sync, vote and broadcast the winning votes to next stage.
const p2p::proposal p = create_stage123_proposal(STAGE2_THRESHOLD, votes, lcl, unl_count, state);
const p2p::proposal p = create_stage123_proposal(STAGE2_THRESHOLD, votes, lcl, unl_count, state, unl_hash);
broadcast_proposal(p);
}
@@ -158,14 +159,14 @@ namespace consensus
}
else if (ctx.stage == 3)
{
if (is_in_sync(lcl, unl_count, votes))
if (is_in_sync(lcl, unl_hash, unl_count, votes))
{
// If we are in sync, vote and get the final winning votes.
// This is the consensus proposal which makes it into the ledger and contract execution
const p2p::proposal p = create_stage123_proposal(STAGE3_THRESHOLD, votes, lcl, unl_count, state);
const p2p::proposal p = create_stage123_proposal(STAGE3_THRESHOLD, votes, lcl, unl_count, state, unl_hash);
// Update the unl with the unl changeset that subjected to the consensus.
unl::update(p.unl_changeset.additions, p.unl_changeset.removals);
unl::apply_changeset(p.unl_changeset.additions, p.unl_changeset.removals);
// Update the ledger and execute the contract using the consensus proposal.
if (update_ledger_and_execute_contract(p, lcl, state) == -1)
@@ -178,7 +179,7 @@ namespace consensus
return 0;
}
bool is_in_sync(std::string_view lcl, const size_t unl_count, vote_counter &votes)
bool is_in_sync(std::string_view lcl, std::string_view unl_hash, const size_t unl_count, vote_counter &votes)
{
// Check if we're ahead/behind of consensus lcl.
bool is_lcl_desync = false;
@@ -206,8 +207,19 @@ namespace consensus
state_sync::set_target(majority_state);
}
// Check unl hash with the majority unl hash.
bool is_unl_desync = false;
std::string majority_unl;
check_unl_votes(is_unl_desync, majority_unl, votes, unl_hash);
// Start unl sync if we are out-of-sync with majority unl.
if (is_unl_desync)
{
conf::change_operating_mode(conf::OPERATING_MODE::OBSERVER);
unl::set_sync_target(majority_unl);
}
// Proceed further only if both lcl and state are in sync with majority.
if (!is_lcl_desync && !is_state_desync)
if (!is_lcl_desync && !is_state_desync && !is_unl_desync)
{
conf::change_operating_mode(conf::OPERATING_MODE::PROPOSER);
return true;
@@ -499,7 +511,7 @@ namespace consensus
return 0;
}
p2p::proposal create_stage0_proposal(std::string_view lcl, hpfs::h32 state)
p2p::proposal create_stage0_proposal(std::string_view lcl, hpfs::h32 state, std::string_view unl_hash)
{
// This is the proposal that stage 0 votes on.
// We report our own values in stage 0.
@@ -508,6 +520,7 @@ namespace consensus
stg_prop.stage = 0;
stg_prop.lcl = lcl;
stg_prop.state = state;
stg_prop.unl_hash = unl_hash;
crypto::random_bytes(stg_prop.nonce, ROUND_NONCE_SIZE);
// Populate the proposal with set of candidate user pubkeys.
@@ -527,7 +540,7 @@ namespace consensus
return stg_prop;
}
p2p::proposal create_stage123_proposal(const float_t vote_threshold, vote_counter &votes, std::string_view lcl, const size_t unl_count, const hpfs::h32 state)
p2p::proposal create_stage123_proposal(const float_t vote_threshold, vote_counter &votes, std::string_view lcl, const size_t unl_count, const hpfs::h32 state, std::string_view unl_hash)
{
// The proposal to be emited at the end of this stage.
p2p::proposal stg_prop;
@@ -539,6 +552,8 @@ namespace consensus
// our peers or we will halt depending on level of consensus on the sides of the fork.
stg_prop.lcl = lcl;
stg_prop.unl_hash = unl_hash;
// Vote for rest of the proposal fields by looking at candidate proposals.
for (const auto &[pubkey, cp] : ctx.candidate_proposals)
{
@@ -751,6 +766,33 @@ namespace consensus
is_desync = (state_common::ctx.get_state() != majority_state);
}
/**
* Check unl against the winning and canonical unl
* @param is_desync Is unl is in desync.
* @param majority_unl The majority unl.
* @param votes The voting table.
* @param unl_hash Hash of the current unl list.
*/
void check_unl_votes(bool &is_desync, std::string &majority_unl, vote_counter &votes, std::string_view unl_hash)
{
for (const auto &[pubkey, cp] : ctx.candidate_proposals)
{
increment(votes.unl, cp.unl_hash);
}
uint32_t winning_votes = 0;
for (const auto [unl, votes] : votes.unl)
{
if (votes > winning_votes)
{
winning_votes = votes;
majority_unl = unl;
}
}
is_desync = (unl_hash != majority_unl);
}
/**
* Update the ledger and execute the contract after consensus.
* @param cons_prop The proposal that reached consensus.