Usage of nonce hash for ledger hash generation (#375)

This commit is contained in:
Kithmini Gunawardhana
2023-08-03 16:52:56 +05:30
committed by GitHub
parent 0022bd51e5
commit 6a2384cd03
16 changed files with 148 additions and 117 deletions

View File

@@ -12,6 +12,7 @@
#include "util/h32.hpp"
#include "util/sequence_hash.hpp"
#include "unl.hpp"
#include "ledger/ledger_common.hpp"
#include "ledger/ledger.hpp"
#include "ledger/ledger_query.hpp"
#include "consensus.hpp"
@@ -24,7 +25,6 @@ namespace p2pmsg = msg::fbuf::p2pmsg;
namespace consensus
{
constexpr float STAGE_THRESHOLD_RATIO[3] = {0.5, 0.65, 0.8};
constexpr size_t ROUND_NONCE_SIZE = 64;
constexpr const char *HPFS_SESSION_NAME = "ro_patch_file_to_hp";
// Max no. of time to get unreliable votes before we try heuristics to increase vote receiving reliability.
@@ -242,11 +242,8 @@ namespace consensus
*/
void attempt_ledger_close()
{
std::map<util::h32, uint32_t> hash_votes; // Votes on the proposal hash.
std::map<util::h32, const p2p::proposal> cp_root_hash; // Stores one of proposal to match its root hash.
util::h32 majority_hash = util::h32_empty;
uint32_t stage3_prop_count = 0; // Keep track of the number of stage 3 proposals received.
std::map<util::h32, std::vector<p2p::proposal>> proposal_groups; // Stores sets of proposals against grouped by their root hashes.
uint32_t stage3_prop_count = 0; // Keep track of the number of stage 3 proposals received.
// Count votes of all stage 3 proposal hashes.
for (const auto &[pubkey, cp] : ctx.candidate_proposals)
@@ -254,11 +251,10 @@ namespace consensus
if (cp.stage == 3)
{
stage3_prop_count++;
increment(hash_votes, cp.root_hash);
if (!cp_root_hash.count(cp.root_hash))
cp_root_hash.try_emplace(cp.root_hash, cp);
proposal_groups[cp.root_hash].push_back(cp);
}
}
// Threshold is devided by 100 to convert average to decimal.
const uint32_t min_votes_required = ceil((conf::cfg.contract.consensus.threshold * unl::count()) / 100.0);
if (stage3_prop_count < min_votes_required)
@@ -270,12 +266,13 @@ namespace consensus
// Find the winning hash and no. of votes for it.
uint32_t winning_votes = 0;
for (const auto [hash, votes] : hash_votes)
util::h32 winning_hash = util::h32_empty;
for (const auto [hash, proposals] : proposal_groups)
{
if (votes > winning_votes)
if (proposals.size() > winning_votes)
{
winning_votes = votes;
majority_hash = hash;
winning_votes = proposals.size();
winning_hash = hash;
}
}
@@ -285,20 +282,16 @@ namespace consensus
return;
}
const auto itr = cp_root_hash.find(majority_hash);
if (itr == cp_root_hash.end())
{
LOG_ERROR << "No proposal matching the majority hash found.";
return;
}
const p2p::proposal &majority_prop = itr->second;
// Consensus reached. This is the winning set of proposals.
std::vector<p2p::proposal> &winning_group = proposal_groups[winning_hash];
LOG_DEBUG << "Closing ledger with proposal:" << majority_prop.root_hash;
p2p::proposal &winning_prop = winning_group.front();
LOG_DEBUG << "Closing ledger with proposal:" << winning_prop.root_hash;
// Upon successful ledger close condition, update the ledger and execute the contract using the consensus proposal.
consensed_user_map consensed_users;
if (prepare_consensed_users(consensed_users, majority_prop) == -1 ||
commit_consensus_results(majority_prop, consensed_users) == -1)
if (prepare_consensed_users(consensed_users, winning_prop) == -1 ||
commit_consensus_results(winning_prop, consensed_users) == -1)
{
LOG_ERROR << "Error occured when closing ledger";
@@ -919,7 +912,12 @@ namespace consensus
p.last_primary_shard_id = last_primary_shard_id;
p.last_raw_shard_id = last_raw_shard_id;
p.time_config = CURRENT_TIME_CONFIG;
crypto::random_bytes(p.nonce, ROUND_NONCE_SIZE);
// In stage 0 proposals, we calculate a random nonce from this node to contribute to the group nonce
std::string rand_bytes;
crypto::random_bytes(rand_bytes, ledger::ROUND_NONCE_SIZE);
ctx.round_nonce = crypto::get_hash(rand_bytes);
p.node_nonce = ctx.round_nonce;
// Populate the proposal with set of candidate user pubkeys.
p.users.swap(ctx.candidate_users);
@@ -940,19 +938,24 @@ namespace consensus
{
// The proposal to be emited at the end of this stage.
p2p::proposal p;
p.stage = ctx.stage;
// We always vote for our current information regardless of what other peers are saying.
// If there's a fork condition we will either request shards or hpfs state from
// our peers or we will halt depending on level of consensus on the sides of the fork.
p.stage = ctx.stage;
p.state_hash = state_hash;
p.patch_hash = patch_hash;
p.last_primary_shard_id = last_primary_shard_id;
p.last_raw_shard_id = last_raw_shard_id;
p.time_config = CURRENT_TIME_CONFIG;
p.output_hash.resize(BLAKE3_OUT_LEN); // Default empty hash.
p.node_nonce = ctx.round_nonce;
const uint64_t time_now = util::get_epoch_milliseconds();
// Collect ordered nonces from all proposals in order to calculate the group nonce.
std::set<std::string> node_nonces;
// Vote for rest of the proposal fields by looking at candidate proposals.
for (const auto &[pubkey, cp] : ctx.candidate_proposals)
{
@@ -961,9 +964,6 @@ namespace consensus
if (time_now > cp.time && (time_now - cp.time) <= (conf::cfg.contract.consensus.roundtime * 2))
increment(votes.time, cp.time);
// Vote for round nonce.
increment(votes.nonce, cp.nonce);
// Vote for user pubkeys.
for (const std::string &pubkey : cp.users)
increment(votes.users, pubkey);
@@ -975,8 +975,14 @@ namespace consensus
// Vote for contract output hash.
increment(votes.output_hash, cp.output_hash);
if (ctx.stage == 3)
node_nonces.emplace(cp.node_nonce.to_string_view());
}
if (ctx.stage == 3)
p.group_nonce = crypto::get_list_hash(node_nonces);
// Compute the stage tresholds using ratios.
// Threshold is devided by 100 to convert average to decimal.
const float stage_threshold = ((STAGE_THRESHOLD_RATIO[ctx.stage - 1] * conf::cfg.contract.consensus.threshold) / (STAGE_THRESHOLD_RATIO[2] * 100.0));
@@ -1043,17 +1049,6 @@ namespace consensus
if (p.time == 0)
p.time = ctx.round_start_time;
// Round nonce is voted on a simple sorted (highest to lowest) and majority basis, since there will always be disagreement.
uint32_t highest_nonce_vote = 0;
for (const auto [nonce, numvotes] : votes.nonce)
{
if (numvotes > highest_nonce_vote)
{
highest_nonce_vote = numvotes;
p.nonce = nonce;
}
}
return p;
}