diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index f3e02dfb2..29adb00d3 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -400,7 +400,7 @@ uint64_t BlockchainDB::add_block( const std::pair& blck } else { // Prior to activation of conversions, the staking reward is purely a percentage of the block reward - if (blk.miner_tx.amount_burnt == 0) + if (blk.miner_tx.amount_burnt == 0 and prev_height != 0) throw std::runtime_error("Staking reward is zero, but block reward is present"); slippage_total = blk.miner_tx.amount_burnt; } diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 845536d8c..d2db9a8f0 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -197,14 +197,6 @@ struct txpool_tx_meta_t } }; -typedef struct yield_block_info { - uint64_t block_height; - uint64_t slippage_total_this_block; - uint64_t locked_coins_this_block; - uint64_t locked_coins_tally; - uint8_t network_health_percentage; -} yield_block_info; - typedef struct yield_tx_info { uint64_t block_height; crypto::hash tx_hash; diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 588ed1caf..d885e8241 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -498,6 +498,24 @@ namespace cryptonote return boost::apply_visitor(txin_signature_size_visitor(), tx_in); } + struct yield_block_info { + uint64_t block_height; + uint64_t slippage_total_this_block; + uint64_t locked_coins_this_block; + uint64_t locked_coins_tally; + uint8_t network_health_percentage; + + BEGIN_SERIALIZE() + VARINT_FIELD(block_height) + VARINT_FIELD(slippage_total_this_block) + VARINT_FIELD(locked_coins_this_block) + VARINT_FIELD(locked_coins_tally) + VARINT_FIELD(network_health_percentage) + END_SERIALIZE() + }; + + + /************************************************************************/ /* */ /************************************************************************/ diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index a853404e6..5d6cc13e8 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -38,6 +38,7 @@ #include #include #include +#include "blockchain_db/blockchain_db.h" #include "cryptonote_basic.h" #include "difficulty.h" #include "common/unordered_containers_boost_serialization.h" @@ -236,6 +237,16 @@ namespace boost a & pr.signature; } + template + inline void serialize(Archive &a, cryptonote::yield_block_info &ybi, const boost::serialization::version_type ver) + { + a & ybi.block_height; + a & ybi.slippage_total_this_block; + a & ybi.locked_coins_this_block; + a & ybi.locked_coins_tally; + a & ybi.network_health_percentage; + } + template inline void serialize(Archive &a, cryptonote::block &b, const boost::serialization::version_type ver) { diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index dd62ee8b6..8ab898d45 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4393,6 +4393,27 @@ bool Blockchain::validate_ybi_cache() return true; } //------------------------------------------------------------------ +bool Blockchain::get_ybi_cache(std::map& ybi_cache) +{ + LOG_PRINT_L3("Blockchain::" << __func__); + + // Clear the provided container + ybi_cache.clear(); + + // Make sure the cache is fully populated and up to date + if (!validate_ybi_cache()) { + LOG_PRINT_L1("yield information cache is invalid - rebuilding cache"); + if (!rebuild_ybi_cache()) { + LOG_ERROR("Failed to rebuild yield information cache - aborting"); + return false; + } + } + + // Copy the cache + ybi_cache = m_yield_block_info_cache; + return true; +} +//------------------------------------------------------------------ bool Blockchain::get_ybi_entry(const uint64_t height, cryptonote::yield_block_info& ybi) { LOG_PRINT_L3("Blockchain::" << __func__); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index c99a55119..5653449e2 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1154,6 +1154,17 @@ namespace cryptonote */ bool calculate_yield_payouts(const uint64_t start_height, std::vector>& yield_payouts); + /** + * @brief get the complete YBI cache + * + * Retrieve the YBI local cache. + * If the cache is out of date, the cache will (attempt to) be rebuilt + * before being returned. + * + * @return TRUE if the call is successful, FALSE otherwise + */ + bool get_ybi_cache(std::map& ybi_cache); + /** * @brief get the YBI entry for a particular height from the cache * diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index ddf6290d5..9c62d91ee 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2939,6 +2939,35 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_yield_info(const COMMAND_RPC_GET_YIELD_INFO::request& req, COMMAND_RPC_GET_YIELD_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) + { + PERF_TIMER(on_get_yield_info); + uint64_t height = m_core.get_current_blockchain_height(); + std::map ybi_cache; + if (!m_core.get_blockchain_storage().get_ybi_cache(ybi_cache)) { + res.status = "failed to get YBI data from blockchain"; + return true; + } + // Iterate over the cache, supplying the data in a more accessible format + res.yield_data.clear(); + for (const auto& entry: ybi_cache) { + // Skip this entry if out-of=range + if (req.from_height > 0 and entry.first < req.from_height) continue; + if (req.to_height > 0 and entry.first > req.to_height) continue; + + // Clone the data into the response + COMMAND_RPC_GET_YIELD_INFO::yield_data_t yd; + yd.block_height = entry.second.block_height; + yd.slippage_total_this_block = entry.second.slippage_total_this_block; + yd.locked_coins_this_block = entry.second.locked_coins_this_block; + yd.locked_coins_tally = entry.second.locked_coins_tally; + yd.network_health_percentage = entry.second.network_health_percentage; + res.yield_data.push_back(yd); + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) { RPC_TRACKER(get_base_fee_estimate); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 6e46ab89e..fe8f8a75b 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -175,6 +175,7 @@ namespace cryptonote MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted) MAP_JON_RPC_WE("get_circulating_supply", on_get_circulating_supply, COMMAND_RPC_GET_CIRCULATING_SUPPLY) + MAP_JON_RPC_WE("get_yield_info", on_get_yield_info, COMMAND_RPC_GET_YIELD_INFO) MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) @@ -253,6 +254,7 @@ namespace cryptonote bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_circulating_supply(const COMMAND_RPC_GET_CIRCULATING_SUPPLY::request& req, COMMAND_RPC_GET_CIRCULATING_SUPPLY::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); + bool on_get_yield_info(const COMMAND_RPC_GET_YIELD_INFO::request& req, COMMAND_RPC_GET_YIELD_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 38fdbc401..aa34ebcff 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1225,9 +1225,31 @@ namespace cryptonote struct COMMAND_RPC_GET_YIELD_INFO { + struct yield_data_t + { + uint64_t block_height; + uint64_t slippage_total_this_block; + uint64_t locked_coins_this_block; + uint64_t locked_coins_tally; + uint8_t network_health_percentage; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(block_height) + KV_SERIALIZE(slippage_total_this_block) + KV_SERIALIZE(locked_coins_this_block) + KV_SERIALIZE(locked_coins_tally) + KV_SERIALIZE(network_health_percentage) + END_KV_SERIALIZE_MAP() + }; + struct request_t { + uint64_t from_height; + uint64_t to_height; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(from_height, (uint64_t)0) + KV_SERIALIZE_OPT(to_height, (uint64_t)0) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; @@ -1235,10 +1257,11 @@ namespace cryptonote struct response_t { - cryptonote::yield_block_info& latest_ybi; - + std::string status; + std::vector yield_data; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(latest_ybi) + KV_SERIALIZE(status) + KV_SERIALIZE(yield_data) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 064fc90f2..3cdc52d12 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8167,25 +8167,41 @@ bool simple_wallet::supply_info(const std::vector &args) { } //---------------------------------------------------------------------------------------------------- bool simple_wallet::yield_info(const std::vector &args) { - // get circulating supply + + // Get the total circulating supply of SALs std::vector> supply_amounts; if(!m_wallet->get_circulating_supply(supply_amounts)) { fail_msg_writer() << "failed to get circulating supply. Make sure you are connected to a daemon."; return false; } - - // get pricing record - std::string err; - uint64_t bc_height = get_daemon_blockchain_height(err); - oracle::pricing_record pr; - if (!m_wallet->get_pricing_record(pr, bc_height-1)) { - fail_msg_writer() << "failed to get pricing record. Make sure you are connected to a daemon."; - return false; + boost::multiprecision::uint128_t total_supply_128 = 0; + for (auto supply_asset: supply_amounts) { + if (supply_asset.first == "SAL") { + boost::multiprecision::uint128_t supply_128(supply_asset.second); + total_supply_128 = supply_128; + break; + } } - - // calculate current block cap - //uint64_t block_cap = cryptonote::get_block_cap(supply_amounts, pr, m_wallet->get_current_hard_fork()); - //message_writer() << boost::format(tr("Current Block Cap(height %d): %d XHV")) % bc_height % print_money(block_cap); + + // Get the yield data from the blockchain + std::vector ybi_data; + bool r = m_wallet->get_yield_info(ybi_data); + if (!r) + return false; + + // Scan the entries we have received to gather the state (total yield over period captured) + uint64_t total_yield = 0; + for (const auto& entry: ybi_data) { + total_yield += entry.slippage_total_this_block; + } + + // Output the necessary information + message_writer(console_color_default, false) << boost::format(tr("YIELD INFO:\n\tTotal SAL supply: %d\n\tTotal coins locked: %d\n\tYield accrued over %s: %d")) + % print_money(total_supply_128.convert_to()) + % print_money(ybi_data.back().locked_coins_tally) + % get_human_readable_timespan(ybi_data.size() * DIFFICULTY_TARGET_V2) + % print_money(total_yield); + return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 74d5f2520..56b84272e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2035,6 +2035,36 @@ bool wallet2::get_circulating_supply(std::vector& ybi_data) +{ + // Issue an RPC call to get the block header (and thus the pricing record) at the specified height + cryptonote::COMMAND_RPC_GET_YIELD_INFO::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_YIELD_INFO::response res = AUTO_VAL_INIT(res); + m_daemon_rpc_mutex.lock(); + bool r = invoke_http_json_rpc("/json_rpc", "get_yield_info", req, res, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + if (r && res.status == CORE_RPC_STATUS_OK) + { + ybi_data.clear(); + for (const auto& entry: res.yield_data) { + // Create a YBI + cryptonote::yield_block_info ybi; + ybi.block_height = entry.block_height; + ybi.slippage_total_this_block = entry.slippage_total_this_block; + ybi.locked_coins_this_block = entry.locked_coins_this_block; + ybi.locked_coins_tally = entry.locked_coins_tally; + ybi.network_health_percentage = entry.network_health_percentage; + ybi_data.push_back(ybi); + } + return true; + } + else + { + MERROR("Failed to retrieve yield info from daemon"); + return false; + } +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, const std::vector &asset_type_output_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map, size_t> *output_tracker_cache) { PERF_TIMER(process_new_transaction); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index afa4eece1..d5b806824 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1694,6 +1694,7 @@ private: bool get_pricing_record(oracle::pricing_record& pr, const uint64_t height); bool get_circulating_supply(std::vector> &amounts); + bool get_yield_info(std::vector& ybi_data); private: /*!