/** * Copyright (c) 2017-2019 woodser * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Parts of this file are originally copyright (c) 2014-2019, The Monero Project * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * All rights reserved. * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers */ #include "monero_utils.h" #include "rpc/core_rpc_server_commands_defs.h" #include "storages/portable_storage_template_helper.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "string_tools.h" using namespace std; using namespace cryptonote; using namespace monero_utils; // -------------------------- BINARY SERIALIZATION ---------------------------- void monero_utils::json_to_binary(const std::string &json, std::string &bin) { epee::serialization::portable_storage ps; ps.load_from_json(json); ps.store_to_binary(bin); } void monero_utils::binary_to_json(const std::string &bin, std::string &json) { epee::serialization::portable_storage ps; ps.load_from_binary(bin); ps.dump_as_json(json); } void monero_utils::binary_blocks_to_json(const std::string &bin, std::string &json) { // load binary rpc response to struct cryptonote::COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response resp_struct; epee::serialization::load_t_from_binary(resp_struct, bin); // build property tree from deserialized blocks and transactions boost::property_tree::ptree root; boost::property_tree::ptree blocksNode; // array of block strings boost::property_tree::ptree txsNodes; // array of txs per block (array of array) for (int blockIdx = 0; blockIdx < resp_struct.blocks.size(); blockIdx++) { // parse and validate block cryptonote::block block; if (cryptonote::parse_and_validate_block_from_blob(resp_struct.blocks[blockIdx].block, block)) { // add block node to blocks node boost::property_tree::ptree blockNode; blockNode.put("", cryptonote::obj_to_json_str(block)); // TODO: no pretty print blocksNode.push_back(std::make_pair("", blockNode)); } else { throw std::runtime_error("failed to parse block blob at index " + std::to_string(blockIdx)); } // parse and validate txs boost::property_tree::ptree txs_node; for (int txIdx = 0; txIdx < resp_struct.blocks[blockIdx].txs.size(); txIdx++) { cryptonote::transaction tx; if (cryptonote::parse_and_validate_tx_from_blob(resp_struct.blocks[blockIdx].txs[txIdx].blob, tx)) { // add tx node to txs node boost::property_tree::ptree txNode; MTRACE("PRUNED:\n" << monero_utils::get_pruned_tx_json(tx)); txNode.put("", monero_utils::get_pruned_tx_json(tx)); // TODO: no pretty print txs_node.push_back(std::make_pair("", txNode)); } else { throw std::runtime_error("failed to parse tx blob at index " + std::to_string(txIdx)); } } txsNodes.push_back(std::make_pair("", txs_node)); // array of array of transactions, one array per block } root.add_child("blocks", blocksNode); root.add_child("txs", txsNodes); root.put("status", resp_struct.status); root.put("untrusted", resp_struct.untrusted); // TODO: loss of ints and bools // convert root to string // TODO: common utility with serial_bridge std::stringstream ss; boost::property_tree::write_json(ss, root, false/*pretty*/); json = ss.str(); } // ------------------------------- RAPIDJSON ---------------------------------- string monero_utils::serialize(const rapidjson::Document& doc) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); doc.Accept(writer); return buffer.GetString(); } void monero_utils::addJsonMember(string key, uint8_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) { rapidjson::Value field_key(key.c_str(), key.size(), allocator); field.SetInt(val); root.AddMember(field_key, field, allocator); } void monero_utils::addJsonMember(string key, uint32_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) { rapidjson::Value field_key(key.c_str(), key.size(), allocator); field.SetUint64(val); root.AddMember(field_key, field, allocator); } void monero_utils::addJsonMember(string key, uint64_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) { rapidjson::Value field_key(key.c_str(), key.size(), allocator); field.SetUint64(val); root.AddMember(field_key, field, allocator); } void monero_utils::addJsonMember(string key, string val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) { rapidjson::Value field_key(key.c_str(), key.size(), allocator); field.SetString(val.c_str(), val.size(), allocator); root.AddMember(field_key, field, allocator); } void monero_utils::addJsonMember(string key, bool val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root) { rapidjson::Value field_key(key.c_str(), key.size(), allocator); if (val) { rapidjson::Value field_val(rapidjson::kTrueType); root.AddMember(field_key, field_val, allocator); } else { rapidjson::Value field_val(rapidjson::kFalseType); root.AddMember(field_key, field_val, allocator); } } rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector& strs) { rapidjson::Value value_arr(rapidjson::kArrayType); rapidjson::Value value_str(rapidjson::kStringType); for (const string& str : strs) { value_str.SetString(str.c_str(), str.size(), allocator); value_arr.PushBack(value_str, allocator); } return value_arr; } rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector& nums) { rapidjson::Value value_arr(rapidjson::kArrayType); rapidjson::Value value_num(rapidjson::kNumberType); for (const auto& num : nums) { value_num.SetInt(num); value_arr.PushBack(value_num, allocator); } return value_arr; } // TODO: remove these redundant implementations for different sizes? rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector& nums) { rapidjson::Value value_arr(rapidjson::kArrayType); rapidjson::Value value_num(rapidjson::kNumberType); for (const auto& num : nums) { value_num.SetUint64(num); value_arr.PushBack(value_num, allocator); } return value_arr; } rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector& nums) { rapidjson::Value value_arr(rapidjson::kArrayType); rapidjson::Value value_num(rapidjson::kNumberType); for (const auto& num : nums) { value_num.SetUint64(num); value_arr.PushBack(value_num, allocator); } return value_arr; } // ------------------------ PROPERTY TREES --------------------------- // TODO: switch to rapidjson string monero_utils::serialize(const boost::property_tree::ptree& node) { std::stringstream ss; boost::property_tree::write_json(ss, node, false); string str = ss.str(); return str.substr(0, str.size() - 1); // strip newline } void monero_utils::deserialize(const string& json, boost::property_tree::ptree& root) { std::istringstream iss = json.empty() ? std::istringstream() : std::istringstream(json); try { boost::property_tree::read_json(iss, root); } catch (std::exception const& e) { throw runtime_error("Invalid JSON"); } } // ---------------------------------------------------------------------------- // TODO: this is unused shared_ptr monero_utils::cn_block_to_block(const cryptonote::block& cn_block) { cryptonote::block temp = cn_block; cout << cryptonote::obj_to_json_str(temp) << endl; shared_ptr block = make_shared(); block->m_major_version = cn_block.major_version; block->m_minor_version = cn_block.minor_version; block->m_timestamp = cn_block.timestamp; block->m_prev_id = epee::string_tools::pod_to_hex(cn_block.prev_id); block->m_nonce = cn_block.nonce; block->m_miner_tx = monero_utils::cn_tx_to_tx(cn_block.miner_tx); for (const crypto::hash& tx_hash : cn_block.tx_hashes) { block->m_tx_ids.push_back(epee::string_tools::pod_to_hex(tx_hash)); } return block; } shared_ptr monero_utils::cn_tx_to_tx(const cryptonote::transaction& cn_tx, bool init_as_tx_wallet) { shared_ptr tx = init_as_tx_wallet ? make_shared() : make_shared(); tx->m_version = cn_tx.version; tx->m_unlock_time = cn_tx.unlock_time; tx->m_id = epee::string_tools::pod_to_hex(cn_tx.hash); tx->m_extra = cn_tx.extra; // init vins for (const txin_v& cnVin : cn_tx.vin) { if (cnVin.which() != 0 && cnVin.which() != 3) throw runtime_error("Unsupported variant type"); if (tx->m_is_miner_tx == boost::none) tx->m_is_miner_tx = cnVin.which() == 0; if (cnVin.which() != 3) continue; // only process txin_to_key of variant TODO: support other types, like 0 "gen" which is miner tx? shared_ptr vin = init_as_tx_wallet ? make_shared() : make_shared(); vin->m_tx = tx; tx->m_vins.push_back(vin); const txin_to_key& txin = boost::get(cnVin); vin->m_amount = txin.amount; vin->m_ring_output_indices = txin.key_offsets; crypto::key_image cnKeyImage = txin.k_image; vin->m_key_image = make_shared(); vin->m_key_image.get()->m_hex = epee::string_tools::pod_to_hex(cnKeyImage); } // init vouts for (const tx_out& cnVout : cn_tx.vout) { shared_ptr vout = init_as_tx_wallet ? make_shared() : make_shared(); vout->m_tx = tx; tx->m_vouts.push_back(vout); vout->m_amount = cnVout.amount; const crypto::public_key& cnStealthPublicKey = boost::get(cnVout.target).key; vout->m_stealth_public_key = epee::string_tools::pod_to_hex(cnStealthPublicKey); } return tx; // TODO: finish this, cryptonote::transaction has: // std::vector > m_signatures; // rct::rctSig m_rct_signatures; // mutable size_t blob_size; }