mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
242 lines
9.2 KiB
C++
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 ¤t_patch_hash);
|
|
|
|
void refresh_time_config(const bool perform_detection);
|
|
|
|
} // namespace consensus
|
|
|
|
#endif
|