diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blockchain_utilities/CMakeLists.txt | 22 | ||||
-rw-r--r-- | src/blockchain_utilities/cn_deserialize.cpp | 190 | ||||
-rw-r--r-- | src/crypto/skein.c | 4 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_format_utils.cpp | 2 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 8 | ||||
-rw-r--r-- | src/ringct/rctOps.cpp | 59 | ||||
-rw-r--r-- | src/ringct/rctOps.h | 24 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 144 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 4 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.cpp | 13 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 114 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 15 |
12 files changed, 494 insertions, 105 deletions
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index ccfd4a279..198f15ca3 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -58,6 +58,11 @@ monero_private_headers(blockchain_export ${blockchain_export_private_headers}) +set(cn_deserialize_sources + cn_deserialize.cpp + ) + + monero_add_executable(blockchain_import ${blockchain_import_sources} ${blockchain_import_private_headers}) @@ -104,3 +109,20 @@ add_dependencies(blockchain_export set_property(TARGET blockchain_export PROPERTY OUTPUT_NAME "monero-blockchain-export") + +monero_add_executable(cn_deserialize + ${cn_deserialize_sources} + ${cn_deserialize_private_headers}) + +target_link_libraries(cn_deserialize + LINK_PRIVATE + cryptonote_core + p2p + ${CMAKE_THREAD_LIBS_INIT}) + +add_dependencies(cn_deserialize + version) +set_property(TARGET cn_deserialize + PROPERTY + OUTPUT_NAME "cn_deserialize") + diff --git a/src/blockchain_utilities/cn_deserialize.cpp b/src/blockchain_utilities/cn_deserialize.cpp new file mode 100644 index 000000000..a8448dcee --- /dev/null +++ b/src/blockchain_utilities/cn_deserialize.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2014-2016, The Monero Project +// +// 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 "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/tx_extra.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_utilities.h" +#include "common/command_line.h" +#include "version.h" + +namespace po = boost::program_options; +using namespace epee; // log_space + +using namespace cryptonote; + +int main(int argc, char* argv[]) +{ + uint32_t log_level = 0; + std::string input; + + tools::sanitize_locale(); + + boost::filesystem::path output_file_path; + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true}; + const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level}; + const command_line::arg_descriptor<std::string> arg_input = {"input", "Specify input has a hexadecimal string", ""}; + + command_line::add_arg(desc_cmd_sett, arg_output_file); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_input); + + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + po::store(po::parse_command_line(argc, argv, desc_options), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + log_level = command_line::get_arg(vm, arg_log_level); + input = command_line::get_arg(vm, arg_input); + if (input.empty()) + { + std::cerr << "--input is mandatory" << std::endl; + return 1; + } + + log_space::get_set_log_detalisation_level(true, log_level); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + + std::string m_config_folder; + + std::ostream *output; + std::ofstream *raw_data_file = NULL; + if (command_line::has_arg(vm, arg_output_file)) + { + output_file_path = boost::filesystem::path(command_line::get_arg(vm, arg_output_file)); + + const boost::filesystem::path dir_path = output_file_path.parent_path(); + if (!dir_path.empty()) + { + if (boost::filesystem::exists(dir_path)) + { + if (!boost::filesystem::is_directory(dir_path)) + { + std::cerr << "output directory path is a file: " << dir_path << std::endl; + return 1; + } + } + else + { + if (!boost::filesystem::create_directory(dir_path)) + { + std::cerr << "Failed to create directory " << dir_path << std::endl; + return 1; + } + } + } + + raw_data_file = new std::ofstream(); + raw_data_file->open(output_file_path.string(), std::ios_base::out | std::ios::trunc); + if (raw_data_file->fail()) + return 1; + output = raw_data_file; + } + else + { + output_file_path = ""; + output = &std::cout; + } + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(input, blob)) + { + std::cerr << "Invalid hex input" << std::endl; + std::cerr << "Invalid hex input: " << input << std::endl; + return 1; + } + + cryptonote::block block; + cryptonote::transaction tx; + std::vector<cryptonote::tx_extra_field> fields; + if (cryptonote::parse_and_validate_block_from_blob(blob, block)) + { + std::cout << "Parsed block:" << std::endl; + std::cout << cryptonote::obj_to_json_str(block) << std::endl; + } + else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx)) + { + std::cout << "Parsed transaction:" << std::endl; + std::cout << cryptonote::obj_to_json_str(tx) << std::endl; + + if (cryptonote::parse_tx_extra(tx.extra, fields)) + { + std::cout << "tx_extra has " << fields.size() << " field(s)" << std::endl; + for (size_t n = 0; n < fields.size(); ++n) + { + std::cout << "field " << n << ": "; + if (typeid(cryptonote::tx_extra_padding) == fields[n].type()) std::cout << "extra padding: " << boost::get<cryptonote::tx_extra_padding>(fields[n]).size << " bytes"; + else if (typeid(cryptonote::tx_extra_pub_key) == fields[n].type()) std::cout << "extra pub key: " << boost::get<cryptonote::tx_extra_pub_key>(fields[n]).pub_key; + else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_nonce>(fields[n]).nonce); + else if (typeid(cryptonote::tx_extra_merge_mining_tag) == fields[n].type()) std::cout << "extra merge mining tag: depth " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).depth << ", merkle root " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).merkle_root; + else if (typeid(cryptonote::tx_extra_mysterious_minergate) == fields[n].type()) std::cout << "extra minergate custom: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_mysterious_minergate>(fields[n]).data); + else std::cout << "unknown"; + std::cout << std::endl; + } + } + else + { + std::cout << "Failed to parse tx_extra" << std::endl; + } + } + else + { + std::cerr << "Not a recognized CN type" << std::endl; + return 1; + } + + + + if (output->fail()) + return 1; + output->flush(); + if (raw_data_file) + delete raw_data_file; + + return 0; +} diff --git a/src/crypto/skein.c b/src/crypto/skein.c index 9c8ac288d..65e4525c3 100644 --- a/src/crypto/skein.c +++ b/src/crypto/skein.c @@ -77,7 +77,7 @@ typedef struct /* 1024-bit Skein hash context stru } Skein1024_Ctxt_t; /* Skein APIs for (incremental) "straight hashing" */ -#if SKEIN_256_NIST_MAX_HASH_BITS +#if SKEIN_256_NIST_MAX_HASHBITS static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); #endif static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); @@ -1941,7 +1941,7 @@ static HashReturn Final (hashState *state, BitSequence *hashval); /* select the context size and init the context */ static HashReturn Init(hashState *state, int hashbitlen) { -#if SKEIN_256_NIST_MAX_HASH_BITS +#if SKEIN_256_NIST_MAX_HASHBITS if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) { Skein_Assert(hashbitlen > 0,BAD_HASHLEN); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 870e8f0d8..6d64a43cb 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -509,7 +509,7 @@ namespace cryptonote std::string extra_nonce; set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_fields)); + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce)); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) { LOG_ERROR("Failed to add encrypted payment id to tx extra"); diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index d8d8aac99..7d50ae76b 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -859,10 +859,10 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { for (const auto &tx_info: res.transactions) { bytes += tx_info.blob_size; - if (min_bytes == 0 || bytes < min_bytes) - min_bytes = bytes; - if (bytes > max_bytes) - max_bytes = bytes; + if (min_bytes == 0 || tx_info.blob_size < min_bytes) + min_bytes = tx_info.blob_size; + if (tx_info.blob_size > max_bytes) + max_bytes = tx_info.blob_size; if (!tx_info.relayed) n_not_relayed++; fee += tx_info.fee; diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 239168388..cf55897a7 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -37,50 +37,12 @@ namespace rct { //Various key initialization functions - //Creates a zero scalar - void zero(key &zero) { - memset(&zero, 0, 32); - } - - //Creates a zero scalar - key zero() { - static const key z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; - return z; - } - - //Creates a zero elliptic curve point - void identity(key &Id) { - Id[0] = (unsigned char)(0x01); - memset(Id.bytes+1, 0, 31); - } - - //Creates a zero elliptic curve point - key identity() { - key Id; - Id[0] = (unsigned char)(0x01); - memset(Id.bytes+1, 0, 31); - return Id; - } - - //copies a scalar or point - void copy(key &AA, const key &A) { - memcpy(&AA, &A, 32); - } - - //copies a scalar or point - key copy(const key &A) { - key AA; - memcpy(&AA, &A, 32); - return AA; - } - - //initializes a key matrix; //first parameter is rows, //second is columns - keyM keyMInit(int rows, int cols) { + keyM keyMInit(size_t rows, size_t cols) { keyM rv(cols); - int i = 0; + size_t i = 0; for (i = 0 ; i < cols ; i++) { rv[i] = keyV(rows); } @@ -107,11 +69,12 @@ namespace rct { //Generates a vector of secret key //Mainly used in testing - keyV skvGen(int rows ) { + keyV skvGen(size_t rows ) { keyV rv(rows); - int i = 0; + size_t i = 0; + crypto::rand(rows * sizeof(key), (uint8_t*)&rv[0]); for (i = 0 ; i < rows ; i++) { - skGen(rv[i]); + sc_reduce32(rv[i].bytes); } return rv; } @@ -155,7 +118,7 @@ namespace rct { //generates a <secret , public> / Pedersen commitment but takes bH as input - tuple<ctkey, ctkey> ctskpkGen(key bH) { + tuple<ctkey, ctkey> ctskpkGen(const key &bH) { ctkey sk, pk; skpkGen(sk.dest, pk.dest); skpkGen(sk.mask, pk.mask); @@ -172,12 +135,12 @@ namespace rct { return mask; } - key commit(xmr_amount amount, key mask) { - mask = scalarmultBase(mask); + key commit(xmr_amount amount, const key &mask) { + key c = scalarmultBase(mask); key am = d2h(amount); key bH = scalarmultH(am); - addKeys(mask, mask, bH); - return mask; + addKeys(c, c, bH); + return c; } //generates a random uint long long (for testing) diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index a7e13eefa..cd3a6dc0d 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -64,19 +64,23 @@ namespace rct { //Various key initialization functions + static const key Z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; + static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; + //Creates a zero scalar - key zero(); - void zero(key &z); + inline key zero() { return Z; } + inline void zero(key &z) { memset(&z, 0, 32); } //Creates a zero elliptic curve point - key identity(); - void identity(key &Id); + inline key identity() { return I; } + inline void identity(key &Id) { memcpy(&Id, &I, 32); } //copies a scalar or point - void copy(key &AA, const key &A); - key copy(const key & AA); + inline void copy(key &AA, const key &A) { memcpy(&AA, &A, 32); } + inline key copy(const key & A) { key AA; memcpy(&AA, &A, 32); return AA; } + //initializes a key matrix; //first parameter is rows, //second is columns - keyM keyMInit(int, int); + keyM keyMInit(size_t rows, size_t cols); //Various key generation functions @@ -85,7 +89,7 @@ namespace rct { void skGen(key &); //generates a vector of secret keys of size "int" - keyV skvGen(int ); + keyV skvGen(size_t rows ); //generates a random curve point (for testing) key pkGen(); @@ -97,9 +101,9 @@ namespace rct { //generates C =aG + bH from b, a is random void genC(key & C, const key & a, xmr_amount amount); //this one is mainly for testing, can take arbitrary amounts.. - tuple<ctkey, ctkey> ctskpkGen(key bH); + tuple<ctkey, ctkey> ctskpkGen(const key &bH); // make a pedersen commitment with given key - key commit(xmr_amount amount, key mask); + key commit(xmr_amount amount, const key &mask); // make a pedersen commitment with zero key key zeroCommit(xmr_amount amount); //generates a random uint long long diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3f494f512..b1049265d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -78,6 +78,7 @@ typedef cryptonote::simple_wallet sw; #define DEFAULT_MIX 4 #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\001" +#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\001" // workaround for a suspected bug in pthread/kernel on MacOS X #ifdef __APPLE__ @@ -703,6 +704,8 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), tr("Verify a signature on the contents of a file")); m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), tr("Export a signed set of key images")); m_cmd_binder.set_handler("import_key_images", boost::bind(&simple_wallet::import_key_images, this, _1), tr("Import signed key images list and verify their spent status")); + m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("Export a set of outputs owned by this wallet")); + m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), tr("Import set of outputs owned by this wallet")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help")); } //---------------------------------------------------------------------------------------------------- @@ -3111,16 +3114,16 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) +bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx) { // gather info to ask the user uint64_t amount = 0, amount_to_dests = 0, change = 0; size_t min_mixin = ~0; std::unordered_map<std::string, uint64_t> dests; const std::string wallet_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); - for (size_t n = 0; n < txs.txes.size(); ++n) + for (size_t n = 0; n < get_num_txes(); ++n) { - const tools::wallet2::tx_construction_data &cd = txs.txes[n]; + const tools::wallet2::tx_construction_data &cd = get_tx(n); for (size_t s = 0; s < cd.sources.size(); ++s) { amount += cd.sources[s].amount; @@ -3128,9 +3131,9 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) if (mixin < min_mixin) min_mixin = mixin; } - for (size_t d = 0; d < cd.destinations.size(); ++d) + for (size_t d = 0; d < cd.splitted_dsts.size(); ++d) { - const tx_destination_entry &entry = cd.destinations[d]; + const tx_destination_entry &entry = cd.splitted_dsts[d]; std::string address = get_account_address_as_str(m_wallet->testnet(), entry.addr); std::unordered_map<std::string,uint64_t>::iterator i = dests.find(address); if (i == dests.end()) @@ -3141,9 +3144,21 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) } if (cd.change_dts.amount > 0) { - dests.insert(std::make_pair(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr), cd.change_dts.amount)); - amount_to_dests += cd.change_dts.amount; - change += cd.change_dts.amount; + std::unordered_map<std::string, uint64_t>::iterator it = dests.find(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr)); + if (it == dests.end()) + { + fail_msg_writer() << tr("Claimed change does not go to a paid address"); + return false; + } + if (it->second < cd.change_dts.amount) + { + fail_msg_writer() << tr("Claimed change is larger than payment to the change address"); + return false; + } + change = cd.change_dts.amount; + it->second -= cd.change_dts.amount; + if (it->second == 0) + dests.erase(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr)); } } std::string dest_string; @@ -3158,11 +3173,21 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) dest_string = tr("with no destinations"); uint64_t fee = amount - amount_to_dests; - std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, change %s, %s, with min mixin %lu (full details in log file). Is this okay? (Y/Yes/N/No)")) % (unsigned long)txs.txes.size() % print_money(amount) % print_money(fee) % print_money(change) % dest_string % (unsigned long)min_mixin).str(); + std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, change %s, %s, with min mixin %lu. Is this okay? (Y/Yes/N/No)")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % print_money(change) % dest_string % (unsigned long)min_mixin).str(); std::string accepted = command_line::input_line(prompt_str); return is_it_true(accepted); } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) +{ + return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs) +{ + return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;}); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::sign_transfer(const std::vector<std::string> &args_) { if(m_wallet->watch_only()) @@ -3198,7 +3223,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_) try { std::vector<tools::wallet2::pending_tx> ptx_vector; - bool r = m_wallet->load_tx("signed_monero_tx", ptx_vector); + bool r = m_wallet->load_tx("signed_monero_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); }); if (!r) { fail_msg_writer() << tr("Failed to load transaction from file"); @@ -3236,7 +3261,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_) if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee); if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address")) % print_money(dust_not_in_fee); - prompt << tr(".") << ENDL << tr("Is this okay? (Y/Yes/N/No)"); + prompt << tr(".") << ENDL << "Full transaction details are available in the log file" << ENDL << tr("Is this okay? (Y/Yes/N/No)"); std::string accepted = command_line::input_line(prompt.str()); if (std::cin.eof()) @@ -4091,6 +4116,103 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::export_outputs(const std::vector<std::string> &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: export_outputs <filename>"); + return true; + } + std::string filename = args[0]; + + try + { + std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs(); + + std::stringstream oss; + boost::archive::binary_oarchive ar(oss); + ar << outs; + + std::string data(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); + const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; + data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); + data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); + bool r = epee::file_io_utils::save_string_to_file(filename, data + oss.str()); + if (!r) + { + fail_msg_writer() << tr("failed to save file ") << filename; + return true; + } + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting outputs: " << e.what()); + fail_msg_writer() << "Error exporting outputs: " << e.what(); + return true; + } + + success_msg_writer() << tr("Outputs exported to ") << filename; + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::import_outputs(const std::vector<std::string> &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: import_outputs <filename>"); + return true; + } + std::string filename = args[0]; + + std::string data; + bool r = epee::file_io_utils::load_file_to_string(filename, data); + if (!r) + { + fail_msg_writer() << tr("failed to read file ") << filename; + return true; + } + const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC); + if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen)) + { + fail_msg_writer() << "Bad output export file magic in " << filename; + return true; + } + const size_t headerlen = magiclen + 2 * sizeof(crypto::public_key); + if (data.size() < headerlen) + { + fail_msg_writer() << "Bad data size from file " << filename; + return true; + } + const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[magiclen]; + const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[magiclen + sizeof(crypto::public_key)]; + const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; + if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) + { + fail_msg_writer() << "Outputs from " << filename << " are for a different account"; + return true; + } + + try + { + std::string body(data, headerlen); + std::stringstream iss; + iss << body; + boost::archive::binary_iarchive ar(iss); + std::vector<tools::wallet2::transfer_details> outputs; + ar >> outputs; + + size_t n_outputs = m_wallet->import_outputs(outputs); + success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported"; + } + catch (const std::exception &e) + { + fail_msg_writer() << "Failed to import outputs: " << e.what(); + return true; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::process_command(const std::vector<std::string> &args) { return m_cmd_binder.process_command_vec(args); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 375716604..fcc77ff69 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -152,11 +152,15 @@ namespace cryptonote bool verify(const std::vector<std::string> &args); bool export_key_images(const std::vector<std::string> &args); bool import_key_images(const std::vector<std::string> &args); + bool export_outputs(const std::vector<std::string> &args); + bool import_outputs(const std::vector<std::string> &args); uint64_t get_daemon_blockchain_height(std::string& err); bool try_connect_to_daemon(bool silent = false); bool ask_wallet_create_if_needed(); + bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx); bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs); + bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs); bool get_address_from_str(const std::string &str, cryptonote::account_public_address &address, bool &has_payment_id, crypto::hash8 &payment_id); /*! diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index aa99476ee..d2395ace1 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -81,6 +81,12 @@ bool WalletManagerImpl::closeWallet(Wallet *wallet) bool WalletManagerImpl::walletExists(const std::string &path) { + bool keys_file_exists; + bool wallet_file_exists; + tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists); + if(keys_file_exists){ + return true; + } return false; } @@ -88,10 +94,13 @@ bool WalletManagerImpl::walletExists(const std::string &path) std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path) { std::vector<std::string> result; + boost::filesystem::path work_dir(path); + // return empty result if path doesn't exist + if(!boost::filesystem::is_directory(path)){ + return result; + } const boost::regex wallet_rx("(.*)\\.(keys)$"); // searching for <wallet_name>.keys files boost::filesystem::recursive_directory_iterator end_itr; // Default ctor yields past-the-end - boost::filesystem::path work_dir(path); - for (boost::filesystem::recursive_directory_iterator itr(path); itr != end_itr; ++itr) { // Skip if not a file if (!boost::filesystem::is_regular_file(itr->status())) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 23e016f7b..8ea605375 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -74,8 +74,8 @@ using namespace cryptonote; // arbitrary, used to generate different hashes from the same input #define CHACHA8_KEY_TAIL 0x8c -#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\001" -#define SIGNED_TX_PREFIX "Monero signed tx set\001" +#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\002" +#define SIGNED_TX_PREFIX "Monero signed tx set\002" #define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone #define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone @@ -246,6 +246,15 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::public_key pub, } } //---------------------------------------------------------------------------------------------------- +bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki) +{ + if (!cryptonote::generate_key_image_helper(ack, tx_public_key, real_output_index, in_ephemeral, ki)) + return false; + if (m_watch_only) + memset(&ki, 0, 32); + return true; +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) { class lazy_txid_getter @@ -317,7 +326,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s // this assumes that the miner tx pays a single address if (received) { - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]); + wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]); THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); @@ -360,7 +369,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } if (received[i]) { - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]); + wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]); THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); @@ -408,7 +417,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } if (received[i]) { - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]); + wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]); THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); @@ -440,7 +449,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s { if (received) { - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]); + wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]); THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); @@ -518,14 +527,14 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount) { - LOG_ERROR("key image " << epee::string_tools::pod_to_hex(ki) + LOG_ERROR("key image " << epee::string_tools::pod_to_hex(kit->first) << " from received " << print_money(tx.vout[o].amount) << " output already exists with " << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " " << print_money(m_transfers[kit->second].amount()) << ", received output ignored"); } else { - LOG_ERROR("key image " << epee::string_tools::pod_to_hex(ki) + LOG_ERROR("key image " << epee::string_tools::pod_to_hex(kit->first) << " from received " << print_money(tx.vout[o].amount) << " output already exists with " << print_money(m_transfers[kit->second].amount()) << ", replacing with new output"); // The new larger output replaced a previous smaller one @@ -2138,9 +2147,14 @@ void wallet2::rescan_spent() std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size())); // update spent status + key_image zero_ki; + memset(&zero_ki, 0, 32); for (size_t i = 0; i < m_transfers.size(); ++i) { transfer_details& td = m_transfers[i]; + // a view wallet may not know about key images + if (td.m_key_image == zero_ki) + continue; if (td.m_spent != (daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT)) { if (td.m_spent) @@ -2630,11 +2644,8 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s signed_txes.ptx.push_back(pending_tx()); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); crypto::secret_key tx_key; - std::vector<cryptonote::tx_destination_entry> dests = sd.destinations; - if (sd.change_dts.amount > 0) - dests.push_back(sd.change_dts); - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sd.sources, dests, sd.extra, ptx.tx, sd.unlock_time, tx_key, sd.use_rct); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.destinations, sd.unlock_time, m_testnet); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sd.sources, sd.splitted_dsts, sd.extra, ptx.tx, sd.unlock_time, tx_key, sd.use_rct); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's @@ -2661,13 +2672,13 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s ptx.key_images = key_images; ptx.fee = 0; for (const auto &i: sd.sources) ptx.fee += i.amount; - for (const auto &i: dests) ptx.fee -= i.amount; + for (const auto &i: sd.splitted_dsts) ptx.fee -= i.amount; ptx.dust = 0; ptx.dust_added_to_fee = false; ptx.change_dts = sd.change_dts; -// ptx.selected_transfers = selected_transfers; + ptx.selected_transfers = sd.selected_transfers; ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet - ptx.dests = sd.destinations; + ptx.dests = sd.splitted_dsts; ptx.construction_data = sd; } @@ -2681,7 +2692,7 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s return epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + s); } //---------------------------------------------------------------------------------------------------- -bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx) +bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func) { std::string s; boost::system::error_code errcode; @@ -2709,7 +2720,14 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal LOG_PRINT_L0("Failed to parse data from " << signed_filename); return false; } - LOG_PRINT_L1("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions"); + LOG_PRINT_L0("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions"); + for (auto &ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx)); + + if (accept_func && !accept_func(signed_txs)) + { + LOG_PRINT_L1("Transactions rejected by callback"); + return false; + } ptx = signed_txs.ptx; @@ -3188,8 +3206,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent ptx.tx_key = tx_key; ptx.dests = dsts; ptx.construction_data.sources = sources; - ptx.construction_data.destinations = dsts; ptx.construction_data.change_dts = change_dts; + ptx.construction_data.splitted_dsts = splitted_dsts; + ptx.construction_data.selected_transfers = selected_transfers; ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; @@ -3307,8 +3326,9 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry ptx.tx_key = tx_key; ptx.dests = dsts; ptx.construction_data.sources = sources; - ptx.construction_data.destinations = dsts; ptx.construction_data.change_dts = change_dts; + ptx.construction_data.splitted_dsts = splitted_dsts; + ptx.construction_data.selected_transfers = selected_transfers; ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = true; @@ -3531,7 +3551,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp if (!prefered_inputs.empty()) { string s; - for (auto i: prefered_inputs) s += print_money(m_transfers[i].amount()) + " "; + for (auto i: prefered_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") "; LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s); } } @@ -3551,7 +3571,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp size_t idx = !prefered_inputs.empty() ? pop_back(prefered_inputs) : !unused_transfers_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : pop_best_value(unused_dust_indices, tx.selected_transfers); const transfer_details &td = m_transfers[idx]; - LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount())); + LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()) << ", ki " << td.m_key_image); // add this output to the list to spend tx.selected_transfers.push_back(idx); @@ -4243,7 +4263,11 @@ std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key crypto::key_image ki; cryptonote::keypair in_ephemeral; cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, td.m_internal_output_index, in_ephemeral, ki); - THROW_WALLET_EXCEPTION_IF(ki != td.m_key_image, + + bool zero_key_image = true; + for (size_t i = 0; i < sizeof(td.m_key_image); ++i) + zero_key_image &= (td.m_key_image.data[i] == 0); + THROW_WALLET_EXCEPTION_IF(!zero_key_image && ki != td.m_key_image, error::wallet_internal_error, "key_image generated not matched with cached key image"); THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); @@ -4330,6 +4354,50 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag return m_transfers[signed_key_images.size() - 1].m_block_height; } //---------------------------------------------------------------------------------------------------- +std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const +{ + std::vector<tools::wallet2::transfer_details> outs; + + outs.reserve(m_transfers.size()); + for (size_t n = 0; n < m_transfers.size(); ++n) + { + const transfer_details &td = m_transfers[n]; + + outs.push_back(td); + } + + return outs; +} +//---------------------------------------------------------------------------------------------------- +size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs) +{ + m_transfers.clear(); + m_transfers.reserve(outputs.size()); + for (size_t i = 0; i < outputs.size(); ++i) + { + transfer_details td = outputs[i]; + + // the hot wallet wouldn't have known about key images (except if we already exported them) + cryptonote::keypair in_ephemeral; + std::vector<tx_extra_field> tx_extra_fields; + tx_extra_pub_key pub_key_field; + + THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + i); + THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(td.m_tx.extra, tx_extra_fields), error::wallet_internal_error, + "Transaction extra has unsupported format at index " + i); + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error, + "Public key wasn't found in the transaction extra at index " + i); + + cryptonote::generate_key_image_helper(m_account.get_keys(), pub_key_field.pub_key, td.m_internal_output_index, in_ephemeral, td.m_key_image); + THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + i); + + m_transfers.push_back(td); + } + + return m_transfers.size(); +} +//---------------------------------------------------------------------------------------------------- void wallet2::generate_genesis(cryptonote::block& b) { if (m_testnet) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index fa9797219..6cd288ac1 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -154,16 +154,18 @@ namespace tools struct tx_construction_data { std::vector<cryptonote::tx_source_entry> sources; - std::vector<cryptonote::tx_destination_entry> destinations; cryptonote::tx_destination_entry change_dts; + std::vector<cryptonote::tx_destination_entry> splitted_dsts; + std::list<size_t> selected_transfers; std::vector<uint8_t> extra; uint64_t unlock_time; bool use_rct; BEGIN_SERIALIZE_OBJECT() FIELD(sources) - FIELD(destinations) FIELD(change_dts) + FIELD(splitted_dsts) + FIELD(selected_transfers) FIELD(extra) VARINT_FIELD(unlock_time) FIELD(use_rct) @@ -355,7 +357,7 @@ namespace tools void commit_tx(std::vector<pending_tx>& ptx_vector); bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename); bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::function<bool(const unsigned_tx_set&)> accept_func = NULL); - bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx); + bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); @@ -471,6 +473,9 @@ namespace tools std::string sign(const std::string &data) const; bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const; + std::vector<tools::wallet2::transfer_details> export_outputs() const; + size_t import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs); + std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const; uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent); @@ -521,6 +526,7 @@ namespace tools void set_unspent(size_t idx); template<typename entry> void get_outs(std::vector<std::vector<entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); + bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); cryptonote::account_base m_account; std::string m_daemon_address; @@ -930,8 +936,9 @@ namespace tools ptx.tx_key = tx_key; ptx.dests = dsts; ptx.construction_data.sources = sources; - ptx.construction_data.destinations = dsts; ptx.construction_data.change_dts = change_dts; + ptx.construction_data.splitted_dsts = splitted_dsts; + ptx.construction_data.selected_transfers = selected_transfers; ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; |