From 9dbfe09710351f66a489a6a5d7ec98d6f00df392 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 9 Mar 2026 10:07:34 +0100 Subject: update --- patches/monero/0016-serialize-cache-to-JSON.patch | 463 ++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 patches/monero/0016-serialize-cache-to-JSON.patch (limited to 'patches/monero/0016-serialize-cache-to-JSON.patch') diff --git a/patches/monero/0016-serialize-cache-to-JSON.patch b/patches/monero/0016-serialize-cache-to-JSON.patch new file mode 100644 index 0000000..38aaee4 --- /dev/null +++ b/patches/monero/0016-serialize-cache-to-JSON.patch @@ -0,0 +1,463 @@ +From e6785290c24eb48d6b6aec8e1831b96f65cd3bfd Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Tue, 12 Aug 2025 07:09:14 -0400 +Subject: [PATCH 16/20] serialize cache to JSON + +--- + src/wallet/CMakeLists.txt | 1 + + src/wallet/api/wallet.cpp | 5 + + src/wallet/api/wallet.h | 2 + + src/wallet/api/wallet2_api.h | 3 + + src/wallet/wallet2.h | 6 + + src/wallet/wallet_cache_to_json.cpp | 368 ++++++++++++++++++++++++++++ + 6 files changed, 385 insertions(+) + create mode 100644 src/wallet/wallet_cache_to_json.cpp + +diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt +index b163212b7..196ad671f 100644 +--- a/src/wallet/CMakeLists.txt ++++ b/src/wallet/CMakeLists.txt +@@ -38,6 +38,7 @@ set(wallet_sources + message_store.cpp + message_transporter.cpp + wallet_rpc_payments.cpp ++ wallet_cache_to_json.cpp + ) + + monero_find_all_headers(wallet_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 107f516f3..c24b4a97d 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -3474,4 +3474,9 @@ void Wallet::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command + #endif + } + ++std::string WalletImpl::serializeCacheToJson() const ++{ ++ return std::string(m_wallet->serialize_cache_to_json()); ++} ++ + } // namespace +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index bfe81c590..98c03b9c1 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -335,6 +335,8 @@ private: + bool getWaitsForDeviceSend(); + + bool getWaitsForDeviceReceive(); ++ ++ virtual std::string serializeCacheToJson() const override; + }; + + +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index fcb8187d4..3d11929f9 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1217,6 +1217,9 @@ struct Wallet + static void setDeviceReceivedData(unsigned char* data, size_t len); + static void setDeviceSendData(unsigned char* data, size_t len); + static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)); ++ ++ //! serialize wallet cache to JSON ++ virtual std::string serializeCacheToJson() const = 0; + }; + + /** +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index d07dc7e8b..37a2447d2 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1436,6 +1436,12 @@ private: + FIELD(m_background_sync_data) + END_SERIALIZE() + ++ /*! ++ * \brief Serialize wallet cache fields to JSON ++ * \return const char* pointing to JSON string containing all cache fields ++ */ ++ const char* serialize_cache_to_json() const; ++ + /*! + * \brief Check if wallet keys and bin files exist + * \param file_path Wallet file path +diff --git a/src/wallet/wallet_cache_to_json.cpp b/src/wallet/wallet_cache_to_json.cpp +new file mode 100644 +index 000000000..4743852ca +--- /dev/null ++++ b/src/wallet/wallet_cache_to_json.cpp +@@ -0,0 +1,368 @@ ++#include "wallet2.h" ++#include "serialization/binary_archive.h" ++#include "serialization/json_archive.h" ++#include "serialization/serialization.h" ++#include ++#include ++ ++namespace tools ++{ ++ ++static void write_escaped_json_string(std::ostream& os, const std::string& str) ++{ ++ for (char c : str) { ++ switch (c) { ++ case '"': os << "\\\""; break; ++ case '\\': os << "\\\\"; break; ++ case '\n': os << "\\n"; break; ++ case '\r': os << "\\r"; break; ++ case '\t': os << "\\t"; break; ++ case '\b': os << "\\b"; break; ++ case '\f': os << "\\f"; break; ++ default: os << c; break; ++ } ++ } ++} ++ ++static void post_process_json(std::string& json) ++{ ++ // ": ," --> ": null," ++ size_t pos = 0; ++ while ((pos = json.find(": ,", pos)) != std::string::npos) { ++ json.replace(pos, 3, ": null,"); ++ pos += 7; ++ } ++ ++ // ": }" --> ": null}" ++ pos = 0; ++ while ((pos = json.find(": }", pos)) != std::string::npos) { ++ json.replace(pos, 3, ": null}"); ++ pos += 7; ++ } ++ ++ // ": ]" --> ": null]" ++ pos = 0; ++ while ((pos = json.find(": ]", pos)) != std::string::npos) { ++ json.replace(pos, 3, ": null]"); ++ pos += 7; ++ } ++ ++ // "key": number"hexstring" --> "key": "numberhexstring" ++ pos = 0; ++ while (pos < json.length()) { ++ size_t colon_pos = json.find(": ", pos); ++ if (colon_pos == std::string::npos) break; ++ ++ size_t value_start = colon_pos + 2; ++ if (value_start >= json.length()) break; ++ ++ if (std::isdigit(json[value_start])) { ++ size_t quote_pos = json.find('"', value_start); ++ if (quote_pos != std::string::npos && quote_pos < json.find_first_of(",}]", value_start)) { ++ size_t closing_quote = json.find('"', quote_pos + 1); ++ if (closing_quote != std::string::npos && closing_quote < json.find_first_of(",}]", value_start)) { ++ std::string digits; ++ size_t digit_end = value_start; ++ while (digit_end < quote_pos && std::isdigit(json[digit_end])) { ++ digits += json[digit_end]; ++ digit_end++; ++ } ++ ++ if (digit_end == quote_pos && !digits.empty()) { ++ std::string hex_part = json.substr(quote_pos + 1, closing_quote - quote_pos - 1); ++ ++ std::string replacement = "\"" + digits + hex_part + "\""; ++ json.replace(value_start, closing_quote - value_start + 1, replacement); ++ pos = value_start + replacement.length(); ++ continue; ++ } ++ } ++ } ++ } ++ ++ pos = colon_pos + 1; ++ } ++} ++ ++const char* wallet2::serialize_cache_to_json() const ++{ ++ static std::string json_result; ++ ++ try ++ { ++ std::stringstream oss; ++ json_archive ar(oss, true); // true for pretty printing ++ ++ ar.begin_object(); ++ ++ // MAGIC_FIELD("monero wallet cache") ++ std::string magic = "monero wallet cache"; ++ ar.tag("magic"); ++ ar.serialize_blob((void*)magic.data(), magic.size()); ++ if (!ar.good()) { ++ json_result = "{\"error\":\"Failed to serialize magic field\"}"; ++ return json_result.c_str(); ++ } ++ ++ // VERSION_FIELD(2) ++ uint32_t version = 2; ++ ar.tag("version"); ++ ar.serialize_varint(version); ++ if (!ar.good()) { ++ json_result = "{\"error\":\"Failed to serialize version field\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_blockchain) - hashchain type, has serialization support ++ ar.tag("m_blockchain"); ++ if (!::serialization::serialize(ar, const_cast(m_blockchain))) { ++ json_result = "{\"error\":\"Failed to serialize m_blockchain\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_transfers) - transfer_container (std::vector) ++ ar.tag("m_transfers"); ++ if (!::serialization::serialize(ar, const_cast(m_transfers))) { ++ json_result = "{\"error\":\"Failed to serialize m_transfers\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_account_public_address) - cryptonote::account_public_address ++ ar.tag("m_account_public_address"); ++ if (!::serialization::serialize(ar, const_cast(m_account_public_address))) { ++ json_result = "{\"error\":\"Failed to serialize m_account_public_address\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_key_images) - serializable_unordered_map ++ ar.tag("m_key_images"); ++ if (!::serialization::serialize(ar, const_cast&>(m_key_images))) { ++ json_result = "{\"error\":\"Failed to serialize m_key_images\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_unconfirmed_txs) - serializable_unordered_map ++ ar.tag("m_unconfirmed_txs"); ++ if (!::serialization::serialize(ar, const_cast&>(m_unconfirmed_txs))) { ++ json_result = "{\"error\":\"Failed to serialize m_unconfirmed_txs\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_payments) - payment_container (serializable_unordered_multimap) ++ ar.tag("m_payments"); ++ if (!::serialization::serialize(ar, const_cast(m_payments))) { ++ json_result = "{\"error\":\"Failed to serialize m_payments\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_tx_keys) - serializable_unordered_map ++ ar.tag("m_tx_keys"); ++ if (!::serialization::serialize(ar, const_cast&>(m_tx_keys))) { ++ json_result = "{\"error\":\"Failed to serialize m_tx_keys\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_confirmed_txs) - serializable_unordered_map ++ ar.tag("m_confirmed_txs"); ++ if (!::serialization::serialize(ar, const_cast&>(m_confirmed_txs))) { ++ json_result = "{\"error\":\"Failed to serialize m_confirmed_txs\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_tx_notes) - serializable_unordered_map ++ ar.tag("m_tx_notes"); ++ if (!::serialization::serialize(ar, const_cast&>(m_tx_notes))) { ++ json_result = "{\"error\":\"Failed to serialize m_tx_notes\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_unconfirmed_payments) - serializable_unordered_multimap ++ ar.tag("m_unconfirmed_payments"); ++ if (!::serialization::serialize(ar, const_cast&>(m_unconfirmed_payments))) { ++ json_result = "{\"error\":\"Failed to serialize m_unconfirmed_payments\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_pub_keys) - serializable_unordered_map ++ ar.tag("m_pub_keys"); ++ if (!::serialization::serialize(ar, const_cast&>(m_pub_keys))) { ++ json_result = "{\"error\":\"Failed to serialize m_pub_keys\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_address_book) - std::vector ++ ar.tag("m_address_book"); ++ if (!::serialization::serialize(ar, const_cast&>(m_address_book))) { ++ json_result = "{\"error\":\"Failed to serialize m_address_book\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_scanned_pool_txs[0]) - std::unordered_set ++ ar.tag("m_scanned_pool_txs_0"); ++ if (!::serialization::serialize(ar, const_cast&>(m_scanned_pool_txs[0]))) { ++ json_result = "{\"error\":\"Failed to serialize m_scanned_pool_txs[0]\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_scanned_pool_txs[1]) - std::unordered_set ++ ar.tag("m_scanned_pool_txs_1"); ++ if (!::serialization::serialize(ar, const_cast&>(m_scanned_pool_txs[1]))) { ++ json_result = "{\"error\":\"Failed to serialize m_scanned_pool_txs[1]\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_subaddresses) - serializable_unordered_map ++ ar.tag("m_subaddresses"); ++ if (!::serialization::serialize(ar, const_cast&>(m_subaddresses))) { ++ json_result = "{\"error\":\"Failed to serialize m_subaddresses\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_subaddress_labels) - std::vector> - manual JSON serialization ++ oss << ", \n \"m_subaddress_labels\": ["; ++ for (size_t i = 0; i < m_subaddress_labels.size(); ++i) { ++ if (i > 0) oss << ", "; ++ oss << "\n ["; ++ for (size_t j = 0; j < m_subaddress_labels[i].size(); ++j) { ++ if (j > 0) oss << ", "; ++ oss << "\""; ++ write_escaped_json_string(oss, m_subaddress_labels[i][j]); ++ oss << "\""; ++ } ++ oss << "]"; ++ } ++ oss << "\n ]"; ++ ++ // FIELD(m_additional_tx_keys) - serializable_unordered_map> ++ ar.tag("m_additional_tx_keys"); ++ if (!::serialization::serialize(ar, const_cast>&>(m_additional_tx_keys))) { ++ json_result = "{\"error\":\"Failed to serialize m_additional_tx_keys\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_attributes) - serializable_unordered_map - manual JSON serialization ++ oss << ", \n \"m_attributes\": {"; ++ bool first_attr = true; ++ for (const auto& attr : m_attributes) { ++ if (!first_attr) oss << ", "; ++ first_attr = false; ++ oss << "\n \""; ++ write_escaped_json_string(oss, attr.first); ++ oss << "\": \""; ++ write_escaped_json_string(oss, attr.second); ++ oss << "\""; ++ } ++ oss << "\n }"; ++ ++ // FIELD(m_account_tags) - std::pair, std::vector> - manual JSON serialization ++ oss << ", \n \"m_account_tags\": {"; ++ oss << "\n \"tags_map\": {"; ++ bool first_tag = true; ++ for (const auto& tag : m_account_tags.first) { ++ if (!first_tag) oss << ", "; ++ first_tag = false; ++ oss << "\n \""; ++ write_escaped_json_string(oss, tag.first); ++ oss << "\": \""; ++ write_escaped_json_string(oss, tag.second); ++ oss << "\""; ++ } ++ oss << "\n },"; ++ oss << "\n \"account_list\": ["; ++ for (size_t i = 0; i < m_account_tags.second.size(); ++i) { ++ if (i > 0) oss << ", "; ++ oss << "\n \""; ++ write_escaped_json_string(oss, m_account_tags.second[i]); ++ oss << "\""; ++ } ++ oss << "\n ]"; ++ oss << "\n }"; ++ ++ // FIELD(m_ring_history_saved) - bool ++ // ar.tag("m_ring_history_saved"); ++ // ar.serialize_blob(&m_ring_history_saved, sizeof(m_ring_history_saved)); ++ // if (!ar.good()) { ++ // json_result = "{\"error\":\"Failed to serialize m_ring_history_saved\"}"; ++ // return json_result.c_str(); ++ // } ++ ++ // FIELD(m_last_block_reward) - uint64_t ++ ar.tag("m_last_block_reward"); ++ ar.serialize_int(m_last_block_reward); ++ if (!ar.good()) { ++ json_result = "{\"error\":\"Failed to serialize m_last_block_reward\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_tx_device) - serializable_unordered_map ++ ar.tag("m_tx_device"); ++ if (!::serialization::serialize(ar, const_cast&>(m_tx_device))) { ++ json_result = "{\"error\":\"Failed to serialize m_tx_device\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_device_last_key_image_sync) - uint64_t ++ ar.tag("m_device_last_key_image_sync"); ++ ar.serialize_int(m_device_last_key_image_sync); ++ if (!ar.good()) { ++ json_result = "{\"error\":\"Failed to serialize m_device_last_key_image_sync\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_cold_key_images) - serializable_unordered_map ++ ar.tag("m_cold_key_images"); ++ if (!::serialization::serialize(ar, const_cast&>(m_cold_key_images))) { ++ json_result = "{\"error\":\"Failed to serialize m_cold_key_images\"}"; ++ return json_result.c_str(); ++ } ++ ++ // FIELD(m_rpc_client_secret_key) - crypto::secret_key ++ // ar.tag("m_rpc_client_secret_key"); ++ // ar.serialize_blob(&m_rpc_client_secret_key, sizeof(m_rpc_client_secret_key)); ++ // if (!ar.good()) { ++ // json_result = "{\"error\":\"Failed to serialize m_rpc_client_secret_key\"}"; ++ // return json_result.c_str(); ++ // } ++ ++ // Version-dependent fields ++ if (version >= 1) { ++ // FIELD(m_has_ever_refreshed_from_node) - bool ++ // ar.tag("m_has_ever_refreshed_from_node"); ++ // ar.serialize_blob(&m_has_ever_refreshed_from_node, sizeof(m_has_ever_refreshed_from_node)); ++ // if (!ar.good()) { ++ // json_result = "{\"error\":\"Failed to serialize m_has_ever_refreshed_from_node\"}"; ++ // return json_result.c_str(); ++ // } ++ } ++ ++ if (version >= 2) { ++ // FIELD(m_background_sync_data) - background_sync_data_t ++ ar.tag("m_background_sync_data"); ++ if (!::serialization::serialize(ar, const_cast(m_background_sync_data))) { ++ json_result = "{\"error\":\"Failed to serialize m_background_sync_data\"}"; ++ return json_result.c_str(); ++ } ++ } ++ ++ ar.end_object(); ++ ++ if (!ar.good()) { ++ json_result = "{\"error\":\"Failed to finalize JSON serialization\"}"; ++ return json_result.c_str(); ++ } ++ ++ json_result = oss.str(); ++ ++ // Post-process to fix malformed JSON ++ post_process_json(json_result); ++ ++ return json_result.c_str(); ++ } ++ catch (const std::exception& e) ++ { ++ json_result = "{\"error\":\"Failed to serialize wallet cache: " + std::string(e.what()) + "\"}"; ++ return json_result.c_str(); ++ } ++} ++ ++} // namespace tools +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + -- cgit v1.2.3