added serial_bridge_index with JSON-bridged create tx, with new convenience create_transaction fn for extra construction etc, plus test

pull/29/head
Paul Shapiro 6 years ago
parent b896c29add
commit 2ea3a47d85

@ -79,6 +79,8 @@ set(
src/monero_fork_rules.cpp
src/monero_wallet_utils.hpp
src/monero_wallet_utils.cpp
src/serial_bridge_index.hpp
src/serial_bridge_index.cpp
src/tools__ret_vals.hpp
src/tools__ret_vals.cpp
#

@ -50,7 +50,7 @@ using namespace monero_fork_rules;
// Protocol / Defaults
uint32_t monero_transfer_utils::fixed_ringsize()
{
return 7; // best practice is to conform to fixed ring size
return 7; // best practice is to conform to fixed default ring size
}
uint32_t monero_transfer_utils::fixed_mixinsize()
{
@ -314,10 +314,9 @@ void monero_transfer_utils::create_transaction(
bool rct,
cryptonote::network_type nettype
) {
retVals.errCode = noError; // (does this need to be initialized?)
retVals.errCode = noError;
//
// TODO: sort destinations by amount, here, according to 'decompose_destinations'?
// TODO: do we need to sort destinations by amount, here, according to 'decompose_destinations'?
//
uint32_t fake_outputs_count = fixed_mixinsize();
bool bulletproof = use_fork_rules_fn(get_bulletproof_fork(), 0);
@ -545,3 +544,122 @@ void monero_transfer_utils::create_transaction(
//
retVals.tx = tx;
}
//
//
void monero_transfer_utils::convenience__create_transaction(
Convenience_TransactionConstruction_RetVals &retVals,
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 amount, // to send
uint64_t fee_amount,
const std::vector<cryptonote::tx_destination_entry> &dsts, // this must include change or dummy address
vector<SpendableOutput> &outputs,
vector<RandomAmountOutputs> &mix_outs,
use_fork_rules_fn_type use_fork_rules_fn,
uint64_t unlock_time,
network_type nettype
) {
retVals.errCode = noError;
//
cryptonote::address_parse_info from_addr_info;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_account_address_from_str(from_addr_info, nettype, from_address_string), error::wallet_internal_error, "Couldn't parse from-address");
cryptonote::account_keys account_keys;
{
account_keys.m_account_address = from_addr_info.address;
//
crypto::secret_key sec_viewKey;
THROW_WALLET_EXCEPTION_IF(!string_tools::hex_to_pod(sec_viewKey_string, sec_viewKey), error::wallet_internal_error, "Couldn't parse view key");
account_keys.m_view_secret_key = sec_viewKey;
//
crypto::secret_key sec_spendKey;
THROW_WALLET_EXCEPTION_IF(!string_tools::hex_to_pod(sec_spendKey_string, sec_spendKey), error::wallet_internal_error, "Couldn't parse spend key");
account_keys.m_spend_secret_key = sec_spendKey;
}
//
cryptonote::address_parse_info to_addr_info;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_account_address_from_str(to_addr_info, nettype, to_address_string), error::wallet_internal_error, "Couldn't parse to-address");
//
std::vector<uint8_t> extra;
bool payment_id_seen = false;
{ // Detect hash8 or hash32 char hex string as pid and configure 'extra' accordingly
bool r = false;
if (payment_id_string != none) {
crypto::hash payment_id;
r = monero_paymentID_utils::parse_long_payment_id(*payment_id_string, payment_id);
if (r) {
std::string extra_nonce;
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
r = cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce);
} else {
crypto::hash8 payment_id8;
r = monero_paymentID_utils::parse_short_payment_id(*payment_id_string, payment_id8);
if (r) {
std::string extra_nonce;
cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8);
r = cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce);
}
}
payment_id_seen = true;
} else {
if (to_addr_info.is_subaddress && payment_id_seen) {
retVals.errCode = cantUsePIDWithSubAddress; // Never use a subaddress with a payment ID
return;
}
if (to_addr_info.has_payment_id) {
if (payment_id_seen) {
retVals.errCode = nonZeroPIDWithIntAddress; // can't use int addr at same time as supplying manual pid
return;
}
if (to_addr_info.is_subaddress) {
THROW_WALLET_EXCEPTION_IF(false, error::wallet_internal_error, "Unexpected is_subaddress && has_payment_id"); // should never happen
return;
}
std::string extra_nonce;
cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, to_addr_info.payment_id);
bool r = cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce);
if (!r) {
retVals.errCode = couldntSetPIDToTXExtra;
return;
}
payment_id_seen = true;
}
}
}
//
uint32_t subaddr_account_idx = 0;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[account_keys.m_account_address.m_spend_public_key] = {0,0};
//
TransactionConstruction_RetVals actualCall_retVals;
create_transaction(
actualCall_retVals,
account_keys,
subaddr_account_idx,
subaddresses,
dsts,
outputs,
mix_outs,
fee_amount,
extra,
use_fork_rules_fn,
unlock_time,
true, // rct
nettype
);
if (actualCall_retVals.errCode != noError) {
retVals.errCode = actualCall_retVals.errCode; // pass-through
return; // already set the error
}
auto txBlob = t_serializable_object_to_blob(actualCall_retVals.tx);
size_t txBlob_byteLength = txBlob.size();
// cout << "txBlob: " << txBlob << endl;
cout << "txBlob_byteLength: " << txBlob_byteLength << endl;
THROW_WALLET_EXCEPTION_IF(txBlob_byteLength <= 0, error::wallet_internal_error, "Expected tx blob byte length > 0");
//
// tx hash
retVals.tx_hash_string = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(actualCall_retVals.tx));
retVals.signed_serialized_tx_string = epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(actualCall_retVals.tx));
}

@ -126,8 +126,52 @@ namespace monero_transfer_utils
givenAnInvalidPubKey = 14,
invalidCommitOrMaskOnOutputRCT = 15,
transactionNotConstructed = 16,
transactionTooBig = 17
transactionTooBig = 17,
notYetImplemented = 18
};
static inline string err_msg_from_err_code__create_transaction(CreateTransactionErrorCode code)
{
switch (code) {
case noError:
return "No error";
case noDestinations:
return "No destinations provided";
case wrongNumberOfMixOutsProvided:
return "Wrong number of mix outputs provided";
case notEnoughOutputsForMixing:
return "Not enough outputs for mixing";
case invalidSecretKeys:
return "Invalid secret keys";
case outputAmountOverflow:
return "Output amount overflow";
case inputAmountOverflow:
return "Input amount overflow";
case mixRCTOutsMissingCommit:
return "Mix RCT outs missing commit";
case resultFeeNotEqualToGiven:
return "Result fee not equal to given fee";
case needMoreMoneyThanFound:
return "Need more money than found";
case invalidDestinationAddress:
return "Invalid destination address";
case nonZeroPIDWithIntAddress:
return "Can't supply a PID with an integrated address";
case cantUsePIDWithSubAddress:
return "Can't use PID with subaddress";
case couldntSetPIDToTXExtra:
return "Couldn't set PID to tx extra";
case givenAnInvalidPubKey:
return "Invalid pub key";
case invalidCommitOrMaskOnOutputRCT:
return "Invalid commit or mask on output rct";
case transactionNotConstructed:
return "Transaction not constructed";
case transactionTooBig:
return "Transaction too big";
case notYetImplemented:
return "Not yet implemented";
}
}
struct TransactionConstruction_RetVals
{
CreateTransactionErrorCode errCode;
@ -150,6 +194,30 @@ namespace monero_transfer_utils
bool rct = true,
network_type nettype = MAINNET
);
//
struct Convenience_TransactionConstruction_RetVals
{
CreateTransactionErrorCode errCode;
//
optional<string> signed_serialized_tx_string;
optional<string> tx_hash_string;
};
void convenience__create_transaction(
Convenience_TransactionConstruction_RetVals &retVals,
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 amount, // to send
uint64_t fee_amount,
const std::vector<cryptonote::tx_destination_entry> &dsts, // this must include change or else dummy address
vector<SpendableOutput> &outputs,
vector<RandomAmountOutputs> &mix_outs,
use_fork_rules_fn_type use_fork_rules_fn,
uint64_t unlock_time = 0, // or 0
network_type nettype = MAINNET
);
}
#endif /* monero_transfer_utils_hpp */

@ -0,0 +1,180 @@
//
// serial_bridge_index.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_index.hpp"
//
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
//
#include "monero_fork_rules.hpp"
#include "monero_transfer_utils.hpp"
#include "wallet_errors.h"
//
//
using namespace std;
using namespace boost;
using namespace cryptonote;
using namespace monero_transfer_utils;
using namespace monero_fork_rules;
//
using namespace serial_bridge;
//
network_type nettype_from_string(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 error_ret_json_from_message(string err_msg)
{
boost::property_tree::ptree root;
root.put(ret_json_key__any__err_msg(), err_msg);
stringstream ss;
boost::property_tree::write_json(ss, root);
//
return ss.str();
}
string error_ret_json_from_code(int code)
{
boost::property_tree::ptree root;
root.put(ret_json_key__any__err_code(), code);
stringstream ss;
boost::property_tree::write_json(ss, root);
//
return ss.str();
}
//
string serial_bridge::create_transaction(std::string args_string)
{
std::stringstream ss;
ss << args_string;
boost::property_tree::ptree json_root;
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 error_ret_json_from_message("Invalid JSON"); // TODO: centralize
}
network_type nettype = nettype_from_string(json_root.get<string>("nettype_string"));
//
std::vector<cryptonote::tx_destination_entry> dsts;
BOOST_FOREACH(boost::property_tree::ptree::value_type &dst_desc, json_root.get_child("dsts"))
{
assert(dst_desc.first.empty()); // array elements have no names
cryptonote::tx_destination_entry de;
cryptonote::address_parse_info de_addr_info;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_account_address_from_str(de_addr_info, nettype, dst_desc.second.get<string>("addr")), error::wallet_internal_error, "Invalid dsts.addr");
de.addr = de_addr_info.address;
de.is_subaddress = dst_desc.second.get<bool>("is_subaddress");
THROW_WALLET_EXCEPTION_IF(de.is_subaddress != de_addr_info.is_subaddress, error::wallet_internal_error, "Expected dsts.is_subaddress = parsed is_subaddress");
de.amount = stoull(dst_desc.second.get<string>("amount"));
//
dsts.push_back(de);
}
//
vector<SpendableOutput> outputs;
BOOST_FOREACH(boost::property_tree::ptree::value_type &output_desc, json_root.get_child("outputs"))
{
assert(output_desc.first.empty()); // array elements have no names
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<string>("rct");
out.global_index = stoull(output_desc.second.get<string>("global_index"));
out.index = stoull(output_desc.second.get<string>("index"));
out.tx_pub_key = output_desc.second.get<string>("tx_pub_key");
//
outputs.push_back(out);
}
//
vector<RandomAmountOutputs> 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<string>("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<string>("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<string>("public_key");
amountOutput.rct = mix_out_output_desc.second.get<string>("rct");
amountAndOuts.outputs.push_back(amountOutput);
}
mix_outs.push_back(amountAndOuts);
}
//
Convenience_TransactionConstruction_RetVals retVals;
monero_transfer_utils::convenience__create_transaction(
retVals,
json_root.get<string>("from_address_string"),
json_root.get<string>("sec_viewKey_string"),
json_root.get<string>("sec_spendKey_string"),
json_root.get<string>("to_address_string"),
json_root.get_optional<string>("payment_id_string"),
stoull(json_root.get<string>("amount")), // to send
stoull(json_root.get<string>("fee_amount")),
dsts,
outputs,
mix_outs,
[] (uint8_t version, int64_t early_blocks) -> bool
{
return lightwallet_hardcoded__use_fork_rules(version, early_blocks);
},
0, // unlock_time
nettype
);
if (retVals.errCode != noError) {
return error_ret_json_from_code(retVals.errCode);
}
THROW_WALLET_EXCEPTION_IF(retVals.signed_serialized_tx_string == boost::none, error::wallet_internal_error, "Not expecting no signed_serialized_tx_string given no error");
//
boost::property_tree::ptree root;
root.put(ret_json_key__create_transaction__serialized_signed_tx(), std::move(*(retVals.signed_serialized_tx_string)));
root.put(ret_json_key__create_transaction__tx_hash(), std::move(*(retVals.tx_hash_string)));
stringstream ret_ss;
boost::property_tree::write_json(ret_ss, root);
//
return ret_ss.str();
}

@ -0,0 +1,55 @@
//
// serial_bridge_index.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_index_hpp
#define serial_bridge_index_hpp
//
#include <string>
//
namespace serial_bridge
{
using namespace std;
//
// Bridging Functions - these take and return JSON strings
string create_transaction(string args_string);
//
// JSON keys - Ret vals
static inline string ret_json_key__any__err_msg() { return "err_msg"; }
static inline string ret_json_key__any__err_code() { return "err_code"; }
static inline string ret_json_key__create_transaction__serialized_signed_tx() { return "serialized_signed_tx"; }
static inline string ret_json_key__create_transaction__tx_hash() { return "tx_hash"; }
// JSON keys - Args
// TODO:
// static inline string args_json_key__
}
#endif /* serial_bridge_index_hpp */

@ -112,19 +112,19 @@ BOOST_AUTO_TEST_CASE(transfers__create)
{
using namespace monero_transfer_utils;
using namespace monero_fork_rules;
TransactionConstruction_RetVals retVals;
monero_fork_rules::use_fork_rules_fn_type use_fork_rules_fn = [] (uint8_t version, int64_t early_blocks) -> bool
{
return monero_fork_rules::lightwallet_hardcoded__use_fork_rules(version, early_blocks);
};
cryptonote::network_type nettype = cryptonote::MAINNET;
string from_addressString = "43zxvpcj5Xv9SEkNXbMCG7LPQStHMpFCQCmkmR4u5nzjWwq5Xkv5VmGgYEsHXg4ja2FGRD5wMWbBVMijDTqmmVqm93wHGkg";
string sec_viewKey_string = "......";
string sec_spendKey_string = "......";
string sec_viewKey_string = "...";
string sec_spendKey_string = "...";
cryptonote::address_parse_info from_addr_info;
BOOST_REQUIRE(cryptonote::get_account_address_from_str(from_addr_info, nettype, from_addressString));
cryptonote::account_keys account_keys;
@ -143,7 +143,6 @@ BOOST_AUTO_TEST_CASE(transfers__create)
cout << "spend pub key: " << string_tools::pod_to_hex(from_addr_info.address.m_spend_public_key) << endl;
//
optional<string> payment_id_string = string("b79f8efc81f58f67");
bool pid_encrypt = true;
uint64_t amount = 10000000000;
uint64_t fee_amount = 2167750000;
string to_address_string("43zxvpcj5Xv9SEkNXbMCG7LPQStHMpFCQCmkmR4u5nzjWwq5Xkv5VmGgYEsHXg4ja2FGRD5wMWbBVMijDTqmmVqm93wHGkg");
@ -192,7 +191,7 @@ BOOST_AUTO_TEST_CASE(transfers__create)
if (to_addr_info.is_subaddress) {
BOOST_REQUIRE(false); // should never happen .. logic err?
return;
}
}
std::string extra_nonce;
cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, to_addr_info.payment_id);
bool r = cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce);
@ -309,12 +308,146 @@ BOOST_AUTO_TEST_CASE(transfers__create)
// cout << "txBlob: " << txBlob << endl;
cout << "txBlob_byteLength: " << txBlob_byteLength << endl;
BOOST_REQUIRE(txBlob_byteLength > 0);
// tx hash
auto tx_hash_string = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(retVals.tx));
auto signed_serialized_tx_string = epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(retVals.tx));
cout << "tx_hash_string: " << tx_hash_string << endl;
cout << "signed_serialized_tx_string: " << signed_serialized_tx_string << endl;
}
//
//
// Serialization bridge
#include "../src/serial_bridge_index.hpp"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
BOOST_AUTO_TEST_CASE(bridged__transfers__create)
{
using namespace serial_bridge;
using namespace monero_transfer_utils;
//
string from_address_string = "43zxvpcj5Xv9SEkNXbMCG7LPQStHMpFCQCmkmR4u5nzjWwq5Xkv5VmGgYEsHXg4ja2FGRD5wMWbBVMijDTqmmVqm93wHGkg";
string to_address_string = "43zxvpcj5Xv9SEkNXbMCG7LPQStHMpFCQCmkmR4u5nzjWwq5Xkv5VmGgYEsHXg4ja2FGRD5wMWbBVMijDTqmmVqm93wHGkg";
string amount_string = "10000000000";
//
boost::property_tree::ptree root;
root.put("nettype_string", "MAINNET"); // TODO: specify this by constant and transform fn
root.put("from_address_string", from_address_string);
root.put("sec_viewKey_string", "...");
root.put("sec_spendKey_string", "...");
root.put("to_address_string", to_address_string);
root.put("payment_id_string", "b79f8efc81f58f67");
root.put("amount", amount_string);
root.put("fee_amount", "2167750000");
//
boost::property_tree::ptree dsts;
{ // 0. actual destination address
boost::property_tree::ptree dst;
dst.put("addr", to_address_string);
dst.put("amount", amount_string);
dst.put("is_subaddress", false);
dsts.push_back(std::make_pair("", dst));
}
{ // 1. change (otherwise we'd have to supply a dummy addr)
boost::property_tree::ptree dst;
dst.put("addr", from_address_string);
dst.put("amount", "112832250000");
dst.put("is_subaddress", false);
dsts.push_back(std::make_pair("", dst));
}
root.add_child("dsts", dsts);
//
boost::property_tree::ptree outputs;
{
boost::property_tree::ptree out;
out.put("amount", "125000000000");
out.put("public_key", "596fa47b6b3905269503435099a05e3ede54564026c93cbe5285e2df074c7118");
out.put("rct", "920ee8d99299f304d17fdb104720d1f62be0b03383c7bb466ff39c6a264d80d616ce1eccd6c4de1cc0fba87e463f2e0c373146c475e8a1517f36e7a37351d50034688cc8cb528c14188cae45d89b313d444e583c9d68a32cb80938a5e2aa200b");
out.put("global_index", "6451664");
out.put("index", "0");
out.put("tx_pub_key", "0a86e588dc67ca11993737e003a9e60c57174a663a47495e3b1d764f486fc88f");
outputs.push_back(std::make_pair("", out));
}
root.add_child("outputs", outputs);
//
boost::property_tree::ptree mix_outs;
{
boost::property_tree::ptree mix_out;
mix_out.put("amount", "0");
boost::property_tree::ptree mix_out_outputs;
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "5260585");
mix_out_output.put("public_key", "da77082624fce921891c4fb80a1e7076a6714ca8c9fc547311737926a0b85a46");
mix_out_output.put("rct", "bb227b27e36b7f3e695dffb641c29bb60bfd991accdb5ef4b580c9acd48c16b6");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "1929918");
mix_out_output.put("public_key", "8c983e7053d7a1dc9de8ac00468bcf11836a787d712dc0c02bd54a3ee00a55e8");
mix_out_output.put("rct", "8dec45867644d1a76aafe4487292d7cf401302e6bbbb99a61c2f3b6cef4f4f34");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "3921094");
mix_out_output.put("public_key", "0133219bd5e247eef51003921ec792784c41fc34289c703e9326d46f78d9b10a");
mix_out_output.put("rct", "75082f4ce31904acba4af37699c28d8d4f0f74fdf63b1e4a8069ebed50df3220");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "6627106");
mix_out_output.put("public_key", "daef1663dd1084bd7fe585c3d493480ee1c4cefb93254eac5855afdf38f662b1");
mix_out_output.put("rct", "1d96763c5bc3300090c286705b7d544f02c185d9be8c32baac6bbfb8e0d0d283");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "3308654");
mix_out_output.put("public_key", "ae135f58762b1133667002538f8c353a1869db815aa686e2544b5243c2d2212f");
mix_out_output.put("rct", "15046b93bb181189f2917eed38173202fbbb9cdbfcf3d1bc3e432df999ae1b1c");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "1972531");
mix_out_output.put("public_key", "39e44fa88d684d71762c40eb64ac80ddc694b74a99ac445667bf433536c09c8f");
mix_out_output.put("rct", "66a42d0e8123768b392ad4a230759258d9156fab1aea00a19b041832326aca0a");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
{
boost::property_tree::ptree mix_out_output;
mix_out_output.put("global_index", "3274424");
mix_out_output.put("public_key", "a89b91648645ba6f32e214ba5720f5387376e5a144e698d5d5d1ebac971de349");
mix_out_output.put("rct", "815a6b1da6fc6a3bd791c4342782381cf948ee822ac9da7149f1b3717e0266d2");
mix_out_outputs.push_back(std::make_pair("", mix_out_output));
}
mix_out.add_child("outputs", mix_out_outputs);
mix_outs.push_back(std::make_pair("", mix_out));
}
root.add_child("mix_outs", mix_outs);
//
stringstream args_ss;
boost::property_tree::write_json(args_ss, root);
auto ret_string = serial_bridge::create_transaction(args_ss.str());
stringstream ret_stream;
ret_stream << ret_string;
boost::property_tree::ptree ret_tree;
boost::property_tree::read_json(ret_stream, ret_tree);
optional<uint32_t> err_code = ret_tree.get_optional<uint32_t>(ret_json_key__any__err_code());
if (err_code != none && (CreateTransactionErrorCode)*err_code != monero_transfer_utils::noError) {
auto err_msg = err_msg_from_err_code__create_transaction((CreateTransactionErrorCode)*err_code);
BOOST_REQUIRE_MESSAGE(false, err_msg);
}
optional<string> tx_hash = ret_tree.get_optional<string>(ret_json_key__create_transaction__tx_hash());
optional<string> serialized_signed_tx = ret_tree.get_optional<string>(ret_json_key__create_transaction__serialized_signed_tx());
BOOST_REQUIRE(serialized_signed_tx != none);
BOOST_REQUIRE((*serialized_signed_tx).size() > 0);
cout << "bridged: serialized_signed_tx: " << *serialized_signed_tx << endl;
BOOST_REQUIRE(tx_hash != none);
BOOST_REQUIRE((*tx_hash).size() > 0);
cout << "bridged: tx_hash: " << *tx_hash << endl;
}

Loading…
Cancel
Save