Files
hpcore/src/consensus.hpp
Ravin Perera 9f4499653a Dynamic roundtime detection. (#244)
When consensus is unreliable detect roundtime based on roundtime reported by peers.
2021-02-16 13:34:29 +05:30

166 lines
6.1 KiB
C++

#ifndef _HP_CONS_
#define _HP_CONS_
#include "pchheader.hpp"
#include "util/util.hpp"
#include "util/buffer_store.hpp"
#include "util/merkle_hash_tree.hpp"
#include "./sc/sc.hpp"
#include "p2p/p2p.hpp"
#include "usr/user_input.hpp"
#include "util/h32.hpp"
namespace consensus
{
/**
* Represents a contract input that takes part in consensus.
*/
struct candidate_user_input
{
const std::string userpubkey;
const uint64_t maxledgerseqno = 0;
const util::buffer_view input;
candidate_user_input(const std::string userpubkey, const util::buffer_view input, const uint64_t maxledgerseqno)
: userpubkey(std::move(userpubkey)), input(input), maxledgerseqno(maxledgerseqno)
{
}
};
/**
* Represents a contract-generated user output that takes part in consensus.
*/
struct generated_user_output
{
const std::string userpubkey;
std::list<sc::contract_output> outputs;
generated_user_output(const std::string userpubkey, const std::list<sc::contract_output> outputs)
: userpubkey(std::move(userpubkey)), outputs(std::move(outputs))
{
}
};
/**
* This is used to store consensus information
*/
struct consensus_context
{
// The map of proposals that are being collected as consensus stages are progressing.
// peer public key is the key.
// todo: having a queue of proposals against peer pubkey.
std::unordered_map<std::string, const p2p::proposal> candidate_proposals;
// Set of user pubkeys that is said to be connected to the cluster. This will be cleared in each round.
std::set<std::string> candidate_users;
// Map of candidate user inputs with input hash as map key. Inputs will stay here until they
// achieve consensus or expire (due to maxledgerseqno). Input hash is globally unique among inputs
// from all users. We will use this map to feed inputs into the contract once consensus is achieved.
std::map<std::string, candidate_user_input> candidate_user_inputs;
// Map of outputs generated by the contract with output hash as the map key. Outputs will stay
// here until the end of the current consensus round. Output hash is globally unique among outputs for
// all users. We will use this map to distribute outputs back to connected users once consensus is achieved.
std::map<std::string, generated_user_output> generated_user_outputs;
util::merkle_hash_tree user_outputs_hashtree;
std::string user_outputs_our_sig;
std::vector<std::pair<std::string, std::string>> user_outputs_unl_sig;
uint8_t stage = 1;
uint64_t round_start_time = 0;
uint16_t stage_time = 0; // Time allocated to a consensus stage.
uint16_t stage_reset_wait_threshold = 0; // Minimum stage wait time to reset the stage.
uint64_t round_boundry_offset = 0; // Time window boundry offset based on contract id.
uint16_t unreliable_votes_attempts = 0; // No. of times we failed to get reliable votes continously.
std::optional<sc::execution_context> contract_ctx;
std::mutex contract_ctx_mutex;
bool is_shutting_down = false;
std::thread consensus_thread;
consensus_context() : user_outputs_hashtree(16)
{
}
};
struct vote_counter
{
std::map<uint64_t, uint32_t> time;
std::map<std::string, uint32_t> nonce;
std::map<std::string, uint32_t> lcl;
std::map<std::string, uint32_t> users;
std::map<std::string, uint32_t> inputs;
std::map<std::string, uint32_t> output_hash;
std::map<util::h32, uint32_t> state_hash;
std::map<util::h32, uint32_t> patch_hash;
};
extern std::atomic<bool> is_patch_update_pending; // Keep track whether the patch file is changed by the SC and is not yet applied to runtime.
int init();
void deinit();
void wait();
void run_consensus();
int consensus();
int check_sync_status(std::string_view lcl, const size_t unl_count, vote_counter &votes);
void check_sync_completion();
void revise_candidate_proposals();
bool wait_and_proceed_stage();
void broadcast_nonunl_proposal();
bool push_npl_message(p2p::npl_message &npl_message);
int verify_and_populate_candidate_user_inputs(const uint64_t lcl_seq_no);
p2p::proposal create_stage0_proposal(std::string_view lcl, util::h32 state_hash, util::h32 patch_hash);
p2p::proposal create_stage123_proposal(vote_counter &votes, std::string_view lcl, const size_t unl_count, const util::h32 state_hash, const util::h32 patch_hash);
void broadcast_proposal(const p2p::proposal &p);
bool check_lcl_votes(bool &is_desync, std::string &majority_lcl, vote_counter &votes, std::string_view lcl, const size_t unl_count);
void check_state_votes(bool &is_state_desync, util::h32 &majority_state_hash, vote_counter &votes);
void check_patch_votes(bool &is_patch_desync, util::h32 &majority_patch_hash, vote_counter &votes);
void timewait_stage(const bool reset, const uint64_t time);
uint64_t get_ledger_time_resolution(const uint64_t time);
uint64_t get_stage_time_resolution(const uint64_t time);
int update_ledger_and_execute_contract(const p2p::proposal &cons_prop, std::string &new_lcl, util::h32 &new_state_hash, const util::h32 &patch_hash);
int dispatch_user_outputs(const p2p::proposal &cons_prop, const uint64_t lcl_seq_no, std::string_view lcl);
int feed_user_inputs_to_contract_bufmap(sc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop);
void extract_user_outputs_from_contract_bufmap(sc::contract_bufmap_t &bufmap);
template <typename T>
void increment(std::map<T, uint32_t> &counter, const T &candidate);
int get_initial_state_hash(util::h32 &hash);
bool push_control_message(const std::string &control_msg);
int apply_consensed_patch_file_changes(const util::h32 &prop_patch_hash, const util::h32 &current_patch_hash);
void refresh_roundtime(const bool perform_detection);
} // namespace consensus
#endif