From 7dec6530e7f8bdb6d7a6fc7d5bbfef00b8333f41 Mon Sep 17 00:00:00 2001 From: victor-tucci Date: Wed, 10 Dec 2025 15:51:20 +0530 Subject: [PATCH 1/3] Fix wallet RPC crashing and/or returning null ID The wallet was crashing if you passed in an integer ID in the JSON RPC command because it was only expecting to read a string for it. This meant returning the try-catch into epee's serialisation for `get_value()` which on paper, looks and should be a non-exception throwing function on failure as it returns a boolean and there's only one reasonable way for it to fail, that the key doesn't coerce to the requested type. Fix a null ID being assigned to the JSON response on error by setting the ID in the error. --- .../include/epee/storages/portable_storage.h | 17 ++++++++++------- contrib/epee/src/portable_storage.cpp | 13 ++++++++----- src/wallet/wallet_rpc_server.cpp | 15 ++++++++++++++- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/contrib/epee/include/epee/storages/portable_storage.h b/contrib/epee/include/epee/storages/portable_storage.h index b1edd1d8a63..311b3f43d15 100755 --- a/contrib/epee/include/epee/storages/portable_storage.h +++ b/contrib/epee/include/epee/storages/portable_storage.h @@ -180,15 +180,18 @@ namespace epee bool portable_storage::get_value(const std::string& value_name, T& val, section* parent_section) { static_assert(variant_contains); - //TRY_ENTRY(); - if(!parent_section) parent_section = &m_root; + if (!parent_section) + parent_section = &m_root; storage_entry* pentry = find_storage_entry(value_name, parent_section); - if(!pentry) - return false; + if (!pentry) + return false; - var::visit([&val](const auto& v) { convert_t(v, val); }, *pentry); - return true; - //CATCH_ENTRY("portable_storage::template<>get_value", false); + try { + var::visit([&val](const auto& v) { convert_t(v, val); }, *pentry); + return true; + } catch (const std::exception&) { + return false; + } } //--------------------------------------------------------------------------------------------------------------- template diff --git a/contrib/epee/src/portable_storage.cpp b/contrib/epee/src/portable_storage.cpp index 202c4562d2f..668c882019e 100755 --- a/contrib/epee/src/portable_storage.cpp +++ b/contrib/epee/src/portable_storage.cpp @@ -169,15 +169,18 @@ namespace epee { bool portable_storage::get_value(const std::string& value_name, storage_entry& val, section* parent_section) { - //TRY_ENTRY(); - if(!parent_section) parent_section = &m_root; + if (!parent_section) + parent_section = &m_root; storage_entry* pentry = find_storage_entry(value_name, parent_section); if(!pentry) return false; - val = *pentry; - return true; - //CATCH_ENTRY("portable_storage::template<>get_value", false); + try { + val = *pentry; + return true; + } catch (const std::exception&) { + return false; + } } storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, section* psection) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 078aeed88fd..5e8c8eb0897 100755 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -223,8 +223,21 @@ namespace tools if(!ps.load_from_json(body)) return jsonrpc_error_response(res, -32700, "Parse error", {}); + nlohmann::json id; epee::serialization::storage_entry epee_id{std::string{}}; - ps.get_value("id", epee_id, nullptr); + if (std::string str_val; ps.get_value("id", str_val, nullptr)) { + epee_id = str_val; + id = std::move(str_val); + } else if (int64_t i64_val; ps.get_value("id", i64_val, nullptr)) { + epee_id = i64_val; + id = i64_val; + } else { + return jsonrpc_error_response( + res, + -32700, + "Parse error, missing a valid (string or integer) JSON RPC 'id'", + {}); + } nlohmann::json id = var::get(epee_id); From 520c3d42e2fe7f6af778907682c232da5da8f51f Mon Sep 17 00:00:00 2001 From: victor-tucci Date: Fri, 12 Dec 2025 16:16:46 +0530 Subject: [PATCH 2/3] Use unsigned long long accumulators for emission/fee/burn totals. The previous implementation used int64_t values in std::tuple, which overflow once chain totals exceed the signed 64-bit range, causing negative emission values. Emission, fees, and burnt amounts are non-negative monetary quantities and should be stored as unsigned long long. --- src/cryptonote_core/cryptonote_core.cpp | 23 +++++++++++++++-------- src/cryptonote_core/cryptonote_core.h | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 353fdf71ade..d20d2b9236e 100755 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1758,9 +1758,10 @@ namespace cryptonote return m_mempool.check_for_key_images(key_im, spent); } //----------------------------------------------------------------------------------------------- - std::optional> core::get_coinbase_tx_sum(uint64_t start_offset, size_t count) + std::optional> + core::get_coinbase_tx_sum(uint64_t start_offset, size_t count) { - std::optional> result{{0, 0, 0}}; + std::optional> result{{0ULL, 0ULL, 0ULL}}; if (count == 0) return result; @@ -1787,6 +1788,8 @@ namespace cryptonote { std::shared_lock lock{m_coinbase_cache.mutex}; if (count >= m_coinbase_cache.height) { + // NOTE: if m_coinbase_cache.emissions/fees/burnt are signed types, they will be implicitly + // converted to unsigned here. If they are signed, consider static_cast. emission_amount = m_coinbase_cache.emissions; total_fee_amount = m_coinbase_cache.fees; burnt_beldex = m_coinbase_cache.burnt; @@ -1837,20 +1840,24 @@ namespace cryptonote [this, &cache_to, &result, &cache_build_started](uint64_t height, const crypto::hash& hash, const block& b){ auto& [emission_amount, total_fee_amount, burnt_beldex] = *result; std::vector txs; - auto coinbase_amount = static_cast(get_outs_money_amount(b.miner_tx)); + + // Convert amounts into unsigned accumulators + const unsigned long long coinbase_amount = static_cast(get_outs_money_amount(b.miner_tx)); get_transactions(b.tx_hashes, txs); - int64_t tx_fee_amount = 0; - for(const auto& tx: txs) + + unsigned long long tx_fee_amount = 0ULL; + for (const auto& tx: txs) { - tx_fee_amount += static_cast(get_tx_miner_fee(tx, b.major_version >= feature::FEE_BURNING)); - if(b.major_version >= feature::FEE_BURNING) + tx_fee_amount += static_cast(get_tx_miner_fee(tx, b.major_version >= feature::FEE_BURNING)); + if (b.major_version >= feature::FEE_BURNING) { - burnt_beldex += static_cast(get_burned_amount_from_tx_extra(tx.extra)); + burnt_beldex += static_cast(get_burned_amount_from_tx_extra(tx.extra)); } } emission_amount += coinbase_amount - tx_fee_amount; total_fee_amount += tx_fee_amount; + if (cache_to && cache_to == height) { std::unique_lock lock{m_coinbase_cache.mutex}; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index ccddda5d495..d3e208d3ce4 100755 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -810,7 +810,7 @@ namespace cryptonote * requested range. The optional value will be empty only if requesting the full chain *and* * another thread is already calculating it. */ - std::optional> get_coinbase_tx_sum(uint64_t start_offset, size_t count); + std::optional> get_coinbase_tx_sum(uint64_t start_offset, size_t count); /** * @brief get the network type we're on From a11763bacc6c554f957898db949b991f8476998d Mon Sep 17 00:00:00 2001 From: victor-tucci Date: Mon, 15 Dec 2025 12:25:47 +0530 Subject: [PATCH 3/3] Cleanup README: remove outdated URLs and update valid links --- README.md | 5 +---- src/cryptonote_core/master_node_list.cpp | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 1fb366e0f5a..98c8b3627f7 100755 --- a/README.md +++ b/README.md @@ -10,12 +10,9 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers. ## Development resources - Web: [beldex.io](https://beldex.io) -- Telegram: [t.me/beldexcoin](https://t.me/beldex_official) +- Telegram: [t.me/beldexcoin](https://t.me/beldexcoin) - GitHub: [https://github.com/Beldex-Coin/beldex](https://github.com/Beldex-Coin/beldex) -## Vulnerability disclosure - -- Check out our [Vulnerability Response Process](https://beldex-project.github.io/beldex-docs/Contributing/VULNERABILITY_RESPONSE_BELDEX), encourages prompt disclosure of any Vulnerabilities ## Information diff --git a/src/cryptonote_core/master_node_list.cpp b/src/cryptonote_core/master_node_list.cpp index 816f3723ae2..b6e90a1c8fd 100755 --- a/src/cryptonote_core/master_node_list.cpp +++ b/src/cryptonote_core/master_node_list.cpp @@ -1589,7 +1589,6 @@ namespace master_nodes // NOTE: No POS quorums are generated when the network has insufficient nodes to generate quorums // Or, block specifies time after all the rounds have timed out bool miner_block = !POS_hf || !POS_quorum; - // std::cout << "miner_block : " << miner_block << std::endl; result = verify_block_components(m_blockchain.nettype(), block,