Files
hpcore/src/consensus.hpp
2023-08-03 16:52:56 +05:30

242 lines
9.2 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"
#include "util/sequence_hash.hpp"
namespace consensus
{
/**
* Represents a contract input that takes part in consensus.
* This is used in a map keyed by input ordered hash.
*/
struct candidate_user_input
{
const std::string user_pubkey;
const util::buffer_view input;
const uint64_t max_ledger_seq_no = 0;
candidate_user_input(const std::string &user_pubkey, const util::buffer_view input, const uint64_t max_ledger_seq_no)
: user_pubkey(user_pubkey), input(input), max_ledger_seq_no(max_ledger_seq_no)
{
}
};
/**
* Represents a consensus reached user input.
*/
struct consensed_user_input
{
const std::string ordered_hash; // [nonce] + [input signature hash]
const util::buffer_view input; // The input data buffer pointer.
consensed_user_input(const std::string &ordered_hash, const util::buffer_view input)
: ordered_hash(ordered_hash), input(input)
{
}
};
/**
* Represents all consensus reached outputs for a user.
*/
struct consensed_user_output
{
std::string hash; // The hash of all outputs for the user.
std::vector<std::string> outputs;
};
/**
* Represents a consensed user and any consensus-reached inputs/outputs for that user.
*/
struct consensed_user
{
std::vector<consensed_user_input> consensed_inputs;
consensed_user_output consensed_outputs;
};
/**
* Consensed users map keyed by user binary pubkey.
*/
typedef std::map<std::string, consensed_user> consensed_user_map;
/**
* Represents a contract-generated user output that takes part in consensus.
*/
struct generated_user_output
{
const std::string user_pubkey;
std::list<sc::contract_output> outputs;
generated_user_output(const std::string &user_pubkey, const std::list<sc::contract_output> &&outputs)
: user_pubkey(user_pubkey), outputs(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.
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 ordered hash as map key. Inputs will stay here until they
// achieve consensus or expire (due to max_ledger_seq_no). Ordered 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;
uint32_t stage0_duration = 0; // Time allocated to a consensus stage 0.
uint32_t stage123_duration = 0; // Time allocated to each consensus stage 1,2,3.
uint32_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.
util::h32 round_nonce; // The random nonce generated by this node for this consensus round.
// Indicates whether we are inside a sync cycle or not. Sync cycle is considered to being when we first detect that we are out of sync
// and considered to end when we detect to be in sync inside stage 1 of a round for the first time after we began a sync.
bool sync_ongoing = false;
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> 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;
std::map<util::sequence_hash, uint32_t> last_ledger_primary_shard;
std::map<util::sequence_hash, uint32_t> last_ledger_raw_shard;
void reset()
{
time.clear();
lcl.clear();
users.clear();
inputs.clear();
output_hash.clear();
state_hash.clear();
patch_hash.clear();
last_ledger_primary_shard.clear();
last_ledger_raw_shard.clear();
}
};
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();
void attempt_ledger_close();
int commit_consensus_results(const p2p::proposal &cons_prop, const consensus::consensed_user_map &consensed_users);
status::VOTE_STATUS check_vote_status(const size_t unl_count, vote_counter &votes, const util::sequence_hash &lcl_id);
void revise_candidate_proposals(const bool in_sync);
int prepare_consensed_users(consensed_user_map &consensed_users, const p2p::proposal &cons_prop);
void expire_candidate_inputs(const util::sequence_hash &lcl_id);
int cleanup_consensed_user_inputs(const consensed_user_map &consensed_users);
void cleanup_output_collections();
bool wait_and_proceed_stage();
void broadcast_nonunl_proposal();
bool push_npl_message(const p2p::npl_message &npl_message);
int verify_and_populate_candidate_user_inputs(const uint64_t lcl_seq_no);
p2p::proposal create_stage0_proposal(const util::h32 &state_hash, const util::h32 &patch_hash,
const util::sequence_hash &last_primary_shard_id, const util::sequence_hash &last_raw_shard_id);
p2p::proposal create_stage123_proposal(vote_counter &votes, const size_t unl_count, const util::h32 &state_hash, const util::h32 &patch_hash,
const util::sequence_hash &last_primary_shard_id, const util::sequence_hash &last_raw_shard_id);
void broadcast_proposal(const p2p::proposal &p);
bool check_last_primary_shard_hash_votes(bool &is_desync, util::sequence_hash &majority_primary_shard_id, vote_counter &votes, const size_t unl_count);
void check_last_raw_shard_hash_votes(bool &is_ledger_blob_desync, util::sequence_hash &majority_raw_shard_id, vote_counter &votes);
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_duration_resolution(const uint64_t time);
int execute_contract(const uint64_t time, const consensed_user_map &consensed_users, const util::sequence_hash &lcl_id);
void dispatch_consensed_user_input_responses(const consensed_user_map &consensed_users, const util::sequence_hash &lcl_id);
void dispatch_consensed_user_outputs(const consensed_user_map &consensed_users, const util::sequence_hash &lcl_id);
void dispatch_synced_ledger_input_statuses(const util::sequence_hash &lcl_id);
void feed_user_inputs_to_contract_bufmap(sc::contract_bufmap_t &bufmap, const consensed_user_map &consensed_users);
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_time_config(const bool perform_detection);
} // namespace consensus
#endif