diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 868006e62..2ac50aefc 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -72,7 +72,7 @@ DISABLE_VS_WARNINGS(4267) //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), -m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true) +m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_hardfork(), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -3082,3 +3082,8 @@ HardFork::State Blockchain::get_hard_fork_state() const { return m_hardfork.get_state(); } + +bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const +{ + return m_hardfork.get_voting_info(version, window, votes, threshold, voting); +} diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6a73faf4a..e549ea2d0 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -160,6 +160,9 @@ namespace cryptonote void set_show_time_stats(bool stats) { m_show_time_stats = stats; } HardFork::State get_hard_fork_state() const; + uint8_t get_current_hard_fork_version() const { return m_hardfork.get_current_version(); } + uint8_t get_ideal_hard_fork_version() const { return m_hardfork.get_ideal_version(); } + bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const; BlockchainDB& get_db() { diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index c7a69dadb..8ef45ab5a 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -135,7 +135,7 @@ void HardFork::init() starting[n] = 0; checkpoints.clear(); current_fork_index = 0; - vote_threshold = (unsigned int)ceilf(max_history * threshold_percent / 100.0f); + vote_threshold = (uint32_t)ceilf(max_history * threshold_percent / 100.0f); } bool HardFork::reorganize_from_block_height(const cryptonote::BlockchainDB *db, uint64_t height) @@ -186,7 +186,7 @@ bool HardFork::reorganize_from_chain_height(const cryptonote::BlockchainDB *db, int HardFork::get_voted_fork_index(uint64_t height) const { CRITICAL_REGION_LOCAL(lock); - unsigned int accumulated_votes = 0; + uint32_t accumulated_votes = 0; for (unsigned int n = heights.size() - 1; n > current_fork_index; --n) { uint8_t v = heights[n].version; accumulated_votes += last_versions[v]; @@ -247,6 +247,22 @@ uint8_t HardFork::get_ideal_version() const return heights.back().version; } +bool HardFork::get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const +{ + CRITICAL_REGION_LOCAL(lock); + + const uint8_t current_version = heights[current_fork_index].version; + const bool enabled = current_version >= version; + window = versions.size(); + votes = 0; + for (size_t n = version; n < 256; ++n) + votes += last_versions[n]; + threshold = vote_threshold; + assert((votes >= threshold) == enabled); + voting = heights.back().version; + return enabled; +} + template void HardFork::serialize(archive_t & ar, const unsigned int version) { diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_core/hardfork.h index b478dd0cf..813996b8c 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_core/hardfork.h @@ -162,6 +162,24 @@ namespace cryptonote */ uint8_t get_current_version() const; + /** + * @brief returns information about current voting state + * + * returns true if the given version is enabled (ie, the current version + * is at least the passed version), false otherwise + * + * @param version the version to check voting for + * @param window the number of blocks considered in voting + * @param votes number of votes for next version + * @param threshold number of votes needed to switch to next version + */ + bool get_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const; + + /** + * @brief returns the size of the voting window in blocks + */ + uint64_t get_window_size() const { return max_history; } + template void serialize(archive_t & ar, const unsigned int version); @@ -189,10 +207,10 @@ namespace cryptonote std::vector heights; std::deque versions; /* rolling window of the last N blocks' versions */ - unsigned int last_versions[256]; /* count of the block versions in the lsat N blocks */ + unsigned int last_versions[256]; /* count of the block versions in the last N blocks */ uint64_t starting[256]; /* block height at which each fork starts */ unsigned int current_fork_index; - unsigned int vote_threshold; + uint32_t vote_threshold; uint64_t checkpoint_period; std::vector> checkpoints; diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index e6666c443..7b0f4a66b 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -361,5 +361,27 @@ bool t_command_parser_executor::stop_save_graph(const std::vector& return m_executor.stop_save_graph(); } +bool t_command_parser_executor::hard_fork_info(const std::vector& args) +{ + int version; + if (args.size() == 0) { + version = 0; + } + else if (args.size() == 1) { + try { + version = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + if (version <= 0 || version > 255) + return false; + } + else { + return false; + } + return m_executor.hard_fork_info(version); +} + } // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index ddc207cfe..f900c72bd 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -102,6 +102,8 @@ public: bool start_save_graph(const std::vector& args); bool stop_save_graph(const std::vector& args); + + bool hard_fork_info(const std::vector& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 047b52c3e..446379558 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -184,6 +184,11 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1) , "Stop save data for dr monero" ); + m_command_lookup.set_handler( + "hard_fork_info" + , std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1) + , "Print hard fork voting information" + ); } bool t_command_server::process_command_str(const std::string& cmd) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 62f254c76..c4fe642e8 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -946,4 +946,35 @@ bool t_rpc_command_executor::stop_save_graph() return true; } +bool t_rpc_command_executor::hard_fork_info(uint8_t version) +{ + cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req; + cryptonote::COMMAND_RPC_HARD_FORK_INFO::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + req.version = version; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "/hard_fork_info", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_hard_fork_info(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + version = version > 0 ? version : res.voting; + tools::msg_writer() << "version " << (uint32_t)version << " " << (res.enabled ? "enabled" : "not enabled") << + ", " << res.votes << "/" << res.window << " votes, threshold " << res.threshold; + tools::msg_writer() << "current version " << (uint32_t)res.version << ", voting for version " << (uint32_t)res.voting; + } + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index b40a67bf8..9ad849434 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -120,6 +120,8 @@ public: bool start_save_graph(); bool stop_save_graph(); + + bool hard_fork_info(uint8_t version); }; } // namespace daemonize diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 80bd7e6cd..f9ff632ac 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -812,6 +812,29 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + +#if BLOCKCHAIN_DB == DB_LMDB + const Blockchain &blockchain = m_core.get_blockchain_storage(); + uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version(); + res.version = blockchain.get_current_hard_fork_version(); + res.enabled = blockchain.get_hard_fork_voting_info(version, res.window, res.votes, res.threshold, res.voting); + res.state = blockchain.get_hard_fork_state(); + return true; +#else + error_resp.code = CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC; + error_resp.message = "Hard fork inoperative in memory mode."; + return false; +#endif + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_fast_exit(const COMMAND_RPC_FAST_EXIT::request& req, COMMAND_RPC_FAST_EXIT::response& res) { cryptonote::core::set_fast_exit(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3213e6b1c..60e7d00d5 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -138,6 +138,7 @@ namespace cryptonote bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp); bool on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp); bool on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp); + bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp); //----------------------- private: diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index b2fdd9930..a806cbae9 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -815,5 +815,40 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_HARD_FORK_INFO + { + struct request + { + uint8_t version; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint8_t version; + bool enabled; + uint32_t window; + uint32_t votes; + uint32_t threshold; + uint8_t voting; + uint32_t state; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + KV_SERIALIZE(enabled) + KV_SERIALIZE(window) + KV_SERIALIZE(votes) + KV_SERIALIZE(threshold) + KV_SERIALIZE(voting) + KV_SERIALIZE(state) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 91659eb2a..72c72b94b 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -40,5 +40,6 @@ #define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 #define CORE_RPC_ERROR_CODE_CORE_BUSY -9 #define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10 +#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11