From db9b129d71d9a072bf3e949d475a346f535f77cb Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 6 Jul 2019 19:38:17 -0400 Subject: [PATCH] move deserialization to common MoneroUtils --- src/utils/MoneroUtils.cpp | 408 ++++++++++++++++++++++++++++++++++---- src/utils/MoneroUtils.h | 75 ++----- 2 files changed, 389 insertions(+), 94 deletions(-) diff --git a/src/utils/MoneroUtils.cpp b/src/utils/MoneroUtils.cpp index e36b1a1..2316e20 100644 --- a/src/utils/MoneroUtils.cpp +++ b/src/utils/MoneroUtils.cpp @@ -59,6 +59,231 @@ using namespace std; using namespace cryptonote; using namespace MoneroUtils; +// ------------------------------- INNER HELPERS ------------------------------ + +// TODO: no common utility? make common utility +bool stringToBool(string str) { + transform(str.begin(), str.end(), str.begin(), ::tolower); + if (string("true") == str) return true; + if (string("false") == str) return false; + return boost::lexical_cast(str); +} + +void nodeToTransfer(const boost::property_tree::ptree& node, shared_ptr transfer) { + + // initialize transfer from node + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Transfer node key: " << key << endl; + if (key == string("accountIndex")) transfer->accountIndex = it->second.get_value(); + } +} + +shared_ptr nodeToTransferRequest(const boost::property_tree::ptree& node) { + shared_ptr transferRequest = shared_ptr(new MoneroTransferRequest()); + nodeToTransfer(node, transferRequest); + + // initialize request from node + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Transfer request node key: " << key << endl; + if (key == string("isIncoming")) transferRequest->isIncoming = stringToBool(it->second.data()); + else if (key == string("address")) transferRequest->address = it->second.data(); + else if (key == string("addresses")) throw runtime_error("addresses not implemented"); + else if (key == string("subaddressIndex")) transferRequest->subaddressIndex = it->second.get_value(); + else if (key == string("subaddressIndices")) { + vector subaddressIndices; + for (const auto& child : it->second) subaddressIndices.push_back(child.second.get_value()); + transferRequest->subaddressIndices = subaddressIndices; + } + else if (key == string("destinations")) throw runtime_error("destinations not implemented"); + else if (key == string("hasDestinations")) transferRequest->hasDestinations = stringToBool(it->second.data()); + else if (key == string("txRequest")) throw runtime_error("txRequest not implemented"); + } + + return transferRequest; +} + +shared_ptr nodeToKeyImage(const boost::property_tree::ptree& node) { + + // initialize key image from node + shared_ptr keyImage = shared_ptr(new MoneroKeyImage()); + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Key image node key: " << key << endl; + if (key == string("hex")) keyImage->hex = it->second.data(); + else if (key == string("signature")) keyImage->signature = it->second.data(); + } + + return keyImage; +} + +void nodeToOutput(const boost::property_tree::ptree& node, shared_ptr output) { + cout << "nodeToOutput()" << endl; + +// // print for debug +// std::stringstream ss; +// boost::property_tree::write_json(ss, node, false); +// string receivedNode = ss.str(); +// cout << "Received node: " << receivedNode << endl; + + // initialize output from node + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Output node key: " << key << endl; + if (key == string("keyImage")) output->keyImage = nodeToKeyImage(it->second); + else if (key == string("amount")) output->amount = it->second.get_value(); + else if (key == string("index")) output->index = it->second.get_value(); + else if (key == string("ringOutputIndices")) throw runtime_error("nodeToTx() deserialize ringOutputIndices not implemented"); + else if (key == string("stealthPublicKey")) throw runtime_error("nodeToTx() deserialize stealthPublicKey not implemented"); + } +} + +void nodeToOutputWallet(const boost::property_tree::ptree& node, shared_ptr outputWallet) { + nodeToOutput(node, outputWallet); + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Output wallet node key: " << key << endl; + if (key == string("accountIndex")) outputWallet->accountIndex = it->second.get_value(); + else if (key == string("subaddressIndex")) outputWallet->subaddressIndex = it->second.get_value(); + else if (key == string("isSpent")) outputWallet->isSpent = stringToBool(it->second.data()); + else if (key == string("isUnlocked")) outputWallet->isUnlocked = stringToBool(it->second.data()); + else if (key == string("isFrozen")) outputWallet->isFrozen = stringToBool(it->second.data()); + } +} + +shared_ptr nodeToOutputRequest(const boost::property_tree::ptree& node) { + shared_ptr outputRequest = shared_ptr(new MoneroOutputRequest()); + nodeToOutputWallet(node, outputRequest); + + // initialize request from node + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Output request node key: " << key << endl; + if (key == string("subaddressIndices")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) outputRequest->subaddressIndices.push_back(it2->second.get_value()); + else if (key == string("txRequest")) {} // ignored + } + + return outputRequest; +} + +void nodeToTx(const boost::property_tree::ptree& node, shared_ptr tx) { + cout << "nodeToTx()" << endl; + +// // print for debug +// std::stringstream ss; +// boost::property_tree::write_json(ss, node, false); +// string receivedNode = ss.str(); +// cout << "Received node: " << receivedNode << endl; + + // initialize tx from node + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Tx node key: " << key << endl; + if (key == string("id")) tx->id = it->second.data(); + else if (key == string("version")) throw runtime_error("version deserializationn not implemented"); + else if (key == string("isCoinbase")) tx->isCoinbase = stringToBool(it->second.data()); + else if (key == string("paymentId")) throw runtime_error("paymentId deserializationn not implemented"); + else if (key == string("fee")) throw runtime_error("fee deserialization not implemented"); + else if (key == string("mixin")) throw runtime_error("mixin deserialization not implemented"); + else if (key == string("doNotRelay")) tx->doNotRelay = stringToBool(it->second.data()); + else if (key == string("isRelayed")) tx->isRelayed = stringToBool(it->second.data()); + else if (key == string("isConfirmed")) tx->isConfirmed = stringToBool(it->second.data()); + else if (key == string("inTxPool")) tx->inTxPool = stringToBool(it->second.data()); + else if (key == string("numConfirmations")) throw runtime_error("numConfirmations deserialization not implemented"); + else if (key == string("unlockTime")) throw runtime_error("unlockTime deserialization not implemented"); + else if (key == string("lastRelayedTimestamp")) throw runtime_error("lastRelayedTimestamp deserialization not implemented"); + else if (key == string("receivedTimestamp")) throw runtime_error("receivedTimestamp deserializationn not implemented"); + else if (key == string("isDoubleSpend")) tx->isDoubleSpend = stringToBool(it->second.data()); + else if (key == string("key")) tx->key = it->second.data(); + else if (key == string("fullHex")) tx->fullHex = it->second.data(); + else if (key == string("prunedHex")) tx->prunedHex = it->second.data(); + else if (key == string("prunableHex")) tx->prunableHex = it->second.data(); + else if (key == string("prunableHash")) tx->prunableHash = it->second.data(); + else if (key == string("size")) throw runtime_error("size deserialization not implemented"); + else if (key == string("weight")) throw runtime_error("weight deserialization not implemented"); + else if (key == string("vins")) throw runtime_error("vins deserializationn not implemented"); + else if (key == string("vouts")) throw runtime_error("vouts deserializationn not implemented"); + else if (key == string("outputIndices")) throw runtime_error("outputIndices deserialization not implemented"); + else if (key == string("metadata")) throw runtime_error("metadata deserialization not implemented"); + else if (key == string("commonTxSets")) throw runtime_error("commonTxSets deserialization not implemented"); + else if (key == string("extra")) throw runtime_error("extra deserialization not implemented"); + else if (key == string("rctSignatures")) throw runtime_error("rctSignatures deserialization not implemented"); + else if (key == string("rctSigPrunable")) throw runtime_error("rctSigPrunable deserialization not implemented"); + else if (key == string("isKeptByBlock")) tx->isKeptByBlock = stringToBool(it->second.data()); + else if (key == string("isFailed")) tx->isFailed = stringToBool(it->second.data()); + else if (key == string("lastFailedHeight")) throw runtime_error("lastFailedHeight deserialization not implemented"); + else if (key == string("lastFailedId")) tx->lastFailedId = it->second.data(); + else if (key == string("maxUsedBlockHeight")) throw runtime_error("maxUsedBlockHeight deserialization not implemented"); + else if (key == string("maxUsedBlockId")) tx->maxUsedBlockId = it->second.data(); + else if (key == string("signatures")) throw runtime_error("signatures deserialization not implemented"); + } +} + +void nodeToTxWallet(const boost::property_tree::ptree& node, shared_ptr txWallet) { + nodeToTx(node, txWallet); + + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Tx wallet node key: " << key << endl; + //if (key == string("id")) tx->id = it->second.data(); + } +} + +shared_ptr nodeToTxRequest(const boost::property_tree::ptree& node) { + shared_ptr txRequest = shared_ptr(new MoneroTxRequest()); + nodeToTxWallet(node, txRequest); + + // initialize request from node + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Tx request node key: " << key << endl; + if (key == string("isOutgoing")) txRequest->isOutgoing = stringToBool(it->second.data()); + else if (key == string("isIncoming")) txRequest->isIncoming = stringToBool(it->second.data()); + else if (key == string("txIds")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) txRequest->txIds.push_back(it2->second.data()); + else if (key == string("hasPaymentId")) txRequest->hasPaymentId = stringToBool(it->second.data()); + else if (key == string("paymentIds")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) txRequest->paymentIds.push_back(it2->second.data()); + else if (key == string("minHeight")) txRequest->minHeight = it->second.get_value(); + else if (key == string("maxHeight")) txRequest->maxHeight = it->second.get_value(); + else if (key == string("includeOutputs")) txRequest->includeOutputs = stringToBool(it->second.data()); + else if (key == string("transferRequest")) txRequest->transferRequest = nodeToTransferRequest(it->second); + else if (key == string("outputRequest")) txRequest->outputRequest = nodeToOutputRequest(it->second); + } + + return txRequest; +} + +shared_ptr nodeToBlockRequest(const boost::property_tree::ptree& node) { + cout << "nodeToBlockRequest()" << endl; + shared_ptr block = shared_ptr(new MoneroBlock()); + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Block node key: " << key << endl; + if (key == string("height")) block->height = (uint64_t) 7; // TODO + else if (key == string("txs")) { + boost::property_tree::ptree txsNode = it->second; + for (boost::property_tree::ptree::const_iterator it2 = txsNode.begin(); it2 != txsNode.end(); ++it2) { + block->txs.push_back(nodeToTxRequest(it2->second)); + } + } + } + return block; +} + +shared_ptr nodeToDestination(const boost::property_tree::ptree& node) { + cout << "nodeToDestination()" << endl; + shared_ptr destination = shared_ptr(new MoneroDestination()); + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Destination node key: " << key << endl; + if (key == string("address")) destination->address = it->second.data(); + else if (key == string("amount")) destination->amount = it->second.get_value(); + } + return destination; +} + +// ------------------------- PUBLIC STATIC UTILITIES -------------------------- + void MoneroUtils::jsonToBinary(const std::string &json, std::string &bin) { epee::serialization::portable_storage ps; ps.load_from_json(json); @@ -123,47 +348,148 @@ void MoneroUtils::binaryBlocksToJson(const std::string &bin, std::string &json) json = ss.str(); } -//string MoneroUtils::serialize(const MoneroAccount& account) { -// cout << "serialize(account)" << endl; -// -// // build property tree from account -// boost::property_tree::ptree accountNode; -// //addNode(accountNode, string("index"), account.index); -// -// // convert root to string -// std::stringstream ss; -// boost::property_tree::write_json(ss, accountNode, false); -// return ss.str(); -//} -// -//string MoneroUtils::serialize(const MoneroSubaddress& subaddress) { -// cout << "serialize(subaddress)" << endl; -// throw runtime_error("serialize(subaddress) not implemented"); -//} -// -//string MoneroUtils::serialize(const MoneroBlock& block) { -// throw runtime_error("serialize(block) not implemented"); -//} -// -//void MoneroUtils::deserializeTx(const string& txStr, MoneroTx& tx) { -// throw runtime_error("deserializeTx(txStr) not implemented"); -//} -// -//void MoneroUtils::deserializeTxWallet(const string& txStr, MoneroTxWallet& tx) { -// throw runtime_error("deserializeTxWallet(txStr) not implemented"); -//} -// -//void MoneroUtils::deserializeTxRequest(const string& txRequestStr, MoneroTxRequest& request) { -// throw runtime_error("deserializeTxRequest(txRequestStr) not implemented"); -//} -// -//void MoneroUtils::deserializeOutput(const string& outputStr, MoneroOutput& output) { -// throw runtime_error("deserializeOutput(outputStr) not implemented"); -//} -// -//void MoneroUtils::deserializeOutputWallet(const string& outputStr, MoneroOutputWallet& output) { -// throw runtime_error("deserializeOutputWallet(outputStr) not implemented"); -//} +shared_ptr MoneroUtils::deserializeTxRequest(const string& txRequestStr) { + cout << "deserializeTxRequest(): " << txRequestStr << endl; + + // deserialize tx request string to property rooted at block + std::istringstream iss = txRequestStr.empty() ? std::istringstream() : std::istringstream(txRequestStr); + boost::property_tree::ptree blockNode; + boost::property_tree::read_json(iss, blockNode); + + // convert request property tree to block + shared_ptr block = nodeToBlockRequest(blockNode); + + // get tx request + shared_ptr txRequest = static_pointer_cast(block->txs[0]); + + cout << "Returning deserialized tx request" << endl; + + // return deserialized request + return txRequest; +} + +shared_ptr MoneroUtils::deserializeTransferRequest(const string& transferRequestStr) { + cout << "deserializeTransferRequest(): " << transferRequestStr << endl; + + // deserialize transfer request string to property rooted at block + std::istringstream iss = transferRequestStr.empty() ? std::istringstream() : std::istringstream(transferRequestStr); + boost::property_tree::ptree blockNode; + boost::property_tree::read_json(iss, blockNode); + + // convert request property tree to block + shared_ptr block = nodeToBlockRequest(blockNode); + + // return mpty request if no txs + if (block->txs.empty()) return shared_ptr(new MoneroTransferRequest()); + + // get tx request + shared_ptr txRequest = static_pointer_cast(block->txs[0]); + + // get / create transfer request + shared_ptr transferRequest = txRequest->transferRequest == boost::none ? shared_ptr(new MoneroTransferRequest()) : *txRequest->transferRequest; + + // transfer request references tx request but not the other way around to avoid circular loop // TODO: could add check within meetsCriterias() + transferRequest->txRequest = txRequest; + txRequest->transferRequest = boost::none; + + cout << "Returning deserialized transfer request" << endl; + + // return deserialized request + return transferRequest; +} + +shared_ptr MoneroUtils::deserializeOutputRequest(const string& outputRequestStr) { + cout << "deserializeOutputRequest(): " << outputRequestStr << endl; + + // deserialize output request string to property rooted at block + std::istringstream iss = outputRequestStr.empty() ? std::istringstream() : std::istringstream(outputRequestStr); + boost::property_tree::ptree blockNode; + boost::property_tree::read_json(iss, blockNode); + + // convert request property tree to block + shared_ptr block = nodeToBlockRequest(blockNode); + + // empty request if no txs + if (block->txs.empty()) return shared_ptr(new MoneroOutputRequest()); + + // get tx request + shared_ptr txRequest = static_pointer_cast(block->txs[0]); + + // get / create output request + shared_ptr outputRequest = txRequest->outputRequest == boost::none ? shared_ptr(new MoneroOutputRequest()) : *txRequest->outputRequest; + + // output request references tx request but not the other way around to avoid circular loop // TODO: could add check within meetsCriterias() + outputRequest->txRequest = txRequest; + txRequest->outputRequest = boost::none; + + //cout << block->serialize() << endl; + cout << "Returning deserialized output request" << endl; + + // return deserialized request + return outputRequest; +} + +shared_ptr MoneroUtils::deserializeSendRequest(const string& sendRequestStr) { + cout << "deserializeSendRequest(): " << sendRequestStr << endl; + + // deserialize send request json to property node + std::istringstream iss = sendRequestStr.empty() ? std::istringstream() : std::istringstream(sendRequestStr); + boost::property_tree::ptree node; + boost::property_tree::read_json(iss, node); + + // convert request property tree to MoneroSendRequest + shared_ptr sendRequest = shared_ptr(new MoneroSendRequest()); + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + cout << "Send request node key: " << key << endl; + if (key == string("destinations")) { + boost::property_tree::ptree destinationsNode = it->second; + for (boost::property_tree::ptree::const_iterator it2 = destinationsNode.begin(); it2 != destinationsNode.end(); ++it2) { + sendRequest->destinations.push_back(nodeToDestination(it2->second)); + } + } + else if (key == string("paymentId")) sendRequest->paymentId = it->second.data(); + else if (key == string("priority")) throw runtime_error("deserializeSendRequest() paymentId not implemented"); + else if (key == string("mixin")) sendRequest->mixin = it->second.get_value(); + else if (key == string("ringSize")) sendRequest->ringSize = it->second.get_value(); + else if (key == string("fee")) sendRequest->fee = it->second.get_value(); + else if (key == string("accountIndex")) sendRequest->accountIndex = it->second.get_value(); + else if (key == string("subaddressIndices")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) sendRequest->subaddressIndices.push_back(it2->second.get_value()); + else if (key == string("unlockTime")) sendRequest->unlockTime = it->second.get_value(); + else if (key == string("canSplit")) sendRequest->canSplit = it->second.get_value(); + else if (key == string("doNotRelay")) sendRequest->doNotRelay = it->second.get_value(); + else if (key == string("note")) sendRequest->note = it->second.data(); + else if (key == string("recipientName")) sendRequest->recipientName = it->second.data(); + else if (key == string("belowAmount")) sendRequest->belowAmount = it->second.get_value(); + else if (key == string("sweepEachSubaddress")) sendRequest->sweepEachSubaddress = it->second.get_value(); + else if (key == string("keyImage")) sendRequest->keyImage = it->second.data(); + } + + cout << "Returning deserialized send request" << endl; + return sendRequest; +} + +vector> MoneroUtils::deserializeKeyImages(const string& keyImagesJson) { + + // deserialize json to property node + std::istringstream iss = keyImagesJson.empty() ? std::istringstream() : std::istringstream(keyImagesJson); + boost::property_tree::ptree node; + boost::property_tree::read_json(iss, node); + + // convert property tree to key images + vector> keyImages; + for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) { + string key = it->first; + //cout << "deserializeKeyImage() key: " << key << endl; + if (key == string("keyImages")) { + for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { + keyImages.push_back(nodeToKeyImage(it2->second)); + } + } + else cout << "WARNING MoneroWalletJni::deserializeKeyImages() unrecognized key: " << key << endl; + } + return keyImages; +} // TODO: strip newline string MoneroUtils::serialize(const boost::property_tree::ptree& node) { diff --git a/src/utils/MoneroUtils.h b/src/utils/MoneroUtils.h index 69f94de..8d003e4 100644 --- a/src/utils/MoneroUtils.h +++ b/src/utils/MoneroUtils.h @@ -72,50 +72,20 @@ namespace MoneroUtils void binaryBlocksToJson(const std::string &bin, std::string &json); -// void addNode(boost::property_tree::ptree root, const string& key, shared_ptr ptr) { -// cout << "addNode(...)" << endl; -// throw runtime_error("Not implemented"); -// } -// -// string serialize(const MoneroAccount& account); -// -// string serialize(const MoneroSubaddress& subaddress); -// -// string serialize(const MoneroBlock& block); -// -// void deserializeTx(const string& txStr, MoneroTx& tx); -// -// void deserializeTxWallet(const string& txStr, MoneroTxWallet& tx); -// -// void deserializeTxRequest(const string& txRequestStr, MoneroTxRequest& request); -// -// void deserializeOutput(const string& outputStr, MoneroOutput& output); -// -// void deserializeOutputWallet(const string& outputStr, MoneroOutputWallet& output); + shared_ptr deserializeTxRequest(const string& txRequestStr); - // template string serialize(const vector types) { - // cout << "serialize(types)" << endl; - // boost::property_tree::ptree root; - // boost::property_tree::ptree typesNode; - // for (const auto& type : types) { - // boost::property_tree::ptree typeNode; - // typeNode.put("", serialize(type)); - // typesNode.push_back(std::make_pair("", typeNode)); - // } - // root.add_child("types", typesNode); - // - // // serialize property tree to json - // std::stringstream ss; - // boost::property_tree::write_json(ss, root, false); - // string str = ss.str(); - // return str; - // } + shared_ptr deserializeTransferRequest(const string& transferRequestStr); + + shared_ptr deserializeOutputRequest(const string& outputRequestStr); + + shared_ptr deserializeSendRequest(const string& sendRequestStr); + + vector> deserializeKeyImages(const string& keyImagesJson); string serialize(const boost::property_tree::ptree& node); // // TODO: template implementation here, could move to MoneroUtils.hpp per https://stackoverflow.com/questions/3040480/c-template-function-compiles-in-header-but-not-implementation template boost::property_tree::ptree toPropertyTree(const vector> types) { - //cout << "toPropertyTree(types)" << endl; boost::property_tree::ptree typeNodes; for (const auto& type : types) { typeNodes.push_back(std::make_pair("", type->toPropertyTree())); @@ -125,7 +95,6 @@ namespace MoneroUtils // // TODO: template implementation here, could move to MoneroUtils.hpp per https://stackoverflow.com/questions/3040480/c-template-function-compiles-in-header-but-not-implementation template boost::property_tree::ptree toPropertyTree(const vector types) { - //cout << "toPropertyTree(types)" << endl; boost::property_tree::ptree typeNodes; for (const auto& type : types) { typeNodes.push_back(std::make_pair("", type.toPropertyTree())); @@ -136,22 +105,8 @@ namespace MoneroUtils boost::property_tree::ptree toPropertyTree(const vector strs); boost::property_tree::ptree toPropertyTree(const vector nums); - /** - * Modified from core_rpc_server.cpp to return a string. - * - * TODO: remove this duplicate, use core_rpc_server instead - */ - static std::string get_pruned_tx_json(cryptonote::transaction &tx) - { - std::stringstream ss; - json_archive ar(ss); - bool r = tx.serialize_base(ar); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to serialize rct signatures base"); - return ss.str(); - } // TODO: refactor common template code - template ::value, T>::type* = nullptr> boost::optional reconcile(const boost::optional& val1, const boost::optional& val2, boost::optional resolveDefined, boost::optional resolveTrue, boost::optional resolveMax) { @@ -243,5 +198,19 @@ namespace MoneroUtils // boost::optional reconcile(const boost::optional& val1, const boost::optional& val2) { // return reconcile(val1, val2, boost::none, boost::none, boost::none); // } + + /** + * Modified from core_rpc_server.cpp to return a string. + * + * TODO: remove this duplicate, use core_rpc_server instead + */ + static std::string get_pruned_tx_json(cryptonote::transaction &tx) + { + std::stringstream ss; + json_archive ar(ss); + bool r = tx.serialize_base(ar); + CHECK_AND_ASSERT_MES(r, std::string(), "Failed to serialize rct signatures base"); + return ss.str(); + } } #endif /* MoneroUtils_h */