// // serial_bridge_index.cpp // Copyright (c) 2014-2019, MyMonero.com // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 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. // // // #include "serial_bridge_index.hpp" // #include #include #include // #include "monero_fork_rules.hpp" #include "monero_transfer_utils.hpp" #include "monero_address_utils.hpp" // TODO: split this/these out into a different namespaces or file so this file can scale (leave file for shared utils) #include "monero_paymentID_utils.hpp" #include "monero_wallet_utils.hpp" #include "monero_key_image_utils.hpp" #include "wallet_errors.h" #include "string_tools.h" #include "ringct/rctSigs.h" // #include "serial_bridge_utils.hpp" using namespace std; using namespace boost; using namespace cryptonote; using namespace monero_transfer_utils; using namespace monero_fork_rules; // using namespace serial_bridge; using namespace serial_bridge_utils; // // // Bridge Function Implementations // string serial_bridge::decode_address(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } auto retVals = monero::address_utils::decodedAddress(json_root.get("address"), nettype_from_string(json_root.get("nettype_string"))); if (retVals.did_error) { return error_ret_json_from_message(*(retVals.err_string)); } boost::property_tree::ptree root; root.put(ret_json_key__isSubaddress(), retVals.isSubaddress); root.put(ret_json_key__pub_viewKey_string(), *(retVals.pub_viewKey_string)); root.put(ret_json_key__pub_spendKey_string(), *(retVals.pub_spendKey_string)); if (retVals.paymentID_string != none) { root.put(ret_json_key__paymentID_string(), *(retVals.paymentID_string)); } // return ret_json_from_root(root); } string serial_bridge::is_subaddress(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } bool retVal = monero::address_utils::isSubAddress(json_root.get("address"), nettype_from_string(json_root.get("nettype_string"))); boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), retVal); // return ret_json_from_root(root); } string serial_bridge::is_integrated_address(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } bool retVal = monero::address_utils::isIntegratedAddress(json_root.get("address"), nettype_from_string(json_root.get("nettype_string"))); boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), retVal); // return ret_json_from_root(root); } string serial_bridge::new_integrated_address(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } optional retVal = monero::address_utils::new_integratedAddrFromStdAddr(json_root.get("address"), json_root.get("short_pid"), nettype_from_string(json_root.get("nettype_string"))); boost::property_tree::ptree root; if (retVal != none) { root.put(ret_json_key__generic_retVal(), *retVal); } // return ret_json_from_root(root); } string serial_bridge::new_payment_id(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } optional retVal = monero_paymentID_utils::new_short_plain_paymentID_string(); boost::property_tree::ptree root; if (retVal != none) { root.put(ret_json_key__generic_retVal(), *retVal); } // return ret_json_from_root(root); } // string serial_bridge::newly_created_wallet(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } monero_wallet_utils::WalletDescriptionRetVals retVals; bool r = monero_wallet_utils::convenience__new_wallet_with_language_code( json_root.get("locale_language_code"), retVals, nettype_from_string(json_root.get("nettype_string")) ); bool did_error = retVals.did_error; if (!r) { return error_ret_json_from_message(*(retVals.err_string)); } THROW_WALLET_EXCEPTION_IF(did_error, error::wallet_internal_error, "Illegal success flag but did_error"); // boost::property_tree::ptree root; root.put( ret_json_key__mnemonic_string(), std::string((*(retVals.optl__desc)).mnemonic_string.data(), (*(retVals.optl__desc)).mnemonic_string.size()) ); root.put(ret_json_key__mnemonic_language(), (*(retVals.optl__desc)).mnemonic_language); root.put(ret_json_key__sec_seed_string(), (*(retVals.optl__desc)).sec_seed_string); root.put(ret_json_key__address_string(), (*(retVals.optl__desc)).address_string); root.put(ret_json_key__pub_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).pub_viewKey)); root.put(ret_json_key__sec_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).sec_viewKey)); root.put(ret_json_key__pub_spendKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).pub_spendKey)); root.put(ret_json_key__sec_spendKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).sec_spendKey)); // return ret_json_from_root(root); } string serial_bridge::are_equal_mnemonics(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } bool equal; try { equal = monero_wallet_utils::are_equal_mnemonics( json_root.get("a"), json_root.get("b") ); } catch (std::exception const& e) { return error_ret_json_from_message(e.what()); } boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), equal); // return ret_json_from_root(root); } string serial_bridge::address_and_keys_from_seed(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } monero_wallet_utils::ComponentsFromSeed_RetVals retVals; bool r = monero_wallet_utils::address_and_keys_from_seed( json_root.get("seed_string"), nettype_from_string(json_root.get("nettype_string")), retVals ); bool did_error = retVals.did_error; if (!r) { return error_ret_json_from_message(*(retVals.err_string)); } THROW_WALLET_EXCEPTION_IF(did_error, error::wallet_internal_error, "Illegal success flag but did_error"); // boost::property_tree::ptree root; root.put(ret_json_key__address_string(), (*(retVals.optl__val)).address_string); root.put(ret_json_key__pub_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__val)).pub_viewKey)); root.put(ret_json_key__sec_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__val)).sec_viewKey)); root.put(ret_json_key__pub_spendKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__val)).pub_spendKey)); root.put(ret_json_key__sec_spendKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__val)).sec_spendKey)); // return ret_json_from_root(root); } string serial_bridge::mnemonic_from_seed(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } monero_wallet_utils::SeedDecodedMnemonic_RetVals retVals = monero_wallet_utils::mnemonic_string_from_seed_hex_string( json_root.get("seed_string"), json_root.get("wordset_name") ); boost::property_tree::ptree root; if (retVals.err_string != none) { return error_ret_json_from_message(*(retVals.err_string)); } root.put( ret_json_key__generic_retVal(), std::string((*(retVals.mnemonic_string)).data(), (*(retVals.mnemonic_string)).size()) ); // return ret_json_from_root(root); } string serial_bridge::seed_and_keys_from_mnemonic(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } monero_wallet_utils::WalletDescriptionRetVals retVals; bool r = monero_wallet_utils::wallet_with( json_root.get("mnemonic_string"), retVals, nettype_from_string(json_root.get("nettype_string")) ); bool did_error = retVals.did_error; if (!r) { return error_ret_json_from_message(*retVals.err_string); } monero_wallet_utils::WalletDescription walletDescription = *(retVals.optl__desc); THROW_WALLET_EXCEPTION_IF(did_error, error::wallet_internal_error, "Illegal success flag but did_error"); // boost::property_tree::ptree root; root.put(ret_json_key__sec_seed_string(), (*(retVals.optl__desc)).sec_seed_string); root.put(ret_json_key__mnemonic_language(), (*(retVals.optl__desc)).mnemonic_language); root.put(ret_json_key__address_string(), (*(retVals.optl__desc)).address_string); root.put(ret_json_key__pub_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).pub_viewKey)); root.put(ret_json_key__sec_viewKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).sec_viewKey)); root.put(ret_json_key__pub_spendKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).pub_spendKey)); root.put(ret_json_key__sec_spendKey_string(), epee::string_tools::pod_to_hex((*(retVals.optl__desc)).sec_spendKey)); // return ret_json_from_root(root); } string serial_bridge::validate_components_for_login(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } monero_wallet_utils::WalletComponentsValidationResults retVals; bool r = monero_wallet_utils::validate_wallet_components_with( // returns !did_error json_root.get("address_string"), json_root.get("sec_viewKey_string"), json_root.get_optional("sec_spendKey_string"), json_root.get_optional("seed_string"), nettype_from_string(json_root.get("nettype_string")), retVals ); bool did_error = retVals.did_error; if (!r) { return error_ret_json_from_message(*retVals.err_string); } THROW_WALLET_EXCEPTION_IF(did_error, error::wallet_internal_error, "Illegal success flag but did_error"); // boost::property_tree::ptree root; root.put(ret_json_key__isValid(), retVals.isValid); root.put(ret_json_key__isInViewOnlyMode(), retVals.isInViewOnlyMode); root.put(ret_json_key__pub_viewKey_string(), retVals.pub_viewKey_string); root.put(ret_json_key__pub_spendKey_string(), retVals.pub_spendKey_string); // return ret_json_from_root(root); } string serial_bridge::estimated_tx_network_fee(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } uint8_t fork_version = 0; // if missing optional optl__fork_version_string = json_root.get_optional("fork_version"); if (optl__fork_version_string != none) { fork_version = stoul(*optl__fork_version_string); } uint64_t fee = monero_fee_utils::estimated_tx_network_fee( stoull(json_root.get("fee_per_b")), stoul(json_root.get("priority")), monero_fork_rules::make_use_fork_rules_fn(fork_version) ); std::ostringstream o; o << fee; // boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), o.str()); // return ret_json_from_root(root); } string serial_bridge::estimate_fee(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { return error_ret_json_from_message("Invalid JSON"); } // bool use_per_byte_fee = json_root.get("use_per_byte_fee"); bool use_rct = json_root.get("use_rct"); int n_inputs = stoul(json_root.get("n_inputs")); int mixin = stoul(json_root.get("mixin")); int n_outputs = stoul(json_root.get("n_outputs")); size_t extra_size = stoul(json_root.get("extra_size")); bool bulletproof = json_root.get("bulletproof"); uint64_t base_fee = stoull(json_root.get("base_fee")); uint64_t fee_quantization_mask = stoull(json_root.get("fee_quantization_mask")); uint32_t priority = stoul(json_root.get("priority")); uint8_t fork_version = stoul(json_root.get("fork_version")); use_fork_rules_fn_type use_fork_rules_fn = monero_fork_rules::make_use_fork_rules_fn(fork_version); uint64_t fee_multiplier = monero_fee_utils::get_fee_multiplier(priority, monero_fee_utils::default_priority(), monero_fee_utils::get_fee_algorithm(use_fork_rules_fn), use_fork_rules_fn); // uint64_t fee = monero_fee_utils::estimate_fee(use_per_byte_fee, use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, base_fee, fee_multiplier, fee_quantization_mask); // std::ostringstream o; o << fee; boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), o.str()); // return ret_json_from_root(root); } string serial_bridge::estimate_tx_weight(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { return error_ret_json_from_message("Invalid JSON"); } // bool use_rct = json_root.get("use_rct"); int n_inputs = stoul(json_root.get("n_inputs")); int mixin = stoul(json_root.get("mixin")); int n_outputs = stoul(json_root.get("n_outputs")); size_t extra_size = stoul(json_root.get("extra_size")); bool bulletproof = json_root.get("bulletproof"); // uint64_t weight = monero_fee_utils::estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); // std::ostringstream o; o << weight; boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), o.str()); // return ret_json_from_root(root); } string serial_bridge::estimate_rct_tx_size(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } std::size_t size = monero_fee_utils::estimate_rct_tx_size( stoul(json_root.get("n_inputs")), stoul(json_root.get("mixin")), stoul(json_root.get("n_outputs")), stoul(json_root.get("extra_size")), json_root.get("bulletproof") ); std::ostringstream o; o << size; // boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), o.str()); // return ret_json_from_root(root); } // string serial_bridge::generate_key_image(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } crypto::secret_key sec_viewKey{}; crypto::secret_key sec_spendKey{}; crypto::public_key pub_spendKey{}; crypto::public_key tx_pub_key{}; { bool r = false; r = epee::string_tools::hex_to_pod(std::string(json_root.get("sec_viewKey_string")), sec_viewKey); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Invalid secret view key"); r = epee::string_tools::hex_to_pod(std::string(json_root.get("sec_spendKey_string")), sec_spendKey); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Invalid secret spend key"); r = epee::string_tools::hex_to_pod(std::string(json_root.get("pub_spendKey_string")), pub_spendKey); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Invalid public spend key"); r = epee::string_tools::hex_to_pod(std::string(json_root.get("tx_pub_key")), tx_pub_key); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Invalid tx pub key"); } monero_key_image_utils::KeyImageRetVals retVals; bool r = monero_key_image_utils::new__key_image( pub_spendKey, sec_spendKey, sec_viewKey, tx_pub_key, stoull(json_root.get("out_index")), retVals ); if (!r) { return error_ret_json_from_message("Unable to generate key image"); // TODO: return error string? (unwrap optional) } boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), epee::string_tools::pod_to_hex(retVals.calculated_key_image)); // return ret_json_from_root(root); } // string serial_bridge::send_step1__prepare_params_for_get_decoys(const string &args_string) { // TODO: possibly allow this fn to take tx sec key as an arg, although, random bit gen is now handled well by emscripten boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } // vector unspent_outs; BOOST_FOREACH(boost::property_tree::ptree::value_type &output_desc, json_root.get_child("unspent_outs")) { assert(output_desc.first.empty()); // array elements have no names SpendableOutput out{}; out.amount = stoull(output_desc.second.get("amount")); out.public_key = output_desc.second.get("public_key"); out.rct = output_desc.second.get_optional("rct"); if (out.rct != none && (*out.rct).empty() == true) { out.rct = none; // just in case it's an empty string, send to 'none' (even though receiving code now handles empty strs) } out.global_index = stoull(output_desc.second.get("global_index")); out.index = stoull(output_desc.second.get("index")); out.tx_pub_key = output_desc.second.get("tx_pub_key"); // unspent_outs.push_back(std::move(out)); } optional optl__passedIn_attemptAt_fee_string = json_root.get_optional("passedIn_attemptAt_fee"); optional optl__passedIn_attemptAt_fee = none; if (optl__passedIn_attemptAt_fee_string != none) { optl__passedIn_attemptAt_fee = stoull(*optl__passedIn_attemptAt_fee_string); } uint8_t fork_version = 0; // if missing optional optl__fork_version_string = json_root.get_optional("fork_version"); if (optl__fork_version_string != none) { fork_version = stoul(*optl__fork_version_string); } Send_Step1_RetVals retVals; monero_transfer_utils::send_step1__prepare_params_for_get_decoys( retVals, // json_root.get_optional("payment_id_string"), stoull(json_root.get("sending_amount")), json_root.get("is_sweeping"), stoul(json_root.get("priority")), monero_fork_rules::make_use_fork_rules_fn(fork_version), unspent_outs, stoull(json_root.get("fee_per_b")), // per v8 stoull(json_root.get("fee_mask")), // optl__passedIn_attemptAt_fee // use this for passing step2 "must-reconstruct" return values back in, i.e. re-entry; when nil, defaults to attempt at network min ); boost::property_tree::ptree root; if (retVals.errCode != noError) { root.put(ret_json_key__any__err_code(), retVals.errCode); root.put(ret_json_key__any__err_msg(), err_msg_from_err_code__create_transaction(retVals.errCode)); // // The following will be set if errCode==needMoreMoneyThanFound - and i'm depending on them being 0 otherwise root.put(ret_json_key__send__spendable_balance(), RetVals_Transforms::str_from(retVals.spendable_balance)); root.put(ret_json_key__send__required_balance(), RetVals_Transforms::str_from(retVals.required_balance)); } else { root.put(ret_json_key__send__mixin(), RetVals_Transforms::str_from(retVals.mixin)); root.put(ret_json_key__send__using_fee(), RetVals_Transforms::str_from(retVals.using_fee)); root.put(ret_json_key__send__final_total_wo_fee(), RetVals_Transforms::str_from(retVals.final_total_wo_fee)); root.put(ret_json_key__send__change_amount(), RetVals_Transforms::str_from(retVals.change_amount)); { boost::property_tree::ptree using_outs_ptree; BOOST_FOREACH(SpendableOutput &out, retVals.using_outs) { // PROBABLY don't need to shuttle these back (could send only public_key) but consumers might like the feature of being able to send this JSON structure directly back to step2 without reconstructing it for themselves auto out_ptree_pair = std::make_pair("", boost::property_tree::ptree{}); auto& out_ptree = out_ptree_pair.second; out_ptree.put("amount", RetVals_Transforms::str_from(out.amount)); out_ptree.put("public_key", out.public_key); if (out.rct != none && (*out.rct).empty() == false) { out_ptree.put("rct", *out.rct); } out_ptree.put("global_index", RetVals_Transforms::str_from(out.global_index)); out_ptree.put("index", RetVals_Transforms::str_from(out.index)); out_ptree.put("tx_pub_key", out.tx_pub_key); using_outs_ptree.push_back(out_ptree_pair); } root.add_child(ret_json_key__send__using_outs(), using_outs_ptree); } } return ret_json_from_root(root); } string serial_bridge::send_step2__try_create_transaction(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } // vector using_outs; BOOST_FOREACH(boost::property_tree::ptree::value_type &output_desc, json_root.get_child("using_outs")) { assert(output_desc.first.empty()); // array elements have no names SpendableOutput out{}; out.amount = stoull(output_desc.second.get("amount")); out.public_key = output_desc.second.get("public_key"); out.rct = output_desc.second.get_optional("rct"); if (out.rct != none && (*out.rct).empty() == true) { out.rct = none; // send to 'none' if empty str for safety } out.global_index = stoull(output_desc.second.get("global_index")); out.index = stoull(output_desc.second.get("index")); out.tx_pub_key = output_desc.second.get("tx_pub_key"); // using_outs.push_back(std::move(out)); } vector mix_outs; BOOST_FOREACH(boost::property_tree::ptree::value_type &mix_out_desc, json_root.get_child("mix_outs")) { assert(mix_out_desc.first.empty()); // array elements have no names auto amountAndOuts = RandomAmountOutputs{}; amountAndOuts.amount = stoull(mix_out_desc.second.get("amount")); BOOST_FOREACH(boost::property_tree::ptree::value_type &mix_out_output_desc, mix_out_desc.second.get_child("outputs")) { assert(mix_out_output_desc.first.empty()); // array elements have no names auto amountOutput = RandomAmountOutput{}; amountOutput.global_index = stoull(mix_out_output_desc.second.get("global_index")); // this is, I believe, presently supplied as a string by the API, probably to avoid overflow amountOutput.public_key = mix_out_output_desc.second.get("public_key"); amountOutput.rct = mix_out_output_desc.second.get_optional("rct"); amountAndOuts.outputs.push_back(std::move(amountOutput)); } mix_outs.push_back(std::move(amountAndOuts)); } uint8_t fork_version = 0; // if missing optional optl__fork_version_string = json_root.get_optional("fork_version"); if (optl__fork_version_string != none) { fork_version = stoul(*optl__fork_version_string); } Send_Step2_RetVals retVals; monero_transfer_utils::send_step2__try_create_transaction( retVals, // json_root.get("from_address_string"), json_root.get("sec_viewKey_string"), json_root.get("sec_spendKey_string"), json_root.get("to_address_string"), json_root.get_optional("payment_id_string"), stoull(json_root.get("final_total_wo_fee")), stoull(json_root.get("change_amount")), stoull(json_root.get("fee_amount")), stoul(json_root.get("priority")), using_outs, stoull(json_root.get("fee_per_b")), stoull(json_root.get("fee_mask")), mix_outs, monero_fork_rules::make_use_fork_rules_fn(fork_version), stoull(json_root.get("unlock_time")), nettype_from_string(json_root.get("nettype_string")) ); boost::property_tree::ptree root; if (retVals.errCode != noError) { root.put(ret_json_key__any__err_code(), retVals.errCode); root.put(ret_json_key__any__err_msg(), err_msg_from_err_code__create_transaction(retVals.errCode)); } else { if (retVals.tx_must_be_reconstructed) { root.put(ret_json_key__send__tx_must_be_reconstructed(), true); root.put(ret_json_key__send__fee_actually_needed(), RetVals_Transforms::str_from(retVals.fee_actually_needed)); // must be passed back } else { root.put(ret_json_key__send__tx_must_be_reconstructed(), false); // so consumers have it available root.put(ret_json_key__send__serialized_signed_tx(), *(retVals.signed_serialized_tx_string)); root.put(ret_json_key__send__tx_hash(), *(retVals.tx_hash_string)); root.put(ret_json_key__send__tx_key(), *(retVals.tx_key_string)); root.put(ret_json_key__send__tx_pub_key(), *(retVals.tx_pub_key_string)); } } return ret_json_from_root(root); } // string serial_bridge::decodeRct(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } rct::key sk; if (!epee::string_tools::hex_to_pod(json_root.get("sk"), sk)) { return error_ret_json_from_message("Invalid 'sk'"); } unsigned int i = stoul(json_root.get("i")); // NOTE: this rv structure parsing could be factored but it presently does not implement a number of sub-components of rv, such as .pseudoOuts auto rv_desc = json_root.get_child("rv"); rct::rctSig rv = AUTO_VAL_INIT(rv); unsigned int rv_type_int = stoul(rv_desc.get("type")); // got to be a better way to do this if (rv_type_int == rct::RCTTypeNull) { rv.type = rct::RCTTypeNull; } else if (rv_type_int == rct::RCTTypeSimple) { rv.type = rct::RCTTypeSimple; } else if (rv_type_int == rct::RCTTypeFull) { rv.type = rct::RCTTypeFull; } else if (rv_type_int == rct::RCTTypeBulletproof) { rv.type = rct::RCTTypeBulletproof; } else if (rv_type_int == rct::RCTTypeBulletproof2) { rv.type = rct::RCTTypeBulletproof2; } else { return error_ret_json_from_message("Invalid 'rv.type'"); } BOOST_FOREACH(boost::property_tree::ptree::value_type &ecdh_info_desc, rv_desc.get_child("ecdhInfo")) { assert(ecdh_info_desc.first.empty()); // array elements have no names auto ecdh_info = rct::ecdhTuple{}; if (!epee::string_tools::hex_to_pod(ecdh_info_desc.second.get("mask"), ecdh_info.mask)) { return error_ret_json_from_message("Invalid rv.ecdhInfo[].mask"); } if (!epee::string_tools::hex_to_pod(ecdh_info_desc.second.get("amount"), ecdh_info.amount)) { return error_ret_json_from_message("Invalid rv.ecdhInfo[].amount"); } rv.ecdhInfo.push_back(ecdh_info); // rct keys aren't movable } BOOST_FOREACH(boost::property_tree::ptree::value_type &outPk_desc, rv_desc.get_child("outPk")) { assert(outPk_desc.first.empty()); // array elements have no names auto outPk = rct::ctkey{}; if (!epee::string_tools::hex_to_pod(outPk_desc.second.get("mask"), outPk.mask)) { return error_ret_json_from_message("Invalid rv.outPk[].mask"); } // FIXME: does dest need to be placed on the key? rv.outPk.push_back(outPk); // rct keys aren't movable } // rct::key mask; rct::xmr_amount/*uint64_t*/ decoded_amount; try { decoded_amount = rct::decodeRct( rv, sk, i, mask, hw::get_device("default") // presently this uses the default device but we could let a string be passed to switch the type ); } catch (std::exception const& e) { return error_ret_json_from_message(e.what()); } ostringstream decoded_amount_ss; decoded_amount_ss << decoded_amount; // boost::property_tree::ptree root; root.put(ret_json_key__decodeRct_mask(), epee::string_tools::pod_to_hex(mask)); root.put(ret_json_key__decodeRct_amount(), decoded_amount_ss.str()); // return ret_json_from_root(root); } // string serial_bridge::decodeRctSimple(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } rct::key sk; if (!epee::string_tools::hex_to_pod(json_root.get("sk"), sk)) { return error_ret_json_from_message("Invalid 'sk'"); } unsigned int i = stoul(json_root.get("i")); // NOTE: this rv structure parsing could be factored but it presently does not implement a number of sub-components of rv, such as .pseudoOuts auto rv_desc = json_root.get_child("rv"); rct::rctSig rv = AUTO_VAL_INIT(rv); unsigned int rv_type_int = stoul(rv_desc.get("type")); // got to be a better way to do this if (rv_type_int == rct::RCTTypeNull) { rv.type = rct::RCTTypeNull; } else if (rv_type_int == rct::RCTTypeSimple) { rv.type = rct::RCTTypeSimple; } else if (rv_type_int == rct::RCTTypeFull) { rv.type = rct::RCTTypeFull; } else if (rv_type_int == rct::RCTTypeBulletproof) { rv.type = rct::RCTTypeBulletproof; } else if (rv_type_int == rct::RCTTypeBulletproof2) { rv.type = rct::RCTTypeBulletproof2; } else { return error_ret_json_from_message("Invalid 'rv.type'"); } BOOST_FOREACH(boost::property_tree::ptree::value_type &ecdh_info_desc, rv_desc.get_child("ecdhInfo")) { assert(ecdh_info_desc.first.empty()); // array elements have no names auto ecdh_info = rct::ecdhTuple{}; if (rv.type == rct::RCTTypeBulletproof2) { if (!epee::string_tools::hex_to_pod(ecdh_info_desc.second.get("amount"), (crypto::hash8&)ecdh_info.amount)) { return error_ret_json_from_message("Invalid rv.ecdhInfo[].amount"); } } else { if (!epee::string_tools::hex_to_pod(ecdh_info_desc.second.get("mask"), ecdh_info.mask)) { return error_ret_json_from_message("Invalid rv.ecdhInfo[].mask"); } if (!epee::string_tools::hex_to_pod(ecdh_info_desc.second.get("amount"), ecdh_info.amount)) { return error_ret_json_from_message("Invalid rv.ecdhInfo[].amount"); } } rv.ecdhInfo.push_back(ecdh_info); } BOOST_FOREACH(boost::property_tree::ptree::value_type &outPk_desc, rv_desc.get_child("outPk")) { assert(outPk_desc.first.empty()); // array elements have no names auto outPk = rct::ctkey{}; if (!epee::string_tools::hex_to_pod(outPk_desc.second.get("mask"), outPk.mask)) { return error_ret_json_from_message("Invalid rv.outPk[].mask"); } // FIXME: does dest need to be placed on the key? rv.outPk.push_back(outPk); } // rct::key mask; rct::xmr_amount/*uint64_t*/ decoded_amount; try { decoded_amount = rct::decodeRctSimple( rv, sk, i, mask, hw::get_device("default") // presently this uses the default device but we could let a string be passed to switch the type ); } catch (std::exception const& e) { return error_ret_json_from_message(e.what()); } stringstream decoded_amount_ss; decoded_amount_ss << decoded_amount; // boost::property_tree::ptree root; root.put(ret_json_key__decodeRct_mask(), epee::string_tools::pod_to_hex(mask)); root.put(ret_json_key__decodeRct_amount(), decoded_amount_ss.str()); // return ret_json_from_root(root); } string serial_bridge::generate_key_derivation(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } public_key pub_key; if (!epee::string_tools::hex_to_pod(json_root.get("pub"), pub_key)) { return error_ret_json_from_message("Invalid 'pub'"); } secret_key sec_key; if (!epee::string_tools::hex_to_pod(json_root.get("sec"), sec_key)) { return error_ret_json_from_message("Invalid 'sec'"); } crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); if (!crypto::generate_key_derivation(pub_key, sec_key, derivation)) { return error_ret_json_from_message("Unable to generate key derivation"); } boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), epee::string_tools::pod_to_hex(derivation)); // return ret_json_from_root(root); } string serial_bridge::derive_public_key(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } crypto::key_derivation derivation; if (!epee::string_tools::hex_to_pod(json_root.get("derivation"), derivation)) { return error_ret_json_from_message("Invalid 'derivation'"); } std::size_t output_index = stoul(json_root.get("out_index")); crypto::public_key base; if (!epee::string_tools::hex_to_pod(json_root.get("pub"), base)) { return error_ret_json_from_message("Invalid 'pub'"); } crypto::public_key derived_key = AUTO_VAL_INIT(derived_key); if (!crypto::derive_public_key(derivation, output_index, base, derived_key)) { return error_ret_json_from_message("Unable to derive public key"); } boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), epee::string_tools::pod_to_hex(derived_key)); // return ret_json_from_root(root); } string serial_bridge::derive_subaddress_public_key(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } crypto::key_derivation derivation; if (!epee::string_tools::hex_to_pod(json_root.get("derivation"), derivation)) { return error_ret_json_from_message("Invalid 'derivation'"); } std::size_t output_index = stoul(json_root.get("out_index")); crypto::public_key out_key; if (!epee::string_tools::hex_to_pod(json_root.get("output_key"), out_key)) { return error_ret_json_from_message("Invalid 'output_key'"); } crypto::public_key derived_key = AUTO_VAL_INIT(derived_key); if (!crypto::derive_subaddress_public_key(out_key, derivation, output_index, derived_key)) { return error_ret_json_from_message("Unable to derive public key"); } boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), epee::string_tools::pod_to_hex(derived_key)); // return ret_json_from_root(root); } string serial_bridge::derivation_to_scalar(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } crypto::key_derivation derivation; if (!epee::string_tools::hex_to_pod(json_root.get("derivation"), derivation)) { return error_ret_json_from_message("Invalid 'derivation'"); } std::size_t output_index = stoul(json_root.get("output_index")); crypto::ec_scalar scalar = AUTO_VAL_INIT(scalar); crypto::derivation_to_scalar(derivation, output_index, scalar); boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), epee::string_tools::pod_to_hex(scalar)); // return ret_json_from_root(root); } string serial_bridge::encrypt_payment_id(const string &args_string) { boost::property_tree::ptree json_root; if (!parsed_json_root(args_string, json_root)) { // it will already have thrown an exception return error_ret_json_from_message("Invalid JSON"); } crypto::hash8 payment_id; if (!epee::string_tools::hex_to_pod(json_root.get("payment_id"), payment_id)) { return error_ret_json_from_message("Invalid 'payment_id'"); } crypto::public_key public_key; if (!epee::string_tools::hex_to_pod(json_root.get("public_key"), public_key)) { return error_ret_json_from_message("Invalid 'public_key'"); } crypto::secret_key secret_key; if (!epee::string_tools::hex_to_pod(json_root.get("secret_key"), secret_key)) { return error_ret_json_from_message("Invalid 'secret_key'"); } hw::device &hwdev = hw::get_device("default"); hwdev.encrypt_payment_id(payment_id, public_key, secret_key); boost::property_tree::ptree root; root.put(ret_json_key__generic_retVal(), epee::string_tools::pod_to_hex(payment_id)); return ret_json_from_root(root); }