parent
41fbc55521
commit
399d3fadf1
@ -0,0 +1,525 @@
|
||||
//
|
||||
// monero_send_routine.cpp
|
||||
// Copyright © 2018 MyMonero. All rights reserved.
|
||||
//
|
||||
// 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 <boost/property_tree/json_parser.hpp>
|
||||
#include "wallet_errors.h"
|
||||
#include "string_tools.h"
|
||||
//
|
||||
#include "monero_send_routine.hpp"
|
||||
//
|
||||
#include "monero_transfer_utils.hpp"
|
||||
#include "monero_fork_rules.hpp"
|
||||
#include "monero_key_image_utils.hpp"
|
||||
#include "monero_address_utils.hpp"
|
||||
//
|
||||
using namespace std;
|
||||
using namespace crypto;
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
using namespace tools; // for error::
|
||||
using namespace monero_transfer_utils;
|
||||
using namespace monero_fork_rules;
|
||||
using namespace monero_key_image_utils; // for API response parsing
|
||||
using namespace monero_send_routine;
|
||||
//
|
||||
//
|
||||
optional<uint64_t> _possible_uint64_from_json(
|
||||
const boost::property_tree::ptree &res,
|
||||
const string &fieldname
|
||||
) { // throws
|
||||
auto optl_str = res.get_optional<string>(fieldname);
|
||||
// cout << fieldname << ": " << optl_str << endl;
|
||||
if (optl_str != none) {
|
||||
return stoull(*optl_str);
|
||||
}
|
||||
auto optl_uint32 = res.get_optional<uint32_t>(fieldname); // not uint64 b/c JSON can't store such values
|
||||
if (optl_uint32 != none) {
|
||||
return (uint64_t)*optl_uint32; // cast
|
||||
}
|
||||
return none;
|
||||
}
|
||||
//
|
||||
LightwalletAPI_Req_GetUnspentOuts monero_send_routine::new__req_params__get_unspent_outs(
|
||||
const string &from_address_string,
|
||||
const string &sec_viewKey_string
|
||||
) {
|
||||
stringstream dustT_ss;
|
||||
dustT_ss << dust_threshold();
|
||||
return {
|
||||
from_address_string,
|
||||
sec_viewKey_string,
|
||||
"0", // amount - always sent as "0"
|
||||
fixed_mixinsize(),
|
||||
true, // use dust
|
||||
dustT_ss.str()
|
||||
};
|
||||
}
|
||||
LightwalletAPI_Req_GetRandomOuts monero_send_routine::new__req_params__get_random_outs(
|
||||
vector<SpendableOutput> &step1__using_outs
|
||||
) {
|
||||
vector<string> decoy_req__amounts;
|
||||
BOOST_FOREACH(SpendableOutput &using_out, step1__using_outs)
|
||||
{
|
||||
if (using_out.rct != none && (*(using_out.rct)).size() > 0) {
|
||||
decoy_req__amounts.push_back("0");
|
||||
} else {
|
||||
stringstream amount_ss;
|
||||
amount_ss << using_out.amount;
|
||||
decoy_req__amounts.push_back(amount_ss.str());
|
||||
}
|
||||
}
|
||||
return LightwalletAPI_Req_GetRandomOuts{
|
||||
decoy_req__amounts,
|
||||
fixed_mixinsize() + 1 // count; Add one to mixin so we can skip real output key if necessary
|
||||
};
|
||||
}
|
||||
//
|
||||
LightwalletAPI_Res_GetUnspentOuts monero_send_routine::new__parsed_res__get_unspent_outs(
|
||||
property_tree::ptree &res,
|
||||
const secret_key &sec_viewKey,
|
||||
const secret_key &sec_spendKey,
|
||||
const public_key &pub_spendKey
|
||||
) {
|
||||
uint64_t final__per_byte_fee = 0;
|
||||
try {
|
||||
optional<uint64_t> possible__uint64 = _possible_uint64_from_json(res, "per_byte_fee");
|
||||
if (possible__uint64 != none) {
|
||||
final__per_byte_fee = *possible__uint64;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
cout << "Unspent outs per-byte-fee parse error: " << e.what() << endl;
|
||||
string err_msg = "Unspent outs: Unrecognized per-byte fee format";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
if (final__per_byte_fee == 0) {
|
||||
try {
|
||||
optional<uint64_t> possible__uint64 = _possible_uint64_from_json(res, "per_kb_fee");
|
||||
if (possible__uint64 != none) {
|
||||
final__per_byte_fee = (*possible__uint64) / 1024; // scale from kib to b
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
cout << "Unspent outs per-kb-fee parse error: " << e.what() << endl;
|
||||
string err_msg = "Unspent outs: Unrecognized per-kb fee format";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
}
|
||||
if (final__per_byte_fee == 0) {
|
||||
string err_msg = "Unable to get a per-byte fee from server response.";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
vector<SpendableOutput> unspent_outs;
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &output_desc, res.get_child("outputs"))
|
||||
{
|
||||
assert(output_desc.first.empty()); // array elements have no names
|
||||
//
|
||||
auto optl__tx_pub_key = output_desc.second.get_optional<string>("tx_pub_key");
|
||||
if (optl__tx_pub_key == none) { // TODO: do we ever actually expect these not to exist?
|
||||
cout << "Warn: This unspent out was missing a tx_pub_key. Skipping." << endl;
|
||||
continue; // skip
|
||||
}
|
||||
crypto::public_key tx_pub_key{};
|
||||
{
|
||||
bool r = epee::string_tools::hex_to_pod(*optl__tx_pub_key, tx_pub_key);
|
||||
if (!r) {
|
||||
string err_msg = "Invalid tx pub key";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
}
|
||||
uint64_t output__index;
|
||||
try {
|
||||
optional<uint64_t> possible__uint64 = _possible_uint64_from_json(output_desc.second, "index");
|
||||
if (possible__uint64 != none) {
|
||||
output__index = *possible__uint64; // expecting this to exist
|
||||
} else { // bail
|
||||
string err_msg = "Expected unspent output to have an \"index\"";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
cout << "Unspent outs output index parse error: " << e.what() << endl;
|
||||
string err_msg = "Unspent outs: Unrecognized output index format";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
bool isOutputSpent = false; // let's see…
|
||||
{
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &spend_key_image_string, output_desc.second.get_child("spend_key_images"))
|
||||
{
|
||||
// cout << "spend_key_image_string: " << spend_key_image_string.second.data() << endl;
|
||||
KeyImageRetVals retVals;
|
||||
bool r = new__key_image(
|
||||
pub_spendKey, sec_spendKey, sec_viewKey, tx_pub_key,
|
||||
output__index,
|
||||
retVals
|
||||
);
|
||||
if (!r) {
|
||||
string err_msg = "Unable to generate key image";
|
||||
return {
|
||||
err_msg,
|
||||
none, none
|
||||
};
|
||||
}
|
||||
auto calculated_key_image_string = epee::string_tools::pod_to_hex(retVals.calculated_key_image);
|
||||
// cout << "calculated_key_image_string: " << calculated_key_image_string << endl;
|
||||
auto areEqual = calculated_key_image_string == spend_key_image_string.second.data();
|
||||
if (areEqual) {
|
||||
isOutputSpent = true; // output was spent… exclude
|
||||
break; // exit spend key img loop to evaluate isOutputSpent
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isOutputSpent == false) {
|
||||
SpendableOutput out{};
|
||||
out.amount = stoull(output_desc.second.get<string>("amount"));
|
||||
out.public_key = output_desc.second.get<string>("public_key");
|
||||
out.rct = output_desc.second.get_optional<string>("rct");
|
||||
out.global_index = stoull(output_desc.second.get<string>("global_index"));
|
||||
out.index = output__index;
|
||||
out.tx_pub_key = *optl__tx_pub_key; // just b/c we've already accessed it above
|
||||
//
|
||||
unspent_outs.push_back(out);
|
||||
}
|
||||
}
|
||||
return LightwalletAPI_Res_GetUnspentOuts{
|
||||
none,
|
||||
final__per_byte_fee, unspent_outs
|
||||
};
|
||||
}
|
||||
LightwalletAPI_Res_GetRandomOuts monero_send_routine::new__parsed_res__get_random_outs(
|
||||
property_tree::ptree &res
|
||||
) {
|
||||
vector<RandomAmountOutputs> mix_outs;
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &mix_out_desc, res.get_child("amount_outs"))
|
||||
{
|
||||
assert(mix_out_desc.first.empty()); // array elements have no names
|
||||
auto amountAndOuts = RandomAmountOutputs{};
|
||||
try {
|
||||
optional<uint64_t> possible__uint64 = _possible_uint64_from_json(mix_out_desc.second, "amount");
|
||||
if (possible__uint64 != none) {
|
||||
amountAndOuts.amount = *possible__uint64;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
cout << "Random outs response 'amount' parse error: " << e.what() << endl;
|
||||
string err_msg = "Random outs: Unrecognized 'amount' format";
|
||||
return {err_msg, none};
|
||||
}
|
||||
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{};
|
||||
try {
|
||||
optional<uint64_t> possible__uint64 = _possible_uint64_from_json(mix_out_output_desc.second, "global_index");
|
||||
if (possible__uint64 != none) {
|
||||
amountOutput.global_index = *possible__uint64;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
cout << "Random outs response 'global_index' parse error: " << e.what() << endl;
|
||||
string err_msg = "Random outs: Unrecognized 'global_index' format";
|
||||
return {err_msg, none};
|
||||
}
|
||||
amountOutput.public_key = mix_out_output_desc.second.get<string>("public_key");
|
||||
amountOutput.rct = mix_out_output_desc.second.get_optional<string>("rct");
|
||||
//
|
||||
amountAndOuts.outputs.push_back(amountOutput);
|
||||
}
|
||||
mix_outs.push_back(amountAndOuts);
|
||||
}
|
||||
return {
|
||||
none, mix_outs
|
||||
};
|
||||
}
|
||||
//
|
||||
struct _SendFunds_ConstructAndSendTx_Args
|
||||
{
|
||||
const string &from_address_string;
|
||||
const string &sec_viewKey_string;
|
||||
const string &sec_spendKey_string;
|
||||
const string &to_address_string;
|
||||
optional<string> payment_id_string;
|
||||
uint64_t sending_amount;
|
||||
bool is_sweeping;
|
||||
uint32_t simple_priority;
|
||||
const send__get_random_outs_fn_type &get_random_outs_fn;
|
||||
const send__submit_raw_tx_fn_type &submit_raw_tx_fn;
|
||||
const send__status_update_fn_type &status_update_fn;
|
||||
const send__error_cb_fn_type &error_cb_fn;
|
||||
const send__success_cb_fn_type &success_cb_fn;
|
||||
uint64_t unlock_time;
|
||||
cryptonote::network_type nettype;
|
||||
//
|
||||
const vector<SpendableOutput> &unspent_outs;
|
||||
uint64_t fee_per_b;
|
||||
//
|
||||
// cached
|
||||
const secret_key &sec_viewKey;
|
||||
const secret_key &sec_spendKey;
|
||||
//
|
||||
optional<uint64_t> passedIn_attemptAt_fee;
|
||||
size_t constructionAttempt;
|
||||
};
|
||||
void _reenterable_construct_and_send_tx(
|
||||
const _SendFunds_ConstructAndSendTx_Args &args,
|
||||
//
|
||||
// re-entry params
|
||||
optional<uint64_t> passedIn_attemptAt_fee = none,
|
||||
size_t constructionAttempt = 0
|
||||
) {
|
||||
args.status_update_fn(calculatingFee);
|
||||
//
|
||||
Send_Step1_RetVals step1_retVals;
|
||||
monero_transfer_utils::send_step1__prepare_params_for_get_decoys(
|
||||
step1_retVals,
|
||||
//
|
||||
args.payment_id_string,
|
||||
args.sending_amount,
|
||||
args.is_sweeping,
|
||||
args.simple_priority,
|
||||
[] (uint8_t version, int64_t early_blocks) -> bool
|
||||
{
|
||||
return lightwallet_hardcoded__use_fork_rules(version, early_blocks);
|
||||
},
|
||||
args.unspent_outs,
|
||||
args.fee_per_b,
|
||||
//
|
||||
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
|
||||
);
|
||||
if (step1_retVals.errCode != noError) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.errCode = step1_retVals.errCode;
|
||||
error_retVals.spendable_balance = step1_retVals.spendable_balance;
|
||||
error_retVals.required_balance = step1_retVals.required_balance;
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
api_fetch_cb_fn get_random_outs_fn__cb_fn = [
|
||||
args,
|
||||
step1_retVals,
|
||||
constructionAttempt
|
||||
] (
|
||||
property_tree::ptree res
|
||||
) -> void {
|
||||
auto parsed_res = new__parsed_res__get_random_outs(res);
|
||||
if (parsed_res.err_msg != none) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = std::move(*(parsed_res.err_msg));
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
Send_Step2_RetVals step2_retVals;
|
||||
monero_transfer_utils::send_step2__try_create_transaction(
|
||||
step2_retVals,
|
||||
//
|
||||
args.from_address_string,
|
||||
args.sec_viewKey_string,
|
||||
args.sec_spendKey_string,
|
||||
args.to_address_string,
|
||||
args.payment_id_string,
|
||||
step1_retVals.final_total_wo_fee,
|
||||
step1_retVals.change_amount,
|
||||
step1_retVals.using_fee,
|
||||
args.simple_priority,
|
||||
step1_retVals.using_outs,
|
||||
args.fee_per_b,
|
||||
*(parsed_res.mix_outs),
|
||||
[] (uint8_t version, int64_t early_blocks) -> bool
|
||||
{
|
||||
return lightwallet_hardcoded__use_fork_rules(version, early_blocks);
|
||||
},
|
||||
args.unlock_time,
|
||||
args.nettype
|
||||
);
|
||||
if (step2_retVals.errCode != noError) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.errCode = step2_retVals.errCode;
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
if (step2_retVals.tx_must_be_reconstructed) {
|
||||
// this will update status back to .calculatingFee
|
||||
if (constructionAttempt > 15) { // just going to avoid an infinite loop here or particularly long stack
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = "Unable to construct a transaction with sufficient fee for unknown reason.";
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
// cout << "step2_retVals.fee_actually_needed: " << step2_retVals.fee_actually_needed << endl;
|
||||
_reenterable_construct_and_send_tx(
|
||||
args,
|
||||
//
|
||||
step2_retVals.fee_actually_needed, // -> reconstruction attempt's step1's passedIn_attemptAt_fee
|
||||
constructionAttempt+1
|
||||
);
|
||||
return;
|
||||
}
|
||||
args.status_update_fn(submittingTransaction);
|
||||
//
|
||||
api_fetch_cb_fn submit_raw_tx_fn__cb_fn = [
|
||||
args,
|
||||
step1_retVals,
|
||||
step2_retVals
|
||||
] (
|
||||
property_tree::ptree res
|
||||
) -> void {
|
||||
// not actually expecting anything in a success response, so no need to parse
|
||||
SendFunds_Success_RetVals success_retVals;
|
||||
success_retVals.used_fee = step1_retVals.using_fee; // NOTE: not the same thing as step2_retVals.fee_actually_needed
|
||||
success_retVals.total_sent = step1_retVals.final_total_wo_fee + step1_retVals.using_fee;
|
||||
success_retVals.mixin = step1_retVals.mixin;
|
||||
{
|
||||
optional<string> returning__payment_id = args.payment_id_string; // separated from submit_raw_tx_fn so that it can be captured w/o capturing all of args (FIXME: does this matter?)
|
||||
if (returning__payment_id == none) {
|
||||
auto decoded = monero::address_utils::decodedAddress(args.to_address_string, args.nettype);
|
||||
if (decoded.did_error) { // would be very strange...
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = *(decoded.err_string);
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
if (decoded.paymentID_string != none) {
|
||||
returning__payment_id = std::move(*(decoded.paymentID_string)); // just preserving this as an original return value - this can probably eventually be removed
|
||||
}
|
||||
}
|
||||
success_retVals.final_payment_id = returning__payment_id;
|
||||
}
|
||||
success_retVals.signed_serialized_tx_string = std::move(*(step2_retVals.signed_serialized_tx_string));
|
||||
success_retVals.tx_hash_string = std::move(*(step2_retVals.tx_hash_string));
|
||||
success_retVals.tx_key_string = std::move(*(step2_retVals.tx_key_string));
|
||||
success_retVals.tx_pub_key_string = std::move(*(step2_retVals.tx_pub_key_string));
|
||||
//
|
||||
args.success_cb_fn(success_retVals);
|
||||
};
|
||||
args.submit_raw_tx_fn(LightwalletAPI_Req_SubmitRawTx{
|
||||
args.from_address_string,
|
||||
args.sec_viewKey_string,
|
||||
*(step2_retVals.signed_serialized_tx_string)
|
||||
}, submit_raw_tx_fn__cb_fn);
|
||||
};
|
||||
//
|
||||
args.status_update_fn(fetchingDecoyOutputs);
|
||||
//
|
||||
args.get_random_outs_fn(
|
||||
new__req_params__get_random_outs(step1_retVals.using_outs),
|
||||
get_random_outs_fn__cb_fn
|
||||
);
|
||||
}
|
||||
//
|
||||
//
|
||||
// Entrypoint
|
||||
void monero_send_routine::async__send_funds(const Async_SendFunds_Args &args)
|
||||
{
|
||||
uint64_t usable__sending_amount = args.is_sweeping ? 0 : args.sending_amount;
|
||||
crypto::secret_key sec_viewKey{};
|
||||
crypto::secret_key sec_spendKey{};
|
||||
crypto::public_key pub_spendKey{};
|
||||
{
|
||||
bool r = false;
|
||||
r = epee::string_tools::hex_to_pod(args.sec_viewKey_string, sec_viewKey);
|
||||
if (!r) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = "Invalid secret view key";
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
r = epee::string_tools::hex_to_pod(args.sec_spendKey_string, sec_spendKey);
|
||||
if (!r) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = "Invalid sec spend key";
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
r = epee::string_tools::hex_to_pod(args.pub_spendKey_string, pub_spendKey);
|
||||
if (!r) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = "Invalid public spend key";
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
}
|
||||
api_fetch_cb_fn get_unspent_outs_fn__cb_fn = [
|
||||
args,
|
||||
usable__sending_amount,
|
||||
sec_viewKey, sec_spendKey, pub_spendKey
|
||||
] (
|
||||
property_tree::ptree res
|
||||
) -> void {
|
||||
auto parsed_res = new__parsed_res__get_unspent_outs(
|
||||
res,
|
||||
sec_viewKey, sec_spendKey, pub_spendKey
|
||||
);
|
||||
if (parsed_res.err_msg != none) {
|
||||
SendFunds_Error_RetVals error_retVals;
|
||||
error_retVals.explicit_errMsg = std::move(*(parsed_res.err_msg));
|
||||
args.error_cb_fn(error_retVals);
|
||||
return;
|
||||
}
|
||||
_reenterable_construct_and_send_tx(_SendFunds_ConstructAndSendTx_Args{
|
||||
args.from_address_string, args.sec_viewKey_string, args.sec_spendKey_string,
|
||||
args.to_address_string, args.payment_id_string, usable__sending_amount, args.is_sweeping, args.simple_priority,
|
||||
args.get_random_outs_fn, args.submit_raw_tx_fn, args.status_update_fn, args.error_cb_fn, args.success_cb_fn,
|
||||
args.unlock_time == none ? 0 : *(args.unlock_time),
|
||||
args.nettype == none ? MAINNET : *(args.nettype),
|
||||
//
|
||||
*(parsed_res.unspent_outs),
|
||||
*(parsed_res.per_byte_fee),
|
||||
//
|
||||
sec_viewKey, sec_spendKey
|
||||
});
|
||||
};
|
||||
args.status_update_fn(fetchingLatestBalance);
|
||||
//
|
||||
args.get_unspent_outs_fn(
|
||||
new__req_params__get_unspent_outs(
|
||||
args.from_address_string,
|
||||
args.sec_viewKey_string
|
||||
),
|
||||
get_unspent_outs_fn__cb_fn
|
||||
);
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
//
|
||||
// serial_bridge_utils.cpp
|
||||
// Copyright (c) 2014-2018, 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_utils.hpp"
|
||||
//
|
||||
#include <boost/foreach.hpp>
|
||||
//
|
||||
#include "wallet_errors.h"
|
||||
using namespace tools;
|
||||
#include "string_tools.h"
|
||||
//
|
||||
//
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace cryptonote;
|
||||
//
|
||||
using namespace serial_bridge_utils;
|
||||
//
|
||||
// TODO: factor these into a monero_bridge_utils and share with serial_bridge_utils (incl keys declarations there)
|
||||
network_type serial_bridge_utils::nettype_from_string(const string &nettype_string)
|
||||
{ // TODO: possibly move this to network_type declaration
|
||||
if (nettype_string == "MAINNET") {
|
||||
return MAINNET;
|
||||
} else if (nettype_string == "TESTNET") {
|
||||
return TESTNET;
|
||||
} else if (nettype_string == "STAGENET") {
|
||||
return STAGENET;
|
||||
} else if (nettype_string == "FAKECHAIN") {
|
||||
return FAKECHAIN;
|
||||
} else if (nettype_string == "UNDEFINED") {
|
||||
return UNDEFINED;
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(false, error::wallet_internal_error, "Unrecognized nettype_string")
|
||||
return UNDEFINED;
|
||||
}
|
||||
string serial_bridge_utils::string_from_nettype(network_type nettype)
|
||||
{
|
||||
switch (nettype) {
|
||||
case MAINNET:
|
||||
return "MAINNET";
|
||||
case TESTNET:
|
||||
return "TESTNET";
|
||||
case STAGENET:
|
||||
return "STAGENET";
|
||||
case FAKECHAIN:
|
||||
return "FAKECHAIN";
|
||||
case UNDEFINED:
|
||||
return "UNDEFINED";
|
||||
default:
|
||||
THROW_WALLET_EXCEPTION_IF(false, error::wallet_internal_error, "Unrecognized nettype for string conversion")
|
||||
return "UNDEFINED";
|
||||
}
|
||||
}
|
||||
//
|
||||
// Shared - Parsing - Args
|
||||
bool serial_bridge_utils::parsed_json_root(const string &args_string, boost::property_tree::ptree &json_root)
|
||||
{
|
||||
// cout << "args_string: " << args_string << endl;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << args_string;
|
||||
try {
|
||||
boost::property_tree::read_json(ss, json_root);
|
||||
} catch (std::exception const& e) {
|
||||
THROW_WALLET_EXCEPTION_IF(false, error::wallet_internal_error, "Invalid JSON");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// Shared - Factories - Return values
|
||||
string serial_bridge_utils::ret_json_from_root(const boost::property_tree::ptree &root)
|
||||
{
|
||||
stringstream ret_ss;
|
||||
boost::property_tree::write_json(ret_ss, root, false/*pretty*/);
|
||||
//
|
||||
return ret_ss.str();
|
||||
}
|
||||
string serial_bridge_utils::error_ret_json_from_message(const string &err_msg)
|
||||
{
|
||||
boost::property_tree::ptree root;
|
||||
root.put(ret_json_key__any__err_msg(), err_msg);
|
||||
//
|
||||
return ret_json_from_root(root);
|
||||
}
|
||||
string serial_bridge_utils::error_ret_json_from_code(int code, optional<string> err_msg)
|
||||
{
|
||||
boost::property_tree::ptree root;
|
||||
root.put(ret_json_key__any__err_code(), code);
|
||||
if (err_msg != none) {
|
||||
root.put(ret_json_key__any__err_msg(), *err_msg);
|
||||
}
|
||||
//
|
||||
return ret_json_from_root(root);
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
//
|
||||
// serial_bridge_utils.hpp
|
||||
// Copyright (c) 2014-2018, 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.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef serial_bridge_utils_hpp
|
||||
#define serial_bridge_utils_hpp
|
||||
//
|
||||
#include <string>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
//
|
||||
#include "cryptonote_config.h"
|
||||
//
|
||||
namespace serial_bridge_utils
|
||||
{
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace cryptonote;
|
||||
//
|
||||
// JSON convenience fns
|
||||
bool parsed_json_root(const string &args_string, boost::property_tree::ptree &json_root);
|
||||
//
|
||||
// JSON values
|
||||
network_type nettype_from_string(const string &nettype_string);
|
||||
string string_from_nettype(network_type nettype);
|
||||
//
|
||||
struct RetVals_Transforms
|
||||
{
|
||||
static string str_from(uint64_t v)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << v;
|
||||
return o.str();
|
||||
}
|
||||
static string str_from(uint32_t v)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << v;
|
||||
return o.str();
|
||||
}
|
||||
};
|
||||
string ret_json_from_root(const boost::property_tree::ptree &root);
|
||||
string error_ret_json_from_message(const string &err_msg);
|
||||
string error_ret_json_from_code(int code, optional<string> err_msg);
|
||||
//
|
||||
// JSON keys - Ret vals
|
||||
// - - Error
|
||||
static inline string ret_json_key__any__err_msg() { return "err_msg"; } // optional
|
||||
static inline string ret_json_key__any__err_code() { return "err_code"; } // optional
|
||||
//
|
||||
// - - Shared
|
||||
static inline string ret_json_key__generic_retVal() { return "retVal"; }
|
||||
// - - create_transaction / send
|
||||
static inline string ret_json_key__send__spendable_balance() { return "spendable_balance"; }
|
||||
static inline string ret_json_key__send__required_balance() { return "required_balance"; }
|
||||
static inline string ret_json_key__send__mixin() { return "mixin"; }
|
||||
static inline string ret_json_key__send__using_fee() { return "using_fee"; }
|
||||
static inline string ret_json_key__send__final_total_wo_fee() { return "final_total_wo_fee"; }
|
||||
static inline string ret_json_key__send__change_amount() { return "change_amount"; }
|
||||
static inline string ret_json_key__send__using_outs() { return "using_outs"; } // this list's members' keys should probably be declared (is this the best way to do this?)
|
||||
//
|
||||
static inline string ret_json_key__send__tx_must_be_reconstructed() { return "tx_must_be_reconstructed"; }
|
||||
static inline string ret_json_key__send__fee_actually_needed() { return "fee_actually_needed"; }
|
||||
//
|
||||
static inline string ret_json_key__send__serialized_signed_tx() { return "serialized_signed_tx"; }
|
||||
static inline string ret_json_key__send__tx_hash() { return "tx_hash"; }
|
||||
static inline string ret_json_key__send__tx_key() { return "tx_key"; }
|
||||
static inline string ret_json_key__send__tx_pub_key() { return "tx_pub_key"; }
|
||||
//
|
||||
static inline string ret_json_key__send__used_fee() { return "used_fee"; }
|
||||
static inline string ret_json_key__send__total_sent() { return "total_sent"; }
|
||||
static inline string ret_json_key__send__final_payment_id() { return "final_payment_id"; }
|
||||
//
|
||||
// - - decode_address, etc
|
||||
static inline string ret_json_key__paymentID_string() { return "paymentID_string"; } // optional
|
||||
static inline string ret_json_key__isSubaddress() { return "isSubaddress"; }
|
||||
static inline string ret_json_key__mnemonic_string() { return "mnemonic_string"; }
|
||||
static inline string ret_json_key__mnemonic_language() { return "mnemonic_language"; }
|
||||
static inline string ret_json_key__sec_seed_string() { return "sec_seed_string"; }
|
||||
static inline string ret_json_key__address_string() { return "address_string"; }
|
||||
static inline string ret_json_key__pub_viewKey_string() { return "pub_viewKey_string"; }
|
||||
static inline string ret_json_key__pub_spendKey_string() { return "pub_spendKey_string"; }
|
||||
static inline string ret_json_key__sec_viewKey_string() { return "sec_viewKey_string"; }
|
||||
static inline string ret_json_key__sec_spendKey_string() { return "sec_spendKey_string"; }
|
||||
static inline string ret_json_key__isValid() { return "isValid"; }
|
||||
static inline string ret_json_key__isInViewOnlyMode() { return "isInViewOnlyMode"; }
|
||||
static inline string ret_json_key__decodeRct_mask() { return "mask"; }
|
||||
static inline string ret_json_key__decodeRct_amount() { return "amount"; }
|
||||
// JSON keys - Args
|
||||
// TODO: (is there a better way of doing this?) structs with auto parse & serialization?
|
||||
// static inline string args_json_key__
|
||||
}
|
||||
|
||||
#endif /* serial_bridge_utils_hpp */
|
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue