mirror of
https://github.com/wownero/wownero-cpp.git
synced 2026-10-17 15:21:30 +02:00
292 lines
12 KiB
C++
292 lines
12 KiB
C++
/**
|
|
* 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<rapidjson::StringBuffer> 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<string>& 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<uint8_t>& 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<uint32_t>& 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<uint64_t>& 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 ---------------------------
|
|
|
|
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_block> 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<monero_block> block = make_shared<monero_block>();
|
|
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_hash = 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_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash));
|
|
}
|
|
return block;
|
|
}
|
|
|
|
shared_ptr<monero_tx> monero_utils::cn_tx_to_tx(const cryptonote::transaction& cn_tx, bool init_as_tx_wallet) {
|
|
shared_ptr<monero_tx> tx = init_as_tx_wallet ? make_shared<monero_tx_wallet>() : make_shared<monero_tx>();
|
|
tx->m_version = cn_tx.version;
|
|
tx->m_unlock_time = cn_tx.unlock_time;
|
|
tx->m_hash = epee::string_tools::pod_to_hex(cn_tx.hash);
|
|
tx->m_extra = cn_tx.extra;
|
|
|
|
// init inputs
|
|
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<monero_output> input = init_as_tx_wallet ? make_shared<monero_output_wallet>() : make_shared<monero_output>();
|
|
input->m_tx = tx;
|
|
tx->m_inputs.push_back(input);
|
|
const txin_to_key& txin = boost::get<txin_to_key>(cnVin);
|
|
input->m_amount = txin.amount;
|
|
input->m_ring_output_indices = txin.key_offsets;
|
|
crypto::key_image cnKeyImage = txin.k_image;
|
|
input->m_key_image = make_shared<monero_key_image>();
|
|
input->m_key_image.get()->m_hex = epee::string_tools::pod_to_hex(cnKeyImage);
|
|
}
|
|
|
|
// init outputs
|
|
for (const tx_out& cnVout : cn_tx.vout) {
|
|
shared_ptr<monero_output> output = init_as_tx_wallet ? make_shared<monero_output_wallet>() : make_shared<monero_output>();
|
|
output->m_tx = tx;
|
|
tx->m_outputs.push_back(output);
|
|
output->m_amount = cnVout.amount;
|
|
const crypto::public_key& cnStealthPublicKey = boost::get<txout_to_key>(cnVout.target).key;
|
|
output->m_stealth_public_key = epee::string_tools::pod_to_hex(cnStealthPublicKey);
|
|
}
|
|
|
|
return tx;
|
|
|
|
// TODO: finish this, cryptonote::transaction has:
|
|
// std::vector<std::vector<crypto::signature> > m_signatures;
|
|
// rct::rctSig m_rct_signatures;
|
|
// mutable size_t blob_size;
|
|
}
|