diff --git a/include/rta/fullsupernodelist.h b/include/rta/fullsupernodelist.h index 44c8a8c3..fd908d5f 100644 --- a/include/rta/fullsupernodelist.h +++ b/include/rta/fullsupernodelist.h @@ -29,6 +29,8 @@ class FullSupernodeList static constexpr int32_t TIERS = 4; static constexpr int32_t ITEMS_PER_TIER = 2; static constexpr int32_t AUTH_SAMPLE_SIZE = TIERS * ITEMS_PER_TIER; + static constexpr int32_t DISQUALIFICATION_SAMPLE_SIZE = AUTH_SAMPLE_SIZE; + static constexpr int32_t DISQUALIFICATION_CANDIDATES_SIZE = AUTH_SAMPLE_SIZE; static constexpr int64_t AUTH_SAMPLE_HASH_HEIGHT = 20; // block number for calculating auth sample should be calculated as current block height - AUTH_SAMPLE_HASH_HEIGHT; static constexpr int64_t ANNOUNCE_TTL_SECONDS = 60 * 60; // if more than ANNOUNCE_TTL_SECONDS passed from last annouce - supernode excluded from auth sample selection @@ -98,6 +100,15 @@ class FullSupernodeList bool buildAuthSample(const std::string& payment_id, supernode_array &out, uint64_t &out_auth_block_number); + /*! + * \brief buildDisqualificationSamples - builds disqualification samples for given block height + * \param height - block height used to perform selectio + * \param out_disqualification_sample - vector of supernode pointers which should check other nodes + * \param out_nodes_for_check - vector of supernode pointers which should be checked + * \return - true on success + */ + bool buildDisqualificationSamples(uint64_t height, supernode_array &out_disqualification_sample, supernode_array &out_disqualification_candidates); + /*! * \brief items - returns address list of known supernodes * \return @@ -143,8 +154,18 @@ class FullSupernodeList }; typedef std::vector blockchain_based_list_tier; - typedef std::vector blockchain_based_list; - typedef std::shared_ptr blockchain_based_list_ptr; + typedef std::vector blockchain_based_list_tier_array; + + struct blockchain_based_list + { + std::string block_hash; + blockchain_based_list_tier_array tiers; + + blockchain_based_list() {} + blockchain_based_list(const std::string& in_block_hash) : block_hash(in_block_hash) {} + }; + + typedef std::shared_ptr blockchain_based_list_ptr; /*! * \brief setBlockchainBasedList - updates full list of supernodes @@ -184,9 +205,10 @@ class FullSupernodeList * \brief getBlockchainBasedListForAuthSample - builds blockchain based list for specified block height and removes nodes which are not reachable * \param block_number - block height used to list building * \param out_list - resulting list + * \param use_delay - should delay be used for shifting block number * \return - block number which was used for base list */ - uint64_t getBlockchainBasedListForAuthSample(uint64_t block_number, blockchain_based_list& list) const; + uint64_t getBlockchainBasedListForAuthSample(uint64_t block_number, blockchain_based_list& list, bool use_delay = true) const; /*! * \brief synchronizeWithCryptonode - synchronize with cryptonode @@ -203,7 +225,8 @@ class FullSupernodeList private: // bool loadWallet(const std::string &wallet_path); void addImpl(SupernodePtr item); - bool selectSupernodes(size_t items_count, const std::string& payment_id, const blockchain_based_list_tier& src_array, supernode_array& dst_array); + bool selectSupernodes(size_t items_count, const blockchain_based_list_tier& src_array, supernode_array& dst_array); + bool buildSample(const blockchain_based_list& bbl, size_t sample_size, const char* prefix, supernode_array &out); typedef std::unordered_map blockchain_based_list_map; diff --git a/include/supernode/requests/blockchain_based_list.h b/include/supernode/requests/blockchain_based_list.h index 73f79170..e6a4f483 100644 --- a/include/supernode/requests/blockchain_based_list.h +++ b/include/supernode/requests/blockchain_based_list.h @@ -18,6 +18,7 @@ GRAFT_DEFINE_IO_STRUCT_INITED(BlockchainBasedListTier, GRAFT_DEFINE_IO_STRUCT_INITED(BlockchainBasedList, (uint64_t, block_height, uint64_t()), + (std::string, block_hash, std::string()), (std::vector, tiers, std::vector()) ); diff --git a/src/rta/fullsupernodelist.cpp b/src/rta/fullsupernodelist.cpp index b7fd3da0..b68a5795 100644 --- a/src/rta/fullsupernodelist.cpp +++ b/src/rta/fullsupernodelist.cpp @@ -240,7 +240,7 @@ SupernodePtr FullSupernodeList::get(const string &address) const return SupernodePtr(nullptr); } -bool FullSupernodeList::selectSupernodes(size_t items_count, const std::string& payment_id, const blockchain_based_list_tier& src_array, supernode_array& dst_array) +bool FullSupernodeList::selectSupernodes(size_t items_count, const blockchain_based_list_tier& src_array, supernode_array& dst_array) { size_t src_array_size = src_array.size(); @@ -278,11 +278,14 @@ bool FullSupernodeList::selectSupernodes(size_t items_count, const std::string& return true; } -uint64_t FullSupernodeList::getBlockchainBasedListForAuthSample(uint64_t block_number, blockchain_based_list& list) const +uint64_t FullSupernodeList::getBlockchainBasedListForAuthSample(uint64_t block_number, blockchain_based_list& list, bool use_delay) const { boost::shared_lock readerLock(m_access); - uint64_t blockchain_based_list_height = block_number - BLOCKCHAIN_BASED_LIST_DELAY_BLOCK_COUNT; + uint64_t blockchain_based_list_height = block_number; + + if (use_delay) + blockchain_based_list_height -= BLOCKCHAIN_BASED_LIST_DELAY_BLOCK_COUNT; blockchain_based_list_map::const_iterator it = m_blockchain_based_lists.find(block_number); @@ -292,7 +295,9 @@ uint64_t FullSupernodeList::getBlockchainBasedListForAuthSample(uint64_t block_n blockchain_based_list result; blockchain_based_list_ptr bbl = it->second; - for (blockchain_based_list_tier& src : *bbl) + result.block_hash = bbl->block_hash; + + for (blockchain_based_list_tier& src : bbl->tiers) { blockchain_based_list_tier dst; @@ -312,68 +317,34 @@ uint64_t FullSupernodeList::getBlockchainBasedListForAuthSample(uint64_t block_n return true; }); - result.emplace_back(std::move(dst)); + result.tiers.emplace_back(std::move(dst)); } - list.swap(result); + list = std::move(result); return blockchain_based_list_height; } -bool FullSupernodeList::buildAuthSample(uint64_t height, const std::string& payment_id, supernode_array &out, uint64_t &out_auth_block_number) +bool FullSupernodeList::buildSample(const blockchain_based_list& bbl, size_t sample_size, const char* prefix, supernode_array &out) { - blockchain_based_list bbl; + std::array tier_supernodes; - out_auth_block_number = getBlockchainBasedListForAuthSample(height, bbl); + //select supernodes for a full supernode list - if (!out_auth_block_number) + for (size_t i=0, tiers_count=bbl.tiers.size(); i tier_supernodes; - { - boost::unique_lock writerLock(m_access); - - //seed RNG - - std::seed_seq seed(reinterpret_cast(payment_id.c_str()), - reinterpret_cast(payment_id.c_str() + payment_id.size())); - - m_rng.seed(seed); - - //select supernodes for a full supernode list - - MDEBUG("use blockchain based list for height " << out_auth_block_number); - int t = 1; - for (const blockchain_based_list_tier& l : bbl) + if (!selectSupernodes(sample_size, src_array, dst_array)) { - MDEBUG("...tier #" << t); - int j=0; - for (const blockchain_based_list_entry& e : l) - MDEBUG(".....[" << j++ << "]=" << e.supernode_public_id); - t++; + LOG_ERROR("unable to select supernodes for " << prefix << " sample"); + return false; } - for (size_t i=0, tiers_count=bbl.size(); i select; @@ -409,24 +380,53 @@ bool FullSupernodeList::buildAuthSample(uint64_t height, const std::string& paym } if (VLOG_IS_ON(2)) { - std::string auth_sample_str, tier_sample_str; + std::string sample_str, tier_sample_str; for (const auto &a : out) { - auth_sample_str += a->idKeyAsString() + "\n"; + sample_str += a->idKeyAsString() + "\n"; } for (size_t i = 0; i < select.size(); i++) { if (i > 0) tier_sample_str += ", "; tier_sample_str += std::to_string(select[i]) + " T" + std::to_string(i+1); } - MDEBUG("selected " << tier_sample_str << " supernodes of " << size() << " for auth sample"); - MTRACE("auth sample: \n" << auth_sample_str); + MDEBUG("selected " << tier_sample_str << " supernodes of " << size() << " for " << prefix << " sample"); + MTRACE(prefix << " sample: \n" << sample_str); } - if (out.size() > AUTH_SAMPLE_SIZE) - out.resize(AUTH_SAMPLE_SIZE); + if (out.size() > sample_size) + out.resize(sample_size); MDEBUG("..." << out.size() << " supernodes has been selected"); - return out.size() == AUTH_SAMPLE_SIZE; + return out.size() == sample_size; +} + +bool FullSupernodeList::buildAuthSample(uint64_t height, const std::string& payment_id, supernode_array &out, uint64_t &out_auth_block_number) +{ + blockchain_based_list bbl; + + out_auth_block_number = getBlockchainBasedListForAuthSample(height, bbl); + + if (!out_auth_block_number) + { + LOG_ERROR("unable to build auth sample for block height " << height << " (blockchain_based_list_height=" << (height - BLOCKCHAIN_BASED_LIST_DELAY_BLOCK_COUNT) << ") and PaymentID " + << payment_id << ". Blockchain based list for this block is absent, latest block is " << getBlockchainBasedListMaxBlockNumber()); + return false; + } + + MDEBUG("building auth sample for height " << height << " (blockchain_based_list_height=" << out_auth_block_number << ") and PaymentID '" << payment_id << "'"); + + boost::unique_lock writerLock(m_access); + + //seed RNG + + std::seed_seq seed(reinterpret_cast(payment_id.c_str()), + reinterpret_cast(payment_id.c_str() + payment_id.size())); + + m_rng.seed(seed); + + //build sample + + return buildSample(bbl, AUTH_SAMPLE_SIZE, "auth", out); } bool FullSupernodeList::buildAuthSample(const string &payment_id, FullSupernodeList::supernode_array &out, uint64_t &out_auth_block_number) @@ -434,6 +434,36 @@ bool FullSupernodeList::buildAuthSample(const string &payment_id, FullSupernodeL return buildAuthSample(getBlockchainBasedListMaxBlockNumber(), payment_id, out, out_auth_block_number); } +bool FullSupernodeList::buildDisqualificationSamples(uint64_t height, supernode_array &out_disqualification_sample, supernode_array &out_disqualification_candidates) +{ + blockchain_based_list bbl; + + uint64_t out_auth_block_number = getBlockchainBasedListForAuthSample(height, bbl, false); + + if (!out_auth_block_number) + { + LOG_ERROR("unable to build disqualification samples for block height " << height << " (blockchain_based_list_height=" << (height - BLOCKCHAIN_BASED_LIST_DELAY_BLOCK_COUNT) << ") " + ". Blockchain based list for this block is absent, latest block is " << getBlockchainBasedListMaxBlockNumber()); + return false; + } + + MDEBUG("building disqualification samples for height " << height << " (blockchain_based_list_height=" << out_auth_block_number << ") and hash=" << bbl.block_hash); + + boost::unique_lock writerLock(m_access); + + //seed RNG + + std::seed_seq seed(reinterpret_cast(bbl.block_hash.c_str()), + reinterpret_cast(bbl.block_hash.c_str() + bbl.block_hash.size())); + + m_rng.seed(seed); + + //build sample + + return buildSample(bbl, DISQUALIFICATION_SAMPLE_SIZE, "disqualification", out_disqualification_sample) && + buildSample(bbl, DISQUALIFICATION_CANDIDATES_SIZE, "disqualification candidates", out_disqualification_candidates); +} + vector FullSupernodeList::items() const { boost::shared_lock readerLock(m_access); @@ -575,17 +605,6 @@ void FullSupernodeList::setBlockchainBasedList(uint64_t block_number, const bloc { boost::unique_lock writerLock(m_access); - MDEBUG("update blockchain based list for height " << block_number); - int t = 1; - for (const blockchain_based_list_tier& l : *list) - { - MDEBUG("...tier #" << t); - int j=0; - for (const blockchain_based_list_entry& e : l) - MDEBUG(".....[" << j++ << "]=" << e.supernode_public_id); - t++; - } - blockchain_based_list_map::iterator it = m_blockchain_based_lists.find(block_number); if (it != m_blockchain_based_lists.end()) @@ -637,7 +656,7 @@ size_t FullSupernodeList::getSupernodeBlockchainBasedListTier(const std::string& size_t tier_number = 1; - for (const blockchain_based_list_tier& tier : *list) + for (const blockchain_based_list_tier& tier : list->tiers) { for (const blockchain_based_list_entry& sn : tier) if (sn.supernode_public_id == supernode_public_id) diff --git a/src/supernode/requests/blockchain_based_list.cpp b/src/supernode/requests/blockchain_based_list.cpp index 8cc82696..ad3e3ef2 100644 --- a/src/supernode/requests/blockchain_based_list.cpp +++ b/src/supernode/requests/blockchain_based_list.cpp @@ -78,7 +78,7 @@ Status blockchainBasedListHandler //handle tiers - FullSupernodeList::blockchain_based_list tiers; + FullSupernodeList::blockchain_based_list bbl(req.params.block_hash); for (const BlockchainBasedListTier& tier : req.params.tiers) { @@ -99,11 +99,11 @@ Status blockchainBasedListHandler supernodes.emplace_back(std::move(entry)); } - tiers.emplace_back(std::move(supernodes)); + bbl.tiers.emplace_back(std::move(supernodes)); } fsl->setBlockchainBasedList(req.params.block_height, FullSupernodeList::blockchain_based_list_ptr( - new FullSupernodeList::blockchain_based_list(std::move(tiers)))); + new FullSupernodeList::blockchain_based_list(std::move(bbl)))); return Status::Ok; } diff --git a/src/supernode/requests/debug.cpp b/src/supernode/requests/debug.cpp index f4fe8846..4e560b45 100644 --- a/src/supernode/requests/debug.cpp +++ b/src/supernode/requests/debug.cpp @@ -68,7 +68,7 @@ Status getSupernodeList(const Router::vars_t& vars, const graft::Input& input, auto is_supernode_available = [&](const std::string& supernode_public_id) { - for (const FullSupernodeList::blockchain_based_list_tier& tier : auth_sample_base_list) + for (const FullSupernodeList::blockchain_based_list_tier& tier : auth_sample_base_list.tiers) { for (const FullSupernodeList::blockchain_based_list_entry& entry : tier) if (supernode_public_id == entry.supernode_public_id) @@ -235,7 +235,7 @@ Status getBlockchainBasedListImpl(const Router::vars_t& vars, const graft::Input } } - for (const FullSupernodeList::blockchain_based_list_tier& src_tier : bbl) + for (const FullSupernodeList::blockchain_based_list_tier& src_tier : bbl.tiers) { std::vector dst_tier;