serialize cache to json

This commit is contained in:
Czarek Nakamoto
2025-08-05 14:55:35 -04:00
committed by Some Random Crypto Guy
parent 8f10e7748f
commit 2968e45b4c
9 changed files with 509 additions and 9 deletions

View File

@@ -3604,6 +3604,17 @@ void Wallet_setLedgerCallback(Pointer<NativeFunction<Void Function(Pointer<Unsig
return ret;
}
@Deprecated("TODO")
String MONERO_Wallet_serializeCacheToJson(wallet ptr) {
debugStart?.call('MONERO_Wallet_serializeCacheToJson');
lib ??= MoneroC(DynamicLibrary.open(libPath));
final ret = lib!.MONERO_Wallet_serializeCacheToJson(ptr);
final str = ret.cast<Utf8>().toDartString();
MONERO_free(ret.cast());
debugEnd?.call('MONERO_Wallet_serializeCacheToJson');
return str;
}
// WalletManager
@Deprecated("TODO")
typedef WalletManager = Pointer<Void>;

View File

@@ -1,4 +1,4 @@
// ignore_for_file: constant_identifier_names
const String wallet2_api_c_h_sha256 = "91218f60e937753a606b1db899ee2ed66a1b02144eb2d79b950ce59804397f84";
const String wallet2_api_c_cpp_sha256 = "282128494f3419c128dff7ed67daadd9c2ad0600d10557e829df03d9bc1ad891-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe";
const String wallet2_api_c_exp_sha256 = "b2f2355242f9ad852ccf860a60a5c7a74054f9450ba1fbf9c0f42c58dbfdee9d";
const String wallet2_api_c_h_sha256 = "3515e4c9e537ca3efa664f0b364a3aeb106ff64337f542a262ce104c00b31235";
const String wallet2_api_c_cpp_sha256 = "ca44a8d4a201ba6dc0a25c857e95d07bc8d514039b38cd5b3826100e8943cbcc-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe";
const String wallet2_api_c_exp_sha256 = "0561e14606106e6b0ec49fb2aefe743ff500f7c3de07557f7041e06aef9509ce";

View File

@@ -4488,6 +4488,22 @@ class MoneroC {
ffi.Void Function(ffi.Pointer<ffi.UnsignedChar> command,
ffi.UnsignedInt cmd_len)>>)>();
ffi.Pointer<ffi.Char> MONERO_Wallet_serializeCacheToJson(
ffi.Pointer<ffi.Void> wallet_ptr,
) {
return _MONERO_Wallet_serializeCacheToJson(
wallet_ptr,
);
}
late final _MONERO_Wallet_serializeCacheToJsonPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Char> Function(
ffi.Pointer<ffi.Void>)>>('MONERO_Wallet_serializeCacheToJson');
late final _MONERO_Wallet_serializeCacheToJson =
_MONERO_Wallet_serializeCacheToJsonPtr.asFunction<
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Void>)>();
ffi.Pointer<ffi.Void> MONERO_WalletManager_createWallet(
ffi.Pointer<ffi.Void> wm_ptr,
ffi.Pointer<ffi.Char> path,

View File

@@ -1,5 +1,5 @@
export const moneroChecksum = {
wallet2_api_c_h_sha256: "91218f60e937753a606b1db899ee2ed66a1b02144eb2d79b950ce59804397f84",
wallet2_api_c_cpp_sha256: "282128494f3419c128dff7ed67daadd9c2ad0600d10557e829df03d9bc1ad891-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe",
wallet2_api_c_exp_sha256: "b2f2355242f9ad852ccf860a60a5c7a74054f9450ba1fbf9c0f42c58dbfdee9d",
wallet2_api_c_h_sha256: "3515e4c9e537ca3efa664f0b364a3aeb106ff64337f542a262ce104c00b31235",
wallet2_api_c_cpp_sha256: "ca44a8d4a201ba6dc0a25c857e95d07bc8d514039b38cd5b3826100e8943cbcc-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe",
wallet2_api_c_exp_sha256: "0561e14606106e6b0ec49fb2aefe743ff500f7c3de07557f7041e06aef9509ce",
}

View File

@@ -123,6 +123,7 @@ _MONERO_DeviceProgress_progress
_MONERO_DeviceProgress_indeterminate
_MONERO_Wallet_seed
_MONERO_Wallet_setLedgerCallback
_MONERO_Wallet_serializeCacheToJson
_MONERO_Wallet_getSeedLanguage
_MONERO_Wallet_setSeedLanguage
_MONERO_Wallet_status

View File

@@ -1,6 +1,6 @@
#ifndef MONEROC_CHECKSUMS
#define MONEROC_CHECKSUMS
const char * MONERO_wallet2_api_c_h_sha256 = "91218f60e937753a606b1db899ee2ed66a1b02144eb2d79b950ce59804397f84";
const char * MONERO_wallet2_api_c_cpp_sha256 = "282128494f3419c128dff7ed67daadd9c2ad0600d10557e829df03d9bc1ad891-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe";
const char * MONERO_wallet2_api_c_exp_sha256 = "b2f2355242f9ad852ccf860a60a5c7a74054f9450ba1fbf9c0f42c58dbfdee9d";
const char * MONERO_wallet2_api_c_h_sha256 = "3515e4c9e537ca3efa664f0b364a3aeb106ff64337f542a262ce104c00b31235";
const char * MONERO_wallet2_api_c_cpp_sha256 = "ca44a8d4a201ba6dc0a25c857e95d07bc8d514039b38cd5b3826100e8943cbcc-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe";
const char * MONERO_wallet2_api_c_exp_sha256 = "0561e14606106e6b0ec49fb2aefe743ff500f7c3de07557f7041e06aef9509ce";
#endif

View File

@@ -2162,6 +2162,14 @@ void MONERO_Wallet_setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *c
DEBUG_END()
}
const char* MONERO_Wallet_serializeCacheToJson(void* wallet_ptr) {
DEBUG_START()
Monero::Wallet *wallet = reinterpret_cast<Monero::Wallet*>(wallet_ptr);
std::string result = wallet->serializeCacheToJson();
return strdup(result.c_str());
DEBUG_END()
}
void* MONERO_WalletManager_createWallet(void* wm_ptr, const char* path, const char* password, const char* language, int networkType) {
DEBUG_START()
Monero::WalletManager *wm = reinterpret_cast<Monero::WalletManager*>(wm_ptr);

View File

@@ -842,6 +842,7 @@ extern ADDAPI bool MONERO_Wallet_getWaitsForDeviceReceive();
extern ADDAPI void MONERO_Wallet_setDeviceReceivedData(unsigned char* data, size_t len);
extern ADDAPI void MONERO_Wallet_setDeviceSendData(unsigned char* data, size_t len);
extern ADDAPI void MONERO_Wallet_setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len));
extern ADDAPI const char* MONERO_Wallet_serializeCacheToJson(void* wallet_ptr);
// };
// struct WalletManager

View File

@@ -0,0 +1,463 @@
From 7c1d576901a56b7c315b2c54362f7985ff8df753 Mon Sep 17 00:00:00 2001
From: Czarek Nakamoto <cyjan@mrcyjanek.net>
Date: Tue, 12 Aug 2025 07:09:14 -0400
Subject: [PATCH] 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 7d7d0f922..effb6e719 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -3475,4 +3475,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 4f324c238..bc4abc672 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1429,6 +1429,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..64687a7a6
--- /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 <sstream>
+#include <iomanip>
+
+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<true> 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<hashchain&>(m_blockchain))) {
+ json_result = "{\"error\":\"Failed to serialize m_blockchain\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_transfers) - transfer_container (std::vector<transfer_details>)
+ ar.tag("m_transfers");
+ if (!::serialization::serialize(ar, const_cast<transfer_container&>(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<cryptonote::account_public_address&>(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<crypto::key_image, size_t>
+ ar.tag("m_key_images");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::key_image, size_t>&>(m_key_images))) {
+ json_result = "{\"error\":\"Failed to serialize m_key_images\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_unconfirmed_txs) - serializable_unordered_map<crypto::hash, unconfirmed_transfer_details>
+ ar.tag("m_unconfirmed_txs");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, unconfirmed_transfer_details>&>(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<crypto::hash, payment_details>)
+ ar.tag("m_payments");
+ if (!::serialization::serialize(ar, const_cast<payment_container&>(m_payments))) {
+ json_result = "{\"error\":\"Failed to serialize m_payments\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_tx_keys) - serializable_unordered_map<crypto::hash, crypto::secret_key>
+ ar.tag("m_tx_keys");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, crypto::secret_key>&>(m_tx_keys))) {
+ json_result = "{\"error\":\"Failed to serialize m_tx_keys\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_confirmed_txs) - serializable_unordered_map<crypto::hash, confirmed_transfer_details>
+ ar.tag("m_confirmed_txs");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, confirmed_transfer_details>&>(m_confirmed_txs))) {
+ json_result = "{\"error\":\"Failed to serialize m_confirmed_txs\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_tx_notes) - serializable_unordered_map<crypto::hash, std::string>
+ ar.tag("m_tx_notes");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, std::string>&>(m_tx_notes))) {
+ json_result = "{\"error\":\"Failed to serialize m_tx_notes\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_unconfirmed_payments) - serializable_unordered_multimap<crypto::hash, pool_payment_details>
+ ar.tag("m_unconfirmed_payments");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_multimap<crypto::hash, pool_payment_details>&>(m_unconfirmed_payments))) {
+ json_result = "{\"error\":\"Failed to serialize m_unconfirmed_payments\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_pub_keys) - serializable_unordered_map<crypto::public_key, size_t>
+ ar.tag("m_pub_keys");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::public_key, size_t>&>(m_pub_keys))) {
+ json_result = "{\"error\":\"Failed to serialize m_pub_keys\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_address_book) - std::vector<tools::wallet2::address_book_row>
+ ar.tag("m_address_book");
+ if (!::serialization::serialize(ar, const_cast<std::vector<tools::wallet2::address_book_row>&>(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<crypto::hash>
+ ar.tag("m_scanned_pool_txs_0");
+ if (!::serialization::serialize(ar, const_cast<std::unordered_set<crypto::hash>&>(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<crypto::hash>
+ ar.tag("m_scanned_pool_txs_1");
+ if (!::serialization::serialize(ar, const_cast<std::unordered_set<crypto::hash>&>(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<crypto::public_key, cryptonote::subaddress_index>
+ ar.tag("m_subaddresses");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::public_key, cryptonote::subaddress_index>&>(m_subaddresses))) {
+ json_result = "{\"error\":\"Failed to serialize m_subaddresses\"}";
+ return json_result.c_str();
+ }
+
+ // FIELD(m_subaddress_labels) - std::vector<std::vector<std::string>> - 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<crypto::hash, std::vector<crypto::secret_key>>
+ ar.tag("m_additional_tx_keys");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, std::vector<crypto::secret_key>>&>(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<std::string, std::string> - 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<serializable_map<std::string, std::string>, std::vector<std::string>> - 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<crypto::hash, std::string>
+ ar.tag("m_tx_device");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, std::string>&>(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<crypto::public_key, crypto::key_image>
+ ar.tag("m_cold_key_images");
+ if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::public_key, crypto::key_image>&>(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<background_sync_data_t&>(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