From 9f4499653ab031f74a9eb88a2c8f530455e4668e Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Tue, 16 Feb 2021 13:34:29 +0530 Subject: [PATCH] Dynamic roundtime detection. (#244) When consensus is unreliable detect roundtime based on roundtime reported by peers. --- src/conf.cpp | 6 +- src/conf.hpp | 20 +- src/consensus.cpp | 58 +++++- src/consensus.hpp | 3 + src/crypto.cpp | 24 +-- src/crypto.hpp | 14 +- src/hpfs/hpfs_serve.cpp | 5 +- src/hpfs/hpfs_serve.hpp | 2 - src/hpfs/hpfs_sync.cpp | 6 +- src/hpfs/hpfs_sync.hpp | 3 +- src/msg/fbuf/p2pmsg_content.fbs | 2 + src/msg/fbuf/p2pmsg_content_generated.h | 75 ++++++-- src/msg/fbuf/p2pmsg_helpers.cpp | 236 ++++++++++++------------ src/p2p/p2p.hpp | 13 +- src/p2p/peer_comm_session.hpp | 1 + src/p2p/peer_session_handler.cpp | 7 +- src/unl.cpp | 183 +++++++++++++----- src/unl.hpp | 10 +- test/local-cluster/cluster-start.sh | 3 +- 19 files changed, 430 insertions(+), 241 deletions(-) diff --git a/src/conf.cpp b/src/conf.cpp index b5f88c55..8fbfd001 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -148,9 +148,9 @@ namespace conf cfg.contract.is_npl_public = false; cfg.mesh.port = 22860; - cfg.mesh.msg_forwarding = false; + cfg.mesh.msg_forwarding = true; cfg.mesh.idle_timeout = 120; - cfg.mesh.peer_discovery.enabled = false; + cfg.mesh.peer_discovery.enabled = true; cfg.mesh.peer_discovery.interval = 30000; cfg.user.port = 8080; @@ -846,7 +846,7 @@ namespace conf jdoc.insert_or_assign("unl", unl); jdoc.insert_or_assign("bin_path", contract.bin_path); jdoc.insert_or_assign("bin_args", contract.bin_args); - jdoc.insert_or_assign("roundtime", contract.roundtime); + jdoc.insert_or_assign("roundtime", contract.roundtime.load()); jdoc.insert_or_assign("consensus", contract.is_consensus_public ? PUBLIC : PRIVATE); jdoc.insert_or_assign("npl", contract.is_npl_public ? PUBLIC : PRIVATE); diff --git a/src/conf.hpp b/src/conf.hpp index 48026fe7..4047f4c8 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -97,16 +97,16 @@ namespace conf struct contract_config { - std::string id; // Contract guid. - bool execute = false; // Whether or not to execute the contract on the node. - bool log_output = false; // Whether to log stdout/err of the contract process. - std::string version; // Contract version string. - std::set unl; // Unique node list (list of binary public keys) - std::string bin_path; // Full path to the contract binary - std::string bin_args; // CLI arguments to pass to the contract binary - uint16_t roundtime = 0; // Consensus round time in ms - bool is_consensus_public = false; // If true, consensus are broadcasted to non-unl nodes as well. - bool is_npl_public = false; // If true, npl messages are broadcasted to non-unl nodes as well. + std::string id; // Contract guid. + bool execute = false; // Whether or not to execute the contract on the node. + bool log_output = false; // Whether to log stdout/err of the contract process. + std::string version; // Contract version string. + std::set unl; // Unique node list (list of binary public keys) + std::string bin_path; // Full path to the contract binary + std::string bin_args; // CLI arguments to pass to the contract binary + std::atomic roundtime = 0; // Consensus round time in ms + bool is_consensus_public = false; // If true, consensus are broadcasted to non-unl nodes as well. + bool is_npl_public = false; // If true, npl messages are broadcasted to non-unl nodes as well. appbill_config appbill; round_limits_config round_limits; diff --git a/src/consensus.cpp b/src/consensus.cpp index 86dcfce6..aa9badfe 100644 --- a/src/consensus.cpp +++ b/src/consensus.cpp @@ -24,20 +24,16 @@ namespace consensus 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. + constexpr uint16_t MAX_UNRELIABLE_VOTES_ATTEMPTS = 5; + consensus_context ctx; bool init_success = false; std::atomic is_patch_update_pending = false; // Keep track whether the patch file is changed by the SC and is not yet applied to runtime. int init() { - // We allocate 1/4 of roundtime for each stage (0, 1, 2, 3). - ctx.stage_time = conf::cfg.contract.roundtime / 4; - ctx.stage_reset_wait_threshold = conf::cfg.contract.roundtime / 10; - - // We use a time window boundry offset based on contract id to vary the window boundries between - // different contracts with same round time. - std::hash str_hasher; - ctx.round_boundry_offset = str_hasher(conf::cfg.contract.id) % conf::cfg.contract.roundtime; + refresh_roundtime(false); // Starting consensus processing thread. ctx.consensus_thread = std::thread(run_consensus); @@ -138,6 +134,20 @@ namespace consensus vote_counter votes; const int sync_status = check_sync_status(lcl, unl_count, votes); + if (sync_status == -2) // Unreliable votes. + { + ctx.unreliable_votes_attempts++; + if (ctx.unreliable_votes_attempts >= MAX_UNRELIABLE_VOTES_ATTEMPTS) + { + refresh_roundtime(true); + ctx.unreliable_votes_attempts = 0; + } + } + else + { + ctx.unreliable_votes_attempts = 0; + } + if (sync_status == 0) { // If we are in sync, vote and broadcast the winning votes to next stage. @@ -257,6 +267,9 @@ namespace consensus collected_proposals.splice(collected_proposals.end(), p2p::ctx.collected_msgs.proposals); } + // Provide latest roundtime information to unl statistics. + unl::update_roundtime_stats(collected_proposals); + // Move collected propsals to candidate set of proposals. // Add propsals of new nodes and replace proposals from old nodes to reflect current status of nodes. for (const auto &proposal : collected_proposals) @@ -1048,4 +1061,33 @@ namespace consensus return 0; } + /** + * Updates roundtime-based calculations with the latest roundtime value. + * @param perform_detection Whether or not to detect roundtime from latest network information. + */ + void refresh_roundtime(const bool perform_detection) + { + if (perform_detection) + { + LOG_DEBUG << "Detecting roundtime..."; + const uint16_t majority_roundtime = unl::get_majority_roundtime(); + + if (majority_roundtime == 0 || conf::cfg.contract.roundtime == majority_roundtime) + return; + + LOG_INFO << "New roundtime detected:" << majority_roundtime << " previous:" << conf::cfg.contract.roundtime; + + conf::cfg.contract.roundtime = majority_roundtime; + } + + // We allocate 1/4 of roundtime for each stage (0, 1, 2, 3). + ctx.stage_time = conf::cfg.contract.roundtime / 4; + ctx.stage_reset_wait_threshold = conf::cfg.contract.roundtime / 10; + + // We use a time window boundry offset based on contract id to vary the window boundries between + // different contracts with same round time. + std::hash str_hasher; + ctx.round_boundry_offset = str_hasher(conf::cfg.contract.id) % conf::cfg.contract.roundtime; + } + } // namespace consensus diff --git a/src/consensus.hpp b/src/consensus.hpp index 397d2d87..3e1d408a 100644 --- a/src/consensus.hpp +++ b/src/consensus.hpp @@ -72,6 +72,7 @@ namespace consensus 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 contract_ctx; std::mutex contract_ctx_mutex; @@ -157,6 +158,8 @@ namespace consensus int apply_consensed_patch_file_changes(const util::h32 &prop_patch_hash, const util::h32 ¤t_patch_hash); + void refresh_roundtime(const bool perform_detection); + } // namespace consensus #endif diff --git a/src/crypto.cpp b/src/crypto.cpp index cc6673a0..a7768891 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -46,7 +46,7 @@ namespace crypto * @param private_key Private key bytes. * @return Signature bytes. */ - std::string sign(std::string_view msg, std::string_view private_key) + const std::string sign(std::string_view msg, std::string_view private_key) { //Generate the signature using libsodium. @@ -93,7 +93,7 @@ namespace crypto * @param data String to hash. * @return The blake3 hash of the given string. */ - std::string get_hash(std::string_view data) + const std::string get_hash(std::string_view data) { std::string hash; hash.resize(BLAKE3_OUT_LEN); @@ -110,12 +110,12 @@ namespace crypto } /** - * Generate blake3 hash for a given message. - * @param data unsigned char array pointer to hash data. - * @param data_length hash data length. - * @return The blake3 hash of the pointed buffer. - */ - std::string get_hash(const unsigned char *data, size_t data_length) + * Generate blake3 hash for a given message. + * @param data unsigned char array pointer to hash data. + * @param data_length hash data length. + * @return The blake3 hash of the pointed buffer. + */ + const std::string get_hash(const unsigned char *data, size_t data_length) { std::string hash; hash.resize(BLAKE3_OUT_LEN); @@ -134,7 +134,7 @@ namespace crypto /** * Generates blake3 hash for the given set of strings using stream hashing. */ - std::string get_hash(std::string_view s1, std::string_view s2) + const std::string get_hash(std::string_view s1, std::string_view s2) { std::string hash; hash.resize(BLAKE3_OUT_LEN); @@ -156,7 +156,7 @@ namespace crypto /** * Generates blake3 hash for the given string view vector using stream hashing. */ - std::string get_hash(const std::vector &sw_vect) + const std::string get_hash(const std::vector &sw_vect) { std::string hash; hash.resize(BLAKE3_OUT_LEN); @@ -178,7 +178,7 @@ namespace crypto /** * Generates blake3 hash for the given string set using stream hashing. */ - std::string get_hash(const std::set &sw_set) + const std::string get_hash(const std::set &sw_set) { std::string hash; hash.resize(BLAKE3_OUT_LEN); @@ -197,7 +197,7 @@ namespace crypto return hash; } - std::string generate_uuid() + const std::string generate_uuid() { std::string rand_bytes; random_bytes(rand_bytes, 16); diff --git a/src/crypto.hpp b/src/crypto.hpp index d88654ae..5eead63c 100644 --- a/src/crypto.hpp +++ b/src/crypto.hpp @@ -17,23 +17,23 @@ namespace crypto void generate_signing_keys(std::string &pubkey, std::string &seckey); - std::string sign(std::string_view msg, std::string_view seckey); + const std::string sign(std::string_view msg, std::string_view seckey); int verify(std::string_view msg, std::string_view sig, std::string_view pubkey); void random_bytes(std::string &result, const size_t len); - std::string get_hash(std::string_view data); + const std::string get_hash(std::string_view data); - std::string get_hash(const unsigned char *data, size_t data_length); + const std::string get_hash(const unsigned char *data, size_t data_length); - std::string get_hash(std::string_view s1, std::string_view s2); + const std::string get_hash(std::string_view s1, std::string_view s2); - std::string get_hash(const std::vector &sw_vect); + const std::string get_hash(const std::vector &sw_vect); - std::string get_hash(const std::set &sw_set); + const std::string get_hash(const std::set &sw_set); - std::string generate_uuid(); + const std::string generate_uuid(); } // namespace crypto diff --git a/src/hpfs/hpfs_serve.cpp b/src/hpfs/hpfs_serve.cpp index 24ae5477..48f937d7 100644 --- a/src/hpfs/hpfs_serve.cpp +++ b/src/hpfs/hpfs_serve.cpp @@ -33,7 +33,6 @@ namespace hpfs this->name = name; this->fs_mount = fs_mount; - REQUEST_BATCH_TIMEOUT = hpfs::get_request_resubmit_timeout() * 0.9; hpfs_serve_thread = std::thread(&hpfs_serve::hpfs_serve_loop, this); init_success = true; return 0; @@ -57,7 +56,6 @@ namespace hpfs LOG_INFO << "Hpfs " << name << " server started."; - // Indicates whether any requests were processed in the previous loop iteration. bool prev_requests_processed = false; @@ -72,6 +70,7 @@ namespace hpfs prev_requests_processed = !hpfs_requests.empty(); const uint64_t time_start = util::get_epoch_milliseconds(); const std::string lcl = ledger::ctx.get_lcl(); + const uint16_t request_batch_timeout = hpfs::get_request_resubmit_timeout() * 0.9; if (hpfs_requests.empty()) continue; @@ -86,7 +85,7 @@ namespace hpfs // If we have spent too much time handling hpfs requests, abandon the entire batch // because the requester would have stopped waiting for us. const uint64_t time_now = util::get_epoch_milliseconds(); - if ((time_now - time_start) > REQUEST_BATCH_TIMEOUT) + if ((time_now - time_start) > request_batch_timeout) { LOG_DEBUG << "Hpfs " << name << " serve batch timeout. Abandonding hpfs requests."; break; diff --git a/src/hpfs/hpfs_serve.hpp b/src/hpfs/hpfs_serve.hpp index 53b73a2c..af40c018 100644 --- a/src/hpfs/hpfs_serve.hpp +++ b/src/hpfs/hpfs_serve.hpp @@ -11,8 +11,6 @@ namespace hpfs class hpfs_serve { private: - uint16_t REQUEST_BATCH_TIMEOUT; - bool is_shutting_down = false; bool init_success = false; std::thread hpfs_serve_thread; diff --git a/src/hpfs/hpfs_sync.cpp b/src/hpfs/hpfs_sync.cpp index 82975a75..5f84b113 100644 --- a/src/hpfs/hpfs_sync.cpp +++ b/src/hpfs/hpfs_sync.cpp @@ -37,7 +37,6 @@ namespace hpfs this->name = name; this->fs_mount = fs_mount; - REQUEST_RESUBMIT_TIMEOUT = hpfs::get_request_resubmit_timeout(); hpfs_sync_thread = std::thread(&hpfs_sync::hpfs_syncer_loop, this); init_success = true; return 0; @@ -287,13 +286,16 @@ namespace hpfs candidate_hpfs_responses.clear(); + // No. of milliseconds to wait before resubmitting a request. + const uint16_t request_resubmit_timeout = hpfs::get_request_resubmit_timeout(); + // Check for long-awaited responses and re-request them. for (auto &[hash, request] : submitted_requests) { if (should_stop_request_loop(current_target_hash)) return 0; - if (request.waiting_time < REQUEST_RESUBMIT_TIMEOUT) + if (request.waiting_time < request_resubmit_timeout) { // Increment wait time. request.waiting_time += REQUEST_LOOP_WAIT; diff --git a/src/hpfs/hpfs_sync.hpp b/src/hpfs/hpfs_sync.hpp index 2fd4d2a1..659db0ad 100644 --- a/src/hpfs/hpfs_sync.hpp +++ b/src/hpfs/hpfs_sync.hpp @@ -48,8 +48,7 @@ namespace hpfs { private: bool init_success = false; - uint16_t REQUEST_RESUBMIT_TIMEOUT; // No. of milliseconds to wait before resubmitting a request. - std::string name; // Name used for logging. + std::string name; // Name used for logging. std::queue target_list; // The current target hashes we are syncing towards. diff --git a/src/msg/fbuf/p2pmsg_content.fbs b/src/msg/fbuf/p2pmsg_content.fbs index e807f272..a5a3847e 100644 --- a/src/msg/fbuf/p2pmsg_content.fbs +++ b/src/msg/fbuf/p2pmsg_content.fbs @@ -6,6 +6,7 @@ namespace msg.fbuf.p2pmsg; table Peer_Challenge_Message { contract_id:string; + roundtime:uint16; challenge:string; } @@ -52,6 +53,7 @@ table NonUnl_Proposal_Message { table Proposal_Message { //Proposal type message schema stage:uint8; time:uint64; + roundtime:uint16; nonce: [ubyte]; users:[ByteArray]; input_hashes:[ByteArray]; diff --git a/src/msg/fbuf/p2pmsg_content_generated.h b/src/msg/fbuf/p2pmsg_content_generated.h index 2afbe856..a6a4451c 100644 --- a/src/msg/fbuf/p2pmsg_content_generated.h +++ b/src/msg/fbuf/p2pmsg_content_generated.h @@ -81,7 +81,7 @@ struct Peer_List_Response_MessageBuilder; struct Peer_Properties; struct Peer_PropertiesBuilder; -enum Message : uint8_t { +enum Message { Message_NONE = 0, Message_Peer_Challenge_Response_Message = 1, Message_Peer_Challenge_Message = 2, @@ -206,7 +206,7 @@ template<> struct MessageTraits> *values, const flatbuffers::Vector *types); -enum Ledger_Response_Error : uint8_t { +enum Ledger_Response_Error { Ledger_Response_Error_None = 0, Ledger_Response_Error_Invalid_Min_Ledger = 1, Ledger_Response_Error_Req_Ledger_Not_Found = 2, @@ -239,7 +239,7 @@ inline const char *EnumNameLedger_Response_Error(Ledger_Response_Error e) { return EnumNamesLedger_Response_Error()[index]; } -enum Hpfs_Response : uint8_t { +enum Hpfs_Response { Hpfs_Response_NONE = 0, Hpfs_Response_File_HashMap_Response = 1, Hpfs_Response_Block_Response = 2, @@ -298,7 +298,8 @@ struct Peer_Challenge_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Tab typedef Peer_Challenge_MessageBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_CONTRACT_ID = 4, - VT_CHALLENGE = 6 + VT_ROUNDTIME = 6, + VT_CHALLENGE = 8 }; const flatbuffers::String *contract_id() const { return GetPointer(VT_CONTRACT_ID); @@ -306,6 +307,12 @@ struct Peer_Challenge_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Tab flatbuffers::String *mutable_contract_id() { return GetPointer(VT_CONTRACT_ID); } + uint16_t roundtime() const { + return GetField(VT_ROUNDTIME, 0); + } + bool mutate_roundtime(uint16_t _roundtime) { + return SetField(VT_ROUNDTIME, _roundtime, 0); + } const flatbuffers::String *challenge() const { return GetPointer(VT_CHALLENGE); } @@ -316,6 +323,7 @@ struct Peer_Challenge_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Tab return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_CONTRACT_ID) && verifier.VerifyString(contract_id()) && + VerifyField(verifier, VT_ROUNDTIME) && VerifyOffset(verifier, VT_CHALLENGE) && verifier.VerifyString(challenge()) && verifier.EndTable(); @@ -329,6 +337,9 @@ struct Peer_Challenge_MessageBuilder { void add_contract_id(flatbuffers::Offset contract_id) { fbb_.AddOffset(Peer_Challenge_Message::VT_CONTRACT_ID, contract_id); } + void add_roundtime(uint16_t roundtime) { + fbb_.AddElement(Peer_Challenge_Message::VT_ROUNDTIME, roundtime, 0); + } void add_challenge(flatbuffers::Offset challenge) { fbb_.AddOffset(Peer_Challenge_Message::VT_CHALLENGE, challenge); } @@ -336,6 +347,7 @@ struct Peer_Challenge_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Peer_Challenge_MessageBuilder &operator=(const Peer_Challenge_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -346,22 +358,26 @@ struct Peer_Challenge_MessageBuilder { inline flatbuffers::Offset CreatePeer_Challenge_Message( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset contract_id = 0, + uint16_t roundtime = 0, flatbuffers::Offset challenge = 0) { Peer_Challenge_MessageBuilder builder_(_fbb); builder_.add_challenge(challenge); builder_.add_contract_id(contract_id); + builder_.add_roundtime(roundtime); return builder_.Finish(); } inline flatbuffers::Offset CreatePeer_Challenge_MessageDirect( flatbuffers::FlatBufferBuilder &_fbb, const char *contract_id = nullptr, + uint16_t roundtime = 0, const char *challenge = nullptr) { auto contract_id__ = contract_id ? _fbb.CreateString(contract_id) : 0; auto challenge__ = challenge ? _fbb.CreateString(challenge) : 0; return msg::fbuf::p2pmsg::CreatePeer_Challenge_Message( _fbb, contract_id__, + roundtime, challenge__); } @@ -407,6 +423,7 @@ struct Peer_Challenge_Response_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Peer_Challenge_Response_MessageBuilder &operator=(const Peer_Challenge_Response_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -489,6 +506,7 @@ struct UserInputBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + UserInputBuilder &operator=(const UserInputBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -565,6 +583,7 @@ struct UserInputGroupBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + UserInputGroupBuilder &operator=(const UserInputGroupBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -724,6 +743,7 @@ struct ContentBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + ContentBuilder &operator=(const ContentBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -772,6 +792,7 @@ struct NonUnl_Proposal_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + NonUnl_Proposal_MessageBuilder &operator=(const NonUnl_Proposal_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -801,13 +822,14 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_STAGE = 4, VT_TIME = 6, - VT_NONCE = 8, - VT_USERS = 10, - VT_INPUT_HASHES = 12, - VT_OUTPUT_HASH = 14, - VT_OUTPUT_SIG = 16, - VT_STATE_HASH = 18, - VT_PATCH_HASH = 20 + VT_ROUNDTIME = 8, + VT_NONCE = 10, + VT_USERS = 12, + VT_INPUT_HASHES = 14, + VT_OUTPUT_HASH = 16, + VT_OUTPUT_SIG = 18, + VT_STATE_HASH = 20, + VT_PATCH_HASH = 22 }; uint8_t stage() const { return GetField(VT_STAGE, 0); @@ -821,6 +843,12 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_time(uint64_t _time) { return SetField(VT_TIME, _time, 0); } + uint16_t roundtime() const { + return GetField(VT_ROUNDTIME, 0); + } + bool mutate_roundtime(uint16_t _roundtime) { + return SetField(VT_ROUNDTIME, _roundtime, 0); + } const flatbuffers::Vector *nonce() const { return GetPointer *>(VT_NONCE); } @@ -867,6 +895,7 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return VerifyTableStart(verifier) && VerifyField(verifier, VT_STAGE) && VerifyField(verifier, VT_TIME) && + VerifyField(verifier, VT_ROUNDTIME) && VerifyOffset(verifier, VT_NONCE) && verifier.VerifyVector(nonce()) && VerifyOffset(verifier, VT_USERS) && @@ -897,6 +926,9 @@ struct Proposal_MessageBuilder { void add_time(uint64_t time) { fbb_.AddElement(Proposal_Message::VT_TIME, time, 0); } + void add_roundtime(uint16_t roundtime) { + fbb_.AddElement(Proposal_Message::VT_ROUNDTIME, roundtime, 0); + } void add_nonce(flatbuffers::Offset> nonce) { fbb_.AddOffset(Proposal_Message::VT_NONCE, nonce); } @@ -922,6 +954,7 @@ struct Proposal_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Proposal_MessageBuilder &operator=(const Proposal_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -933,6 +966,7 @@ inline flatbuffers::Offset CreateProposal_Message( flatbuffers::FlatBufferBuilder &_fbb, uint8_t stage = 0, uint64_t time = 0, + uint16_t roundtime = 0, flatbuffers::Offset> nonce = 0, flatbuffers::Offset>> users = 0, flatbuffers::Offset>> input_hashes = 0, @@ -949,6 +983,7 @@ inline flatbuffers::Offset CreateProposal_Message( builder_.add_input_hashes(input_hashes); builder_.add_users(users); builder_.add_nonce(nonce); + builder_.add_roundtime(roundtime); builder_.add_stage(stage); return builder_.Finish(); } @@ -957,6 +992,7 @@ inline flatbuffers::Offset CreateProposal_MessageDirect( flatbuffers::FlatBufferBuilder &_fbb, uint8_t stage = 0, uint64_t time = 0, + uint16_t roundtime = 0, const std::vector *nonce = nullptr, const std::vector> *users = nullptr, const std::vector> *input_hashes = nullptr, @@ -975,6 +1011,7 @@ inline flatbuffers::Offset CreateProposal_MessageDirect( _fbb, stage, time, + roundtime, nonce__, users__, input_hashes__, @@ -1014,6 +1051,7 @@ struct Npl_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Npl_MessageBuilder &operator=(const Npl_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1068,6 +1106,7 @@ struct History_Request_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + History_Request_MessageBuilder &operator=(const History_Request_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1146,6 +1185,7 @@ struct History_Response_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + History_Response_MessageBuilder &operator=(const History_Response_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1220,6 +1260,7 @@ struct HistoryLedgerBlockPairBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + HistoryLedgerBlockPairBuilder &operator=(const HistoryLedgerBlockPairBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1279,6 +1320,7 @@ struct HistoryLedgerBlockBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + HistoryLedgerBlockBuilder &operator=(const HistoryLedgerBlockBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1383,6 +1425,7 @@ struct Hpfs_Request_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Hpfs_Request_MessageBuilder &operator=(const Hpfs_Request_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1519,6 +1562,7 @@ struct Hpfs_Response_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Hpfs_Response_MessageBuilder &operator=(const Hpfs_Response_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1591,6 +1635,7 @@ struct Fs_Entry_ResponseBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Fs_Entry_ResponseBuilder &operator=(const Fs_Entry_ResponseBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1656,6 +1701,7 @@ struct File_HashMap_ResponseBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + File_HashMap_ResponseBuilder &operator=(const File_HashMap_ResponseBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1725,6 +1771,7 @@ struct Block_ResponseBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Block_ResponseBuilder &operator=(const Block_ResponseBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1806,6 +1853,7 @@ struct Hpfs_FS_Hash_EntryBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Hpfs_FS_Hash_EntryBuilder &operator=(const Hpfs_FS_Hash_EntryBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1868,6 +1916,7 @@ struct Peer_Requirement_Announcement_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Peer_Requirement_Announcement_MessageBuilder &operator=(const Peer_Requirement_Announcement_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1923,6 +1972,7 @@ struct Available_Capacity_Announcement_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Available_Capacity_Announcement_MessageBuilder &operator=(const Available_Capacity_Announcement_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -1956,6 +2006,7 @@ struct Peer_List_Request_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Peer_List_Request_MessageBuilder &operator=(const Peer_List_Request_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -2000,6 +2051,7 @@ struct Peer_List_Response_MessageBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Peer_List_Response_MessageBuilder &operator=(const Peer_List_Response_MessageBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); @@ -2087,6 +2139,7 @@ struct Peer_PropertiesBuilder { : fbb_(_fbb) { start_ = fbb_.StartTable(); } + Peer_PropertiesBuilder &operator=(const Peer_PropertiesBuilder &); flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); diff --git a/src/msg/fbuf/p2pmsg_helpers.cpp b/src/msg/fbuf/p2pmsg_helpers.cpp index 8be523af..1103fbcb 100644 --- a/src/msg/fbuf/p2pmsg_helpers.cpp +++ b/src/msg/fbuf/p2pmsg_helpers.cpp @@ -20,30 +20,30 @@ namespace msg::fbuf::p2pmsg constexpr size_t MAX_SIZE_FOR_TIME_CHECK = 1 * 1024 * 1024; // 1 MB /** - * This section contains Flatbuffer message reading/writing helpers. - * These helpers are mainly used by peer_session_handler. - * - * All Flatbuffer peer messages are 'Container' messages. 'Container' message is a bucket - * which some common headers (version, singature etc..) and the message 'Content' (Proposal, NPL etc..). - * - * Therefore, when constructing peer messages, we have to first construct 'Content' message and then - * place the 'Content' inside a 'Conatiner. 'Content' and 'Container' messages are constructed using - * Flatbuffer builders. - * - * Reading is also 2 steps because of this. We have first interprit the 'Container' message from the - * received data and then interprit the 'Content' portion of it separately to read the actual content. - */ + * This section contains Flatbuffer message reading/writing helpers. + * These helpers are mainly used by peer_session_handler. + * + * All Flatbuffer peer messages are 'Container' messages. 'Container' message is a bucket + * which some common headers (version, singature etc..) and the message 'Content' (Proposal, NPL etc..). + * + * Therefore, when constructing peer messages, we have to first construct 'Content' message and then + * place the 'Content' inside a 'Conatiner. 'Content' and 'Container' messages are constructed using + * Flatbuffer builders. + * + * Reading is also 2 steps because of this. We have first interprit the 'Container' message from the + * received data and then interprit the 'Content' portion of it separately to read the actual content. + */ //---Message validation helpers---/ /** - * Verifies Conatiner message structure and outputs faltbuffer Container pointer to access the given buffer. - * - * @param container_ref A pointer reference to assign the pointer to the Container object. - * @param container_buf The buffer containing the data that should be validated and interpreted - * via the container pointer. - * @return 0 on successful verification. -1 for failure. - */ + * Verifies Conatiner message structure and outputs faltbuffer Container pointer to access the given buffer. + * + * @param container_ref A pointer reference to assign the pointer to the Container object. + * @param container_buf The buffer containing the data that should be validated and interpreted + * via the container pointer. + * @return 0 on successful verification. -1 for failure. + */ int validate_and_extract_container(const Container **container_ref, std::string_view container_buf) { //Accessing message buffer @@ -123,14 +123,14 @@ namespace msg::fbuf::p2pmsg } /** - * Verifies the Content message structure and outputs faltbuffer Content pointer to access the given buffer. - * - * @param content_ref A pointer reference to assign the pointer to the Content object. - * @param content_ptr Pointer to the buffer containing the data that should validated and interpreted - * via the container pointer. - * @param content_size Data buffer size. - * @return 0 on successful verification. -1 for failure. - */ + * Verifies the Content message structure and outputs faltbuffer Content pointer to access the given buffer. + * + * @param content_ref A pointer reference to assign the pointer to the Content object. + * @param content_ptr Pointer to the buffer containing the data that should validated and interpreted + * via the container pointer. + * @param content_size Data buffer size. + * @return 0 on successful verification. -1 for failure. + */ int validate_and_extract_content(const Content **content_ref, const uint8_t *content_ptr, const flatbuffers::uoffset_t content_size) { //Defining Flatbuffer verifier for message content verification. @@ -151,22 +151,23 @@ namespace msg::fbuf::p2pmsg //---Message reading helpers---/ /** - * Returns challenge from the peer challenge message. - * @param The Flatbuffer peer challenge message received from the peer. - * @return Peer challenge struct. - */ + * Returns challenge from the peer challenge message. + * @param The Flatbuffer peer challenge message received from the peer. + * @return Peer challenge struct. + */ const p2p::peer_challenge get_peer_challenge_from_msg(const Peer_Challenge_Message &msg) { return { std::string(flatbuff_str_to_sv(msg.contract_id())), + msg.roundtime(), std::string(flatbuff_str_to_sv(msg.challenge()))}; } /** - * Creates a peer challenge response struct from the given peer challenge response message. - * @param The Flatbuffer peer challenge response message received from the peer. - * @return A peer challenge response struct representing the message. - */ + * Creates a peer challenge response struct from the given peer challenge response message. + * @param The Flatbuffer peer challenge response message received from the peer. + * @return A peer challenge response struct representing the message. + */ const p2p::peer_challenge_response create_peer_challenge_response_from_msg(const Peer_Challenge_Response_Message &msg, const flatbuffers::Vector *pubkey) { p2p::peer_challenge_response pchalresp; @@ -179,10 +180,10 @@ namespace msg::fbuf::p2pmsg } /** - * Creates a non-unl proposal stuct from the given non-unl proposal message. - * @param The Flatbuffer non-unl poporal received from the peer. - * @return A non-unl proposal struct representing the message. - */ + * Creates a non-unl proposal stuct from the given non-unl proposal message. + * @param The Flatbuffer non-unl poporal received from the peer. + * @return A non-unl proposal struct representing the message. + */ const p2p::nonunl_proposal create_nonunl_proposal_from_msg(const NonUnl_Proposal_Message &msg, const uint64_t timestamp) { p2p::nonunl_proposal nup; @@ -194,10 +195,10 @@ namespace msg::fbuf::p2pmsg } /** - * Creates a history response stuct from the given histrory response message. - * @param msg Flatbuffer History response message received from the peer. - * @return A History response struct representing the message. - */ + * Creates a history response stuct from the given histrory response message. + * @param msg Flatbuffer History response message received from the peer. + * @return A History response struct representing the message. + */ const p2p::history_response create_history_response_from_msg(const History_Response_Message &msg) { p2p::history_response hr; @@ -215,10 +216,10 @@ namespace msg::fbuf::p2pmsg } /** - * Creates a proposal stuct from the given proposal message. - * @param The Flatbuffer poposal received from the peer. - * @return A proposal struct representing the message. - */ + * Creates a proposal stuct from the given proposal message. + * @param The Flatbuffer poposal received from the peer. + * @return A proposal struct representing the message. + */ const p2p::proposal create_proposal_from_msg(const Proposal_Message &msg, const flatbuffers::Vector *pubkey, const uint64_t timestamp, const flatbuffers::Vector *lcl) { p2p::proposal p; @@ -227,6 +228,7 @@ namespace msg::fbuf::p2pmsg p.sent_timestamp = timestamp; p.recv_timestamp = util::get_epoch_milliseconds(); p.time = msg.time(); + p.roundtime = msg.roundtime(); p.nonce = flatbuff_bytes_to_sv(msg.nonce()); p.stage = msg.stage(); p.lcl = flatbuff_bytes_to_sv(lcl); @@ -249,10 +251,10 @@ namespace msg::fbuf::p2pmsg } /** - * Creates a history request struct from the given history request message. - * @param msg Flatbuffer History request message received from the peer. - * @return A History request struct representing the message. - */ + * Creates a history request struct from the given history request message. + * @param msg Flatbuffer History request message received from the peer. + * @return A History request struct representing the message. + */ const p2p::history_request create_history_request_from_msg(const History_Request_Message &msg, const flatbuffers::Vector *lcl) { p2p::history_request hr; @@ -284,10 +286,10 @@ namespace msg::fbuf::p2pmsg } /** - * Creates a peer property list from the given peer list response message. - * @param msg Flatbuffer Peer List response message received from the peer. - * @return A Peer list representing the message. - */ + * Creates a peer property list from the given peer list response message. + * @param msg Flatbuffer Peer List response message received from the peer. + * @return A Peer list representing the message. + */ const std::vector create_peer_list_response_from_msg(const Peer_List_Response_Message &msg) { return flatbuf_peer_propertieslist_to_peer_propertiesvector(msg.peer_list()); @@ -296,10 +298,10 @@ namespace msg::fbuf::p2pmsg //---Message creation helpers---// /** - * Create peer challenge message from the given challenge. - * @param container_builder Flatbuffer builder for the container message. - * @param challenge Challenge message needed to convert to flatbuffer message. - */ + * Create peer challenge message from the given challenge. + * @param container_builder Flatbuffer builder for the container message. + * @param challenge Challenge message needed to convert to flatbuffer message. + */ void create_msg_from_peer_challenge(flatbuffers::FlatBufferBuilder &container_builder, std::string &challenge) { flatbuffers::FlatBufferBuilder builder(1024); @@ -313,6 +315,7 @@ namespace msg::fbuf::p2pmsg CreatePeer_Challenge_Message( builder, sv_to_flatbuff_str(builder, conf::cfg.contract.id), + conf::cfg.contract.roundtime, sv_to_flatbuff_str(builder, challenge)); const flatbuffers::Offset message = CreateContent(builder, Message_Peer_Challenge_Message, peer_challenge_msg.Union()); @@ -323,10 +326,10 @@ namespace msg::fbuf::p2pmsg } /** - * Create peer challenge response message from the given challenge. - * @param container_builder Flatbuffer builder for the container message. - * @param challenge Message which need to be signed and placed in the container message. - */ + * Create peer challenge response message from the given challenge. + * @param container_builder Flatbuffer builder for the container message. + * @param challenge Message which need to be signed and placed in the container message. + */ void create_peer_challenge_response_from_challenge(flatbuffers::FlatBufferBuilder &container_builder, const std::string &challenge) { flatbuffers::FlatBufferBuilder builder(1024); @@ -363,10 +366,10 @@ namespace msg::fbuf::p2pmsg } /** - * Create proposal peer message from the given proposal struct. - * @param container_builder Flatbuffer builder for the container message. - * @param p The proposal struct to be placed in the container message. - */ + * Create proposal peer message from the given proposal struct. + * @param container_builder Flatbuffer builder for the container message. + * @param p The proposal struct to be placed in the container message. + */ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, const p2p::proposal &p) { // todo:get a average propsal message size and allocate content builder based on that. @@ -377,6 +380,7 @@ namespace msg::fbuf::p2pmsg builder, p.stage, p.time, + p.roundtime, sv_to_flatbuff_bytes(builder, p.nonce), stringlist_to_flatbuf_bytearrayvector(builder, p.users), stringlist_to_flatbuf_bytearrayvector(builder, p.input_hashes), @@ -394,11 +398,11 @@ namespace msg::fbuf::p2pmsg } /** - * Ctreat npl message from the given npl output srtuct. - * @param container_builder Flatbuffer builder for the container message. - * @param msg The message to be sent as NPL message. - * @param lcl Lcl value to be passed in the container message. - */ + * Ctreat npl message from the given npl output srtuct. + * @param container_builder Flatbuffer builder for the container message. + * @param msg The message to be sent as NPL message. + * @param lcl Lcl value to be passed in the container message. + */ void create_msg_from_npl_output(flatbuffers::FlatBufferBuilder &container_builder, const std::string_view &msg, std::string_view lcl) { flatbuffers::FlatBufferBuilder builder(1024); @@ -417,10 +421,10 @@ namespace msg::fbuf::p2pmsg } /** - * Create history request message from the given history request struct. - * @param container_builder Flatbuffer builder for the container message. - * @param hr The History request struct to be placed in the container message. - */ + * Create history request message from the given history request struct. + * @param container_builder Flatbuffer builder for the container message. + * @param hr The History request struct to be placed in the container message. + */ void create_msg_from_history_request(flatbuffers::FlatBufferBuilder &container_builder, const p2p::history_request &hr) { flatbuffers::FlatBufferBuilder builder(1024); @@ -439,10 +443,10 @@ namespace msg::fbuf::p2pmsg } /** - * Create history response message from the given history response struct. - * @param container_builder Flatbuffer builder for the container message. - * @param hr The History response struct to be placed in the container message. - */ + * Create history response message from the given history response struct. + * @param container_builder Flatbuffer builder for the container message. + * @param hr The History response struct to be placed in the container message. + */ void create_msg_from_history_response(flatbuffers::FlatBufferBuilder &container_builder, const p2p::history_response &hr) { flatbuffers::FlatBufferBuilder builder(1024); @@ -489,14 +493,14 @@ namespace msg::fbuf::p2pmsg } /** - * Create content response message from the given content response. - * @param container_builder Flatbuffer builder for the container message. - * @param path The path of the directory. - * @param mount_id The mount id of the relavent hpfs mount. - * @param hash_nodes File or directory entries with hashes in the given parent path. - * @param expected_hash The exptected hash of the requested path. - * @param lcl Lcl to be include in the container msg. - */ + * Create content response message from the given content response. + * @param container_builder Flatbuffer builder for the container message. + * @param path The path of the directory. + * @param mount_id The mount id of the relavent hpfs mount. + * @param hash_nodes File or directory entries with hashes in the given parent path. + * @param expected_hash The exptected hash of the requested path. + * @param lcl Lcl to be include in the container msg. + */ void create_msg_from_fsentry_response( flatbuffers::FlatBufferBuilder &container_builder, const std::string_view path, const uint32_t mount_id, std::vector &hash_nodes, util::h32 expected_hash, std::string_view lcl) @@ -523,13 +527,13 @@ namespace msg::fbuf::p2pmsg } /** - * Create content response message from the given content response. - * @param container_builder Flatbuffer builder for the container message. - * @param path The path of the directory. - * @param mount_id The mount id of the relavent hpfs mount. - * @param hashmap Hashmap of the file - * @param lcl Lcl to be include in the container msg. - */ + * Create content response message from the given content response. + * @param container_builder Flatbuffer builder for the container message. + * @param path The path of the directory. + * @param mount_id The mount id of the relavent hpfs mount. + * @param hashmap Hashmap of the file + * @param lcl Lcl to be include in the container msg. + */ void create_msg_from_filehashmap_response( flatbuffers::FlatBufferBuilder &container_builder, std::string_view path, const uint32_t mount_id, std::vector &hashmap, std::size_t file_length, util::h32 expected_hash, std::string_view lcl) @@ -561,12 +565,12 @@ namespace msg::fbuf::p2pmsg } /** - * Create content response message from the given content response. - * @param container_builder Flatbuffer builder for the container message. - * @param block_resp Block response struct to place in the message. - * @param mount_id The mount id of the relavent hpfs mount. - * @param lcl Lcl to be include in the container message. - */ + * Create content response message from the given content response. + * @param container_builder Flatbuffer builder for the container message. + * @param block_resp Block response struct to place in the message. + * @param mount_id The mount id of the relavent hpfs mount. + * @param lcl Lcl to be include in the container message. + */ void create_msg_from_block_response(flatbuffers::FlatBufferBuilder &container_builder, p2p::block_response &block_resp, const uint32_t mount_id, std::string_view lcl) { // todo:get a average propsal message size and allocate content builder based on that. @@ -683,12 +687,12 @@ namespace msg::fbuf::p2pmsg } /** - * Creates a Flatbuffer container message from the given Content message. - * @param container_builder The Flatbuffer builder to which the final container message should be written to. - * @param content_builder The Flatbuffer builder containing the content message that should be placed - * inside the container message. - * @param sign Whether to sign the message content. - */ + * Creates a Flatbuffer container message from the given Content message. + * @param container_builder The Flatbuffer builder to which the final container message should be written to. + * @param content_builder The Flatbuffer builder containing the content message that should be placed + * inside the container message. + * @param sign Whether to sign the message content. + */ void create_containermsg_from_content( flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder, std::string_view lcl, const bool sign) { @@ -854,11 +858,11 @@ namespace msg::fbuf::p2pmsg } /** - * Create peer list message from the given vector of peer properties structs. - * @param container_builder Flatbuffer builder for the container message. - * @param peers The Vector of peer properties to be placed in the container message. - * @param skipping_peer Peer that does not need to be sent. - */ + * Create peer list message from the given vector of peer properties structs. + * @param container_builder Flatbuffer builder for the container message. + * @param peers The Vector of peer properties to be placed in the container message. + * @param skipping_peer Peer that does not need to be sent. + */ const flatbuffers::Offset>> peer_propertiesvector_to_flatbuf_peer_propertieslist(flatbuffers::FlatBufferBuilder &builder, const std::vector &peers, const std::optional &skipping_ip_port) { @@ -879,9 +883,9 @@ namespace msg::fbuf::p2pmsg } /** - * Create vector of peer properties structs from the given peer list message. - * @param fbvec The peer list message to be convert to a list of peer properties structs. - */ + * Create vector of peer properties structs from the given peer list message. + * @param fbvec The peer list message to be convert to a list of peer properties structs. + */ const std::vector flatbuf_peer_propertieslist_to_peer_propertiesvector(const flatbuffers::Vector> *fbvec) { diff --git a/src/p2p/p2p.hpp b/src/p2p/p2p.hpp index 4803d2a5..f1e0b47c 100644 --- a/src/p2p/p2p.hpp +++ b/src/p2p/p2p.hpp @@ -23,10 +23,11 @@ namespace p2p std::string pubkey; uint64_t sent_timestamp = 0; // The timestamp of the sender when this proposal was sent. - uint64_t recv_timestamp = 0; // The timestamp when we received the proposal. (used for statistics) - uint64_t time = 0; // The time value that is voted on. - uint8_t stage = 0; - std::string nonce; // Random nonce that is used to reduce lcl predictability. + uint64_t recv_timestamp = 0; // The timestamp when we received the proposal. (used for network statistics) + uint64_t time = 0; // The descreet concensus time value that is voted on. + uint8_t stage = 0; // The round-stage that this proposal belongs to. + uint16_t roundtime = 0; // Roundtime of the proposer. + std::string nonce; // Random nonce that is used to reduce lcl predictability. std::string lcl; util::h32 state_hash; // Contract state hash. util::h32 patch_hash; // Patch file hash. @@ -56,6 +57,7 @@ namespace p2p struct peer_challenge { std::string contract_id; + uint16_t roundtime = 0; std::string challenge; }; @@ -145,7 +147,7 @@ namespace p2p // Holds all the messages until they are processed by consensus. message_collection collected_msgs; - // Set of currently connected peer connections mapped by the pubkey of socket session. + // Set of currently connected peer connections mapped by the binary pubkey of socket session. std::unordered_map peer_connections; std::mutex peer_connections_mutex; // Mutex for peer connections access race conditions. @@ -192,6 +194,7 @@ namespace p2p int16_t get_available_capacity(); void update_unl_connections(); + } // namespace p2p #endif \ No newline at end of file diff --git a/src/p2p/peer_comm_session.hpp b/src/p2p/peer_comm_session.hpp index 79613498..0d51d9da 100644 --- a/src/p2p/peer_comm_session.hpp +++ b/src/p2p/peer_comm_session.hpp @@ -24,6 +24,7 @@ namespace p2p std::optional known_ipport; // A known ip/port information that matches with our peer list configuration. bool need_consensus_msg_forwarding = false; // Holds whether this node requires consensus message forwarding. bool is_unl = false; // Whether this session's pubkey is in unl list. + uint16_t reported_roundtime = 0; // Initial roundtime reported by this peer on peer challenge. }; } // namespace p2p diff --git a/src/p2p/peer_session_handler.cpp b/src/p2p/peer_session_handler.cpp index 157122af..2e246dfa 100644 --- a/src/p2p/peer_session_handler.cpp +++ b/src/p2p/peer_session_handler.cpp @@ -109,6 +109,9 @@ namespace p2p return -1; } + // Remember the roundtime reported by this peer. + session.reported_roundtime = chall.roundtime; + // Sending the challenge response to the sender. flatbuffers::FlatBufferBuilder fbuf(1024); p2pmsg::create_peer_challenge_response_from_challenge(fbuf, chall.challenge); @@ -153,13 +156,9 @@ namespace p2p const p2pmsg::Peer_Requirement_Announcement_Message *announcement_msg = content->message_as_Peer_Requirement_Announcement_Message(); session.need_consensus_msg_forwarding = announcement_msg->need_consensus_msg_forwarding(); if (session.need_consensus_msg_forwarding) - { LOG_DEBUG << "Consensus message forwaring is required for " << session.display_name(); - } else - { LOG_DEBUG << "Consensus message forwaring is not required for " << session.display_name(); - } } else if (content_message_type == p2pmsg::Message_Proposal_Message) // message is a proposal message { diff --git a/src/unl.cpp b/src/unl.cpp index d09e0543..0c61d2a1 100644 --- a/src/unl.cpp +++ b/src/unl.cpp @@ -9,10 +9,9 @@ */ namespace unl { - std::set list; // List of binary pubkeys of UNL. - std::string json_list; // Stringified json array of UNL. (To be fed into the contract args) + std::map list; // List of binary pubkeys of UNL and their latest reported roundtime. + std::string json_list; // Stringified json array of UNL. (To be fed into the contract args) std::shared_mutex unl_mutex; - std::string hash; /** * Performs startup activitites related to unl list. @@ -24,11 +23,8 @@ namespace unl return -1; std::unique_lock lock(unl_mutex); - list = conf::cfg.contract.unl; - // Update the own node's unl status. - conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end()); - update_json_list(); - hash = calculate_hash(list); + update_unl_list(conf::cfg.contract.unl); + return 0; } @@ -38,13 +34,16 @@ namespace unl return list.size(); } - std::set get() + const std::set get() { std::shared_lock lock(unl_mutex); - return list; + std::set ret; + for (auto [pubkey, roundtime] : list) + ret.emplace(std::move(pubkey)); + return ret; } - std::string get_json() + const std::string get_json() { std::shared_lock lock(unl_mutex); return json_list; @@ -61,34 +60,6 @@ namespace unl return list.find(bin_pubkey) != list.end(); } - void update_json_list() - { - std::ostringstream os; - os << "["; - for (auto pk = list.begin(); pk != list.end(); pk++) - { - if (pk != list.begin()) - os << ","; // Trailing comma separator for previous element. - - // Convert binary pubkey into hex. - os << "\"" << util::to_hex(*pk) << "\""; - } - os << "]"; - json_list = os.str(); - } - - /** - * Calculate hash of the given set. - * @param unl_list UNL list. - * @return Returns the generated hash of the given list. - */ - std::string calculate_hash(const std::set &new_list) - { - std::vector unl_vector(new_list.begin(), new_list.end()); - return crypto::get_hash(unl_vector); - } - - /** * Replace the unl list from the latest unl list from patch file. */ @@ -97,17 +68,7 @@ namespace unl bool is_unl_list_changed = false; { std::unique_lock lock(unl_mutex); - const std::string updated_hash = calculate_hash(conf::cfg.contract.unl); - if (hash != updated_hash) - { - hash = updated_hash; - list = conf::cfg.contract.unl; - update_json_list(); - - // Update the own node's unl status. - conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end()); - is_unl_list_changed = true; - } + is_unl_list_changed = update_unl_list(conf::cfg.contract.unl); } // Update the is_unl flag of peer sessions. @@ -115,8 +76,128 @@ namespace unl if (is_unl_list_changed) { p2p::update_unl_connections(); - usr::announce_unl_list(list); + usr::announce_unl_list(conf::cfg.contract.unl); } } + /** + * Updates unl pubkey-roundtime information using the specified list of proposals. + */ + void update_roundtime_stats(const std::list &proposals) + { + std::unique_lock lock(unl_mutex); + + for (const auto &p : proposals) + { + const auto itr = list.find(p.pubkey); + if (itr != list.end()) + itr->second = p.roundtime; + } + } + + /** + * Returns the majority roundtime reported among the unl. + */ + uint16_t get_majority_roundtime() + { + std::unique_lock lock(unl_mutex); + + // Vote and find majority roundtime within the unl. + // Fill any 0 roundtimes with information from peer connections. + std::map roundtime_votes; + + { + std::scoped_lock lock(p2p::ctx.peer_connections_mutex); + + for (auto itr = list.begin(); itr != list.end(); itr++) + { + // If roundtime is 0, attempt to get from peer connection (if available). + if (itr->second == 0) + { + const auto peer_itr = p2p::ctx.peer_connections.find(itr->first); + if (peer_itr != p2p::ctx.peer_connections.end()) + itr->second = peer_itr->second->reported_roundtime; + } + + const uint16_t roundtime = itr->second; + if (roundtime > 0) + roundtime_votes[roundtime]++; + } + } + + // Find the majority vote. + uint32_t highest_votes = 0; + uint16_t majority_roundtime = 0; + for (const auto [roundtime, num_votes] : roundtime_votes) + { + if (num_votes > highest_votes) + { + highest_votes = num_votes; + majority_roundtime = roundtime; + } + } + + return majority_roundtime; + } + + /** + * Updates the unl list using the provided new list. + * @return Whether or not any unl list changes were made. + */ + bool update_unl_list(const std::set &new_list) + { + bool changes_made = false; + + // Erase any pubkeys from current unl list that does not exist in new config. + for (auto itr = list.begin(); itr != list.end();) + { + if (conf::cfg.contract.unl.count(itr->first) == 0) + { + itr = list.erase(itr); + changes_made = true; + } + else + { + itr++; + } + } + + // Add any pubkeys that are not in current unl list. + for (const std::string pubkey : conf::cfg.contract.unl) + { + if (list.count(pubkey) == 0) + { + list.emplace(pubkey, 0); + changes_made = true; + } + } + + if (!changes_made) + return false; + + // Update the prepared json list which will be fed into contract args. + json_list = prepare_json_list(new_list); + + // Update the own node's unl status. + conf::cfg.node.is_unl = (list.count(conf::cfg.node.public_key) == 1); + + return true; // Changes made. + } + + const std::string prepare_json_list(const std::set &new_list) + { + std::ostringstream os; + os << "["; + for (auto pk = new_list.begin(); pk != new_list.end(); pk++) + { + if (pk != new_list.begin()) + os << ","; // Trailing comma separator for previous element. + + // Convert binary pubkey into hex. + os << "\"" << util::to_hex(*pk) << "\""; + } + os << "]"; + return os.str(); + } + } // namespace unl diff --git a/src/unl.hpp b/src/unl.hpp index 5fe12f92..2d81c96f 100644 --- a/src/unl.hpp +++ b/src/unl.hpp @@ -12,13 +12,15 @@ namespace unl { size_t count(); - std::set get(); - std::string get_json(); + const std::set get(); + const std::string get_json(); bool exists(const std::string &bin_pubkey); int init(); - void update_json_list(); - std::string calculate_hash(const std::set &new_list); void update_unl_changes_from_patch(); + void update_roundtime_stats(const std::list &proposals); + uint16_t get_majority_roundtime(); + bool update_unl_list(const std::set &new_list); + const std::string prepare_json_list(const std::set &new_list); } // namespace unl diff --git a/test/local-cluster/cluster-start.sh b/test/local-cluster/cluster-start.sh index 9879dc0b..74d001c3 100755 --- a/test/local-cluster/cluster-start.sh +++ b/test/local-cluster/cluster-start.sh @@ -16,13 +16,14 @@ clusterloc=$(pwd)/hpcluster n=$1 let pubport=8080+$n -# let peerport=22860+$n #Uncomment if peer port needs to be exposed to host. +let peerport=22860+$n # Mount the node contract directory into hpcore docker container and run. # We specify --network=hpnet so all nodes will communicate via 'hpnet' docker virtual network. # We specify --name for each node so it will be the virtual dns name for each node. docker run --rm -t -i --network=hpnet --name=node${n} \ -p ${pubport}:${pubport} \ + -p ${peerport}:${peerport} \ --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \ --mount type=bind,source=${clusterloc}/node${n},target=/contract \ hpcore:latest run /contract \ No newline at end of file