diff --git a/include/mmx/Node.h b/include/mmx/Node.h index 380842e00..9b82d803c 100644 --- a/include/mmx/Node.h +++ b/include/mmx/Node.h @@ -198,10 +198,10 @@ class Node : public NodeBase { std::vector> get_farmed_blocks( const std::vector& farmer_keys, const vnx::bool_t& full_blocks, const uint32_t& since = 0, const int32_t& limit = -1) const override; - std::map get_farmed_block_count(const uint32_t& since) const override; - farmed_block_summary_t get_farmed_block_summary(const std::vector& farmer_keys, const uint32_t& since = 0) const override; + std::vector> get_farmer_ranking(const int32_t& limit) const override; + void on_stuck_timeout(); void start_sync(const vnx::bool_t& force) override; @@ -386,6 +386,8 @@ class Node : public NodeBase { void purge_tree(); + void update_farmer_ranking(); + void add_proof( const uint32_t height, const hash_t& challenge, std::shared_ptr proof, const vnx::Hash64 farmer_mac); @@ -516,6 +518,8 @@ class Node : public NodeBase { hash_multi_table vplot_map; // [farmer key => contract] hash_multi_table farmer_block_map; // [farmer key => info] + std::vector> farmer_ranking; // sorted by count DSC [farmer key => num. blocks] + uint32_t sync_pos = 0; // current sync height uint32_t sync_retry = 0; int64_t sync_finish_ms = 0; // when peak was reached diff --git a/modules/Node.vni b/modules/Node.vni index 17d23abc8..32d843991 100644 --- a/modules/Node.vni +++ b/modules/Node.vni @@ -270,10 +270,10 @@ module Node implements vnx.addons.HttpComponent { vector get_farmed_blocks(vector farmer_keys, bool full_blocks, uint since, int limit = 100) const; @Permission(permission_e.PUBLIC) - map get_farmed_block_count(uint since) const; + farmed_block_summary_t get_farmed_block_summary(vector farmer_keys, uint since) const; @Permission(permission_e.PUBLIC) - farmed_block_summary_t get_farmed_block_summary(vector farmer_keys, uint since) const; + vector> get_farmer_ranking(int limit = -1) const; void start_sync(bool force); diff --git a/src/Node.cpp b/src/Node.cpp index 5ff8d0ae8..422340c97 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -172,10 +172,20 @@ void Node::main() balance_count++; return true; }); - + { + std::map farmer_block_count; + farmer_block_map.scan([&farmer_block_count](const pubkey_t& key, const farmed_block_info_t& info) -> bool { + farmer_block_count[key]++; + return true; + }); + for(const auto& entry : farmer_block_count) { + farmer_ranking.push_back(entry); + } + update_farmer_ranking(); + } if(height) { - log(INFO) << "Loaded DB at height " << (height - 1) << ", " << balance_count << " balances, " << mmx_address_count << " addresses" - << ", took " << (vnx::get_wall_time_millis() - time_begin) / 1e3 << " sec"; + log(INFO) << "Loaded DB at height " << (height - 1) << ", " << balance_count << " balances, " << mmx_address_count << " addresses, " + << farmer_ranking.size() << " farmers, took " << (vnx::get_wall_time_millis() - time_begin) / 1e3 << " sec"; } } block_chain = std::make_shared(storage_path + "block_chain.dat"); @@ -861,6 +871,14 @@ void Node::purge_tree() } } +void Node::update_farmer_ranking() +{ + std::sort(farmer_ranking.begin(), farmer_ranking.end(), + [](const std::pair& L, const std::pair& R) -> bool { + return L.second > R.second; + }); +} + void Node::commit(std::shared_ptr block) { if(!history.empty()) { @@ -1029,6 +1047,14 @@ void Node::apply( std::shared_ptr block, info.reward = block->reward_amount; info.reward_addr = (block->reward_addr ? *block->reward_addr : addr_t()); farmer_block_map.insert(proof->farmer_key, info); + + for(auto& entry : farmer_ranking) { + if(entry.first == proof->farmer_key) { + entry.second++; + break; + } + } + update_farmer_ranking(); } hash_index.insert(block->hash, block->height); @@ -1140,6 +1166,25 @@ void Node::apply( std::shared_ptr block, void Node::revert(const uint32_t height) { const auto time_begin = vnx::get_wall_time_millis(); + + if(const auto peak = get_peak()) { + // revert farmer_ranking + for(int64_t i = peak->height; i >= int64_t(height); --i) { + if(const auto header = get_header_at(i)) { + if(const auto proof = header->proof) { + for(auto& entry : farmer_ranking) { + if(entry.first == proof->farmer_key) { + if(entry.second) { + entry.second--; + } + break; + } + } + } + } + } + update_farmer_ranking(); + } if(block_chain) { block_index_t entry; if(block_index.find(height, entry)) { diff --git a/src/Node_api.cpp b/src/Node_api.cpp index 3ce2172c8..3c13a1ef2 100644 --- a/src/Node_api.cpp +++ b/src/Node_api.cpp @@ -70,7 +70,7 @@ #include #include #include -#include +#include #include #include @@ -1341,19 +1341,6 @@ std::vector> Node::get_farmed_blocks( return out; } -std::map Node::get_farmed_block_count(const uint32_t& since) const -{ - std::map out; - // TODO: optimize somehow - farmer_block_map.scan([&out, since](const pubkey_t& key, const farmed_block_info_t& info) -> bool { - if(info.height >= since) { - out[key]++; - } - return true; - }); - return out; -} - farmed_block_summary_t Node::get_farmed_block_summary(const std::vector& farmer_keys, const uint32_t& since) const { farmed_block_summary_t out; @@ -1372,6 +1359,22 @@ farmed_block_summary_t Node::get_farmed_block_summary(const std::vector> Node::get_farmer_ranking(const int32_t& limit) const +{ + if(limit < 0) { + return farmer_ranking; + } + std::vector> out; + for(const auto& entry : farmer_ranking) { + if(out.size() < size_t(limit)) { + out.push_back(entry); + } else { + break; + } + } + return out; +} + void Node::http_request_async( std::shared_ptr request, const std::string& sub_path, const vnx::request_id_t& request_id) const { @@ -1468,7 +1471,7 @@ std::shared_ptr Node::vnx_call_switch(std::shared_ptradd_task(std::bind(&Node::async_api_call, this, method, request_id)); diff --git a/src/WebAPI.cpp b/src/WebAPI.cpp index 15b36ca0e..753134be0 100644 --- a/src/WebAPI.cpp +++ b/src/WebAPI.cpp @@ -1559,30 +1559,19 @@ void WebAPI::http_request_async(std::shared_ptr } } else if(sub_path == "/farmers") { - const auto iter_since = query.find("since"); const auto iter_limit = query.find("limit"); const auto iter_offset = query.find("offset"); - uint32_t since = iter_since != query.end() ? vnx::from_string(iter_since->second) : 0; - const uint64_t limit = iter_limit != query.end() ? vnx::from_string(iter_limit->second) : 100; - const uint64_t offset = iter_offset != query.end() ? vnx::from_string(iter_offset->second) : 0; + const uint32_t limit = iter_limit != query.end() ? vnx::from_string(iter_limit->second) : 100; + const uint32_t offset = iter_offset != query.end() ? vnx::from_string(iter_offset->second) : 0; if(is_public) { - if(offset + limit > 10000 || (offset >> 32) || (limit >> 32)) { + if(offset + limit > 10000 || (offset >> 30) || (limit >> 30)) { throw std::logic_error("offset + limit > 10000"); } - const auto max_delta = 3153600u; - if(curr_height > max_delta) { - since = std::max(since, curr_height - max_delta); - } } - node->get_farmed_block_count(since, - [this, request_id, limit, offset](const std::map& result) { - std::vector> data(result.begin(), result.end()); - std::sort(data.begin(), data.end(), - [](const std::pair& L, std::pair& R) -> bool { - return L.second > R.second; - }); + node->get_farmer_ranking(offset + limit, + [this, request_id, limit, offset](const std::vector>& result) { std::vector out; - for(const auto& entry : get_page(data, limit, offset)) { + for(const auto& entry : get_page(result, limit, offset)) { vnx::Object tmp; tmp["farmer_key"] = entry.first.to_string(); tmp["block_count"] = entry.second;