Merge pull request #4720

5f614ba9 simplewallet: print the number of show/all transfers (moneromooo-monero)
8d71b2b1 wallet2: only export necessary outputs and key images (moneromooo-monero)
769ae42a wallet2: faster output and key image import/export (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2018-11-06 14:52:24 +02:00
commit 236c0dd48c
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
9 changed files with 194 additions and 73 deletions

View file

@ -212,9 +212,10 @@ namespace trezor {
tools::wallet2::signed_tx_set & signed_tx, tools::wallet2::signed_tx_set & signed_tx,
hw::tx_aux_data & aux_data) hw::tx_aux_data & aux_data)
{ {
CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset");
size_t num_tx = unsigned_tx.txes.size(); size_t num_tx = unsigned_tx.txes.size();
signed_tx.key_images.clear(); signed_tx.key_images.clear();
signed_tx.key_images.resize(unsigned_tx.transfers.size()); signed_tx.key_images.resize(unsigned_tx.transfers.second.size());
for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) { for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) {
std::shared_ptr<protocol::tx::Signer> signer; std::shared_ptr<protocol::tx::Signer> signer;

View file

@ -4483,7 +4483,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
tools::wallet2::transfer_container transfers; tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers); m_wallet->get_transfers(transfers);
bool transfers_found = false; size_t transfers_found = 0;
for (const auto& td : transfers) for (const auto& td : transfers)
{ {
if (!filter || available != td.m_spent) if (!filter || available != td.m_spent)
@ -4496,7 +4496,6 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
if (verbose) if (verbose)
verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str(); verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str();
message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string; message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string;
transfers_found = true;
} }
std::string verbose_string; std::string verbose_string;
if (verbose) if (verbose)
@ -4511,6 +4510,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
td.m_txid % td.m_txid %
td.m_subaddr_index.minor % td.m_subaddr_index.minor %
verbose_string; verbose_string;
++transfers_found;
} }
} }
@ -4529,6 +4529,10 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
success_msg_writer() << tr("No incoming unavailable transfers"); success_msg_writer() << tr("No incoming unavailable transfers");
} }
} }
else
{
success_msg_writer() << boost::format("Found %u/%u transfers") % transfers_found % transfers.size();
}
return true; return true;
} }
@ -6105,8 +6109,8 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
{ {
std::string extra_message; std::string extra_message;
if (!txs.transfers.empty()) if (!txs.transfers.second.empty())
extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.size()).str(); extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str();
return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message); return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------

View file

@ -69,6 +69,7 @@ using namespace epee;
#include "common/base58.h" #include "common/base58.h"
#include "common/dns_utils.h" #include "common/dns_utils.h"
#include "common/notify.h" #include "common/notify.h"
#include "common/perf_timer.h"
#include "ringct/rctSigs.h" #include "ringct/rctSigs.h"
#include "ringdb.h" #include "ringdb.h"
#include "device/device_cold.hpp" #include "device/device_cold.hpp"
@ -112,11 +113,11 @@ using namespace cryptonote;
#define SUBADDRESS_LOOKAHEAD_MAJOR 50 #define SUBADDRESS_LOOKAHEAD_MAJOR 50
#define SUBADDRESS_LOOKAHEAD_MINOR 200 #define SUBADDRESS_LOOKAHEAD_MINOR 200
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\003"
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" #define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\004"
#define SEGREGATION_FORK_HEIGHT 99999999 #define SEGREGATION_FORK_HEIGHT 99999999
#define TESTNET_SEGREGATION_FORK_HEIGHT 99999999 #define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
@ -1605,6 +1606,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_txid = txid; td.m_txid = txid;
td.m_key_image = tx_scan_info[o].ki; td.m_key_image = tx_scan_info[o].ki;
td.m_key_image_known = !m_watch_only && !m_multisig; td.m_key_image_known = !m_watch_only && !m_multisig;
td.m_key_image_requested = false;
td.m_key_image_partial = m_multisig; td.m_key_image_partial = m_multisig;
td.m_amount = amount; td.m_amount = amount;
td.m_pk_index = pk_index - 1; td.m_pk_index = pk_index - 1;
@ -5465,7 +5467,7 @@ std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) c
txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
} }
txs.transfers = m_transfers; txs.transfers = export_outputs();
// save as binary // save as binary
std::ostringstream oss; std::ostringstream oss;
boost::archive::portable_binary_oarchive ar(oss); boost::archive::portable_binary_oarchive ar(oss);
@ -7951,6 +7953,7 @@ void wallet2::light_wallet_get_unspent_outs()
td.m_key_image = unspent_key_image; td.m_key_image = unspent_key_image;
td.m_key_image_known = !m_watch_only && !m_multisig; td.m_key_image_known = !m_watch_only && !m_multisig;
td.m_key_image_requested = false;
td.m_key_image_partial = m_multisig; td.m_key_image_partial = m_multisig;
td.m_amount = o.amount; td.m_amount = o.amount;
td.m_pk_index = 0; td.m_pk_index = 0;
@ -9124,7 +9127,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
{ {
txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
} }
txs.transfers = m_transfers; txs.transfers = std::make_pair(0, m_transfers);
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
@ -9155,7 +9158,7 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
dev_cold->ki_sync(&wallet_shim, m_transfers, ski); dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
return import_key_images(ski, spent, unspent); return import_key_images(ski, 0, spent, unspent);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
@ -10517,31 +10520,45 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
bool wallet2::export_key_images(const std::string &filename) const bool wallet2::export_key_images(const std::string &filename) const
{ {
std::vector<std::pair<crypto::key_image, crypto::signature>> ski = export_key_images(); PERF_TIMER(export_key_images);
std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
const uint32_t offset = ski.first;
std::string data; std::string data;
data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key));
data.resize(4);
data[0] = offset & 0xff;
data[1] = (offset >> 8) & 0xff;
data[2] = (offset >> 16) & 0xff;
data[3] = (offset >> 24) & 0xff;
data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); 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)); data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
for (const auto &i: ski) for (const auto &i: ski.second)
{ {
data += std::string((const char *)&i.first, sizeof(crypto::key_image)); data += std::string((const char *)&i.first, sizeof(crypto::key_image));
data += std::string((const char *)&i.second, sizeof(crypto::signature)); data += std::string((const char *)&i.second, sizeof(crypto::signature));
} }
// encrypt data, keep magic plaintext // encrypt data, keep magic plaintext
PERF_TIMER(export_key_images_encrypt);
std::string ciphertext = encrypt_with_view_secret_key(data); std::string ciphertext = encrypt_with_view_secret_key(data);
return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key_images() const std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images() const
{ {
PERF_TIMER(export_key_images_raw);
std::vector<std::pair<crypto::key_image, crypto::signature>> ski; std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
ski.reserve(m_transfers.size()); size_t offset = 0;
for (size_t n = 0; n < m_transfers.size(); ++n) while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_requested)
++offset;
ski.reserve(m_transfers.size() - offset);
for (size_t n = offset; n < m_transfers.size(); ++n)
{ {
const transfer_details &td = m_transfers[n]; const transfer_details &td = m_transfers[n];
@ -10585,11 +10602,12 @@ std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key
ski.push_back(std::make_pair(td.m_key_image, signature)); ski.push_back(std::make_pair(td.m_key_image, signature));
} }
return ski; return std::make_pair(offset, ski);
} }
uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent)
{ {
PERF_TIMER(import_key_images_fsu);
std::string data; std::string data;
bool r = epee::file_io_utils::load_file_to_string(filename, data); bool r = epee::file_io_utils::load_file_to_string(filename, data);
@ -10603,6 +10621,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
try try
{ {
PERF_TIMER(import_key_images_decrypt);
data = decrypt_with_view_secret_key(std::string(data, magiclen)); data = decrypt_with_view_secret_key(std::string(data, magiclen));
} }
catch (const std::exception &e) catch (const std::exception &e)
@ -10610,15 +10629,17 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
} }
const size_t headerlen = 2 * sizeof(crypto::public_key); const size_t headerlen = 4 + 2 * sizeof(crypto::public_key);
THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24);
const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4];
const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)];
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
{ {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
} }
THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
@ -10635,28 +10656,33 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
ski.push_back(std::make_pair(key_image, signature)); ski.push_back(std::make_pair(key_image, signature));
} }
return import_key_images(ski, spent, unspent); return import_key_images(ski, offset, spent, unspent);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent) uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent)
{ {
PERF_TIMER(import_key_images_lots);
COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size() - offset, error::wallet_internal_error,
"The blockchain is out of date compared to the signed key images"); "The blockchain is out of date compared to the signed key images");
if (signed_key_images.empty()) if (signed_key_images.empty() && offset == 0)
{ {
spent = 0; spent = 0;
unspent = 0; unspent = 0;
return 0; return 0;
} }
req.key_images.reserve(signed_key_images.size());
PERF_TIMER_START(import_key_images_A);
for (size_t n = 0; n < signed_key_images.size(); ++n) for (size_t n = 0; n < signed_key_images.size(); ++n)
{ {
const transfer_details &td = m_transfers[n]; const transfer_details &td = m_transfers[n + offset];
const crypto::key_image &key_image = signed_key_images[n].first; const crypto::key_image &key_image = signed_key_images[n].first;
const crypto::signature &signature = signed_key_images[n].second; const crypto::signature &signature = signed_key_images[n].second;
@ -10667,30 +10693,37 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target); const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target);
const crypto::public_key pkey = o.key; const crypto::public_key pkey = o.key;
if (!td.m_key_image_known || !(key_image == td.m_key_image))
{
std::vector<const crypto::public_key*> pkeys; std::vector<const crypto::public_key*> pkeys;
pkeys.push_back(&pkey); pkeys.push_back(&pkey);
THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()), THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()),
error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast<std::string>(n) + "/" error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast<std::string>(n + offset) + "/"
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature),
error::signature_check_failed, boost::lexical_cast<std::string>(n) + "/" error::signature_check_failed, boost::lexical_cast<std::string>(n + offset) + "/"
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
+ ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
}
req.key_images.push_back(epee::string_tools::pod_to_hex(key_image)); req.key_images.push_back(epee::string_tools::pod_to_hex(key_image));
} }
PERF_TIMER_STOP(import_key_images_A);
PERF_TIMER_START(import_key_images_B);
for (size_t n = 0; n < signed_key_images.size(); ++n) for (size_t n = 0; n < signed_key_images.size(); ++n)
{ {
m_transfers[n].m_key_image = signed_key_images[n].first; m_transfers[n + offset].m_key_image = signed_key_images[n].first;
m_key_images[m_transfers[n].m_key_image] = n; m_key_images[m_transfers[n + offset].m_key_image] = n + offset;
m_transfers[n].m_key_image_known = true; m_transfers[n + offset].m_key_image_known = true;
m_transfers[n].m_key_image_partial = false; m_transfers[n + offset].m_key_image_requested = false;
m_transfers[n + offset].m_key_image_partial = false;
} }
PERF_TIMER_STOP(import_key_images_B);
if(check_spent) if(check_spent)
{ {
PERF_TIMER(import_key_images_RPC);
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock(); m_daemon_rpc_mutex.unlock();
@ -10702,7 +10735,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size())); std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n) for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
{ {
transfer_details &td = m_transfers[n]; transfer_details &td = m_transfers[n + offset];
td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT; td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
} }
} }
@ -10713,6 +10746,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
// was created by sweep_all, so we can't know the spent height and other detailed info. // was created by sweep_all, so we can't know the spent height and other detailed info.
std::unordered_map<crypto::key_image, crypto::hash> spent_key_images; std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
PERF_TIMER_START(import_key_images_C);
for (const transfer_details &td: m_transfers) for (const transfer_details &td: m_transfers)
{ {
for (const cryptonote::txin_v& in : td.m_tx.vin) for (const cryptonote::txin_v& in : td.m_tx.vin)
@ -10721,10 +10755,12 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid)); spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
} }
} }
PERF_TIMER_STOP(import_key_images_C);
PERF_TIMER_START(import_key_images_D);
for(size_t i = 0; i < signed_key_images.size(); ++i) for(size_t i = 0; i < signed_key_images.size(); ++i)
{ {
transfer_details &td = m_transfers[i]; const transfer_details &td = m_transfers[i + offset];
uint64_t amount = td.amount(); uint64_t amount = td.amount();
if (td.m_spent) if (td.m_spent)
spent += amount; spent += amount;
@ -10742,6 +10778,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
spent_txids.insert(skii->second); spent_txids.insert(skii->second);
} }
} }
PERF_TIMER_STOP(import_key_images_D);
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
if (check_spent) if (check_spent)
@ -10751,8 +10789,12 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res; COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res;
gettxs_req.decode_as_json = false; gettxs_req.decode_as_json = false;
gettxs_req.prune = false; gettxs_req.prune = false;
gettxs_req.txs_hashes.reserve(spent_txids.size());
for (const crypto::hash& spent_txid : spent_txids) for (const crypto::hash& spent_txid : spent_txids)
gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid)); gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
PERF_TIMER_START(import_key_images_E);
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock(); m_daemon_rpc_mutex.unlock();
@ -10760,8 +10802,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size())); "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
PERF_TIMER_STOP(import_key_images_E);
// process each outgoing tx // process each outgoing tx
PERF_TIMER_START(import_key_images_F);
auto spent_txid = spent_txids.begin(); auto spent_txid = spent_txids.begin();
hw::device &hwdev = m_account.get_device(); hw::device &hwdev = m_account.get_device();
for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs) for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
@ -10857,7 +10901,9 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
++spent_txid; ++spent_txid;
} }
PERF_TIMER_STOP(import_key_images_F);
PERF_TIMER_START(import_key_images_G);
for (size_t n : swept_transfers) for (size_t n : swept_transfers)
{ {
const transfer_details& td = m_transfers[n]; const transfer_details& td = m_transfers[n];
@ -10868,6 +10914,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
m_confirmed_txs.insert(std::make_pair(spent_txid, pd)); m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
} }
PERF_TIMER_STOP(import_key_images_G);
} }
return m_transfers[signed_key_images.size() - 1].m_block_height; return m_transfers[signed_key_images.size() - 1].m_block_height;
@ -10888,6 +10935,7 @@ bool wallet2::import_key_images(std::vector<crypto::key_image> key_images)
td.m_key_image = key_images[i]; td.m_key_image = key_images[i];
m_key_images[m_transfers[i].m_key_image] = i; m_key_images[m_transfers[i].m_key_image] = i;
td.m_key_image_known = true; td.m_key_image_known = true;
td.m_key_image_requested = false;
td.m_key_image_partial = false; td.m_key_image_partial = false;
m_pub_keys[m_transfers[i].get_public_key()] = i; m_pub_keys[m_transfers[i].get_public_key()] = i;
} }
@ -10953,50 +11001,86 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs() const
{ {
PERF_TIMER(export_outputs);
std::vector<tools::wallet2::transfer_details> outs; std::vector<tools::wallet2::transfer_details> outs;
outs.reserve(m_transfers.size()); size_t offset = 0;
for (size_t n = 0; n < m_transfers.size(); ++n) while (offset < m_transfers.size() && m_transfers[offset].m_key_image_known)
++offset;
outs.reserve(m_transfers.size() - offset);
for (size_t n = offset; n < m_transfers.size(); ++n)
{ {
const transfer_details &td = m_transfers[n]; const transfer_details &td = m_transfers[n];
outs.push_back(td); outs.push_back(td);
} }
return outs; return std::make_pair(offset, outs);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::string wallet2::export_outputs_to_str() const std::string wallet2::export_outputs_to_str() const
{ {
std::vector<tools::wallet2::transfer_details> outs = export_outputs(); PERF_TIMER(export_outputs_to_str);
std::stringstream oss; std::stringstream oss;
boost::archive::portable_binary_oarchive ar(oss); boost::archive::portable_binary_oarchive ar(oss);
ar << outs; ar << export_outputs();
std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
std::string header; std::string header;
header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
PERF_TIMER(export_outputs_encryption);
std::string ciphertext = encrypt_with_view_secret_key(header + oss.str()); std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
return magic + ciphertext; return magic + ciphertext;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs) size_t wallet2::import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs)
{ {
m_transfers.clear(); PERF_TIMER(import_outputs);
m_transfers.reserve(outputs.size());
for (size_t i = 0; i < outputs.size(); ++i) THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
"Imported outputs omit more outputs that we know of");
const size_t offset = outputs.first;
const size_t original_size = m_transfers.size();
m_transfers.resize(offset + outputs.second.size());
for (size_t i = 0; i < offset; ++i)
m_transfers[i].m_key_image_requested = false;
for (size_t i = 0; i < outputs.second.size(); ++i)
{ {
transfer_details td = outputs[i]; transfer_details td = outputs.second[i];
// skip those we've already imported, or which have different data
if (i + offset < original_size)
{
// compare the data used to create the key image below
const transfer_details &org_td = m_transfers[i + offset];
if (!org_td.m_key_image_known)
goto process;
#define CMPF(f) if (!(td.f == org_td.f)) goto process
CMPF(m_txid);
CMPF(m_key_image);
CMPF(m_internal_output_index);
#undef CMPF
if (!(get_transaction_prefix_hash(td.m_tx) == get_transaction_prefix_hash(org_td.m_tx)))
goto process;
// copy anyway, since the comparison does not include ancillary fields which may have changed
m_transfers[i + offset] = std::move(td);
continue;
}
process:
// the hot wallet wouldn't have known about key images (except if we already exported them) // the hot wallet wouldn't have known about key images (except if we already exported them)
cryptonote::keypair in_ephemeral; cryptonote::keypair in_ephemeral;
THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast<std::string>(i)); THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast<std::string>(i + offset));
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
@ -11007,13 +11091,14 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
expand_subaddresses(td.m_subaddr_index); expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true; td.m_key_image_known = true;
td.m_key_image_requested = true;
td.m_key_image_partial = false; td.m_key_image_partial = false;
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key, THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i)); error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
m_key_images[td.m_key_image] = m_transfers.size(); m_key_images[td.m_key_image] = i + offset;
m_pub_keys[td.get_public_key()] = m_transfers.size(); m_pub_keys[td.get_public_key()] = i + offset;
m_transfers.push_back(std::move(td)); m_transfers[i + offset] = std::move(td);
} }
return m_transfers.size(); return m_transfers.size();
@ -11021,6 +11106,7 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
size_t wallet2::import_outputs_from_str(const std::string &outputs_st) size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
{ {
PERF_TIMER(import_outputs_from_str);
std::string data = outputs_st; std::string data = outputs_st;
const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC); const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen)) if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
@ -11030,6 +11116,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
try try
{ {
PERF_TIMER(import_outputs_decrypt);
data = decrypt_with_view_secret_key(std::string(data, magiclen)); data = decrypt_with_view_secret_key(std::string(data, magiclen));
} }
catch (const std::exception &e) catch (const std::exception &e)
@ -11056,7 +11143,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
std::string body(data, headerlen); std::string body(data, headerlen);
std::stringstream iss; std::stringstream iss;
iss << body; iss << body;
std::vector<tools::wallet2::transfer_details> outputs; std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
try try
{ {
boost::archive::portable_binary_iarchive ar(iss); boost::archive::portable_binary_iarchive ar(iss);
@ -11248,6 +11335,7 @@ void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key
m_key_images.erase(td.m_key_image); m_key_images.erase(td.m_key_image);
td.m_key_image = get_multisig_composite_key_image(n); td.m_key_image = get_multisig_composite_key_image(n);
td.m_key_image_known = true; td.m_key_image_known = true;
td.m_key_image_requested = false;
td.m_key_image_partial = false; td.m_key_image_partial = false;
td.m_multisig_k = multisig_k[n]; td.m_multisig_k = multisig_k[n];
m_key_images[td.m_key_image] = n; m_key_images[td.m_key_image] = n;

View file

@ -247,6 +247,7 @@ namespace tools
uint64_t m_amount; uint64_t m_amount;
bool m_rct; bool m_rct;
bool m_key_image_known; bool m_key_image_known;
bool m_key_image_requested;
size_t m_pk_index; size_t m_pk_index;
cryptonote::subaddress_index m_subaddr_index; cryptonote::subaddress_index m_subaddr_index;
bool m_key_image_partial; bool m_key_image_partial;
@ -270,6 +271,7 @@ namespace tools
FIELD(m_amount) FIELD(m_amount)
FIELD(m_rct) FIELD(m_rct)
FIELD(m_key_image_known) FIELD(m_key_image_known)
FIELD(m_key_image_requested)
FIELD(m_pk_index) FIELD(m_pk_index)
FIELD(m_subaddr_index) FIELD(m_subaddr_index)
FIELD(m_key_image_partial) FIELD(m_key_image_partial)
@ -417,7 +419,7 @@ namespace tools
struct unsigned_tx_set struct unsigned_tx_set
{ {
std::vector<tx_construction_data> txes; std::vector<tx_construction_data> txes;
wallet2::transfer_container transfers; std::pair<size_t, wallet2::transfer_container> transfers;
}; };
struct signed_tx_set struct signed_tx_set
@ -1071,9 +1073,9 @@ namespace tools
bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const;
// Import/Export wallet data // Import/Export wallet data
std::vector<tools::wallet2::transfer_details> export_outputs() const; std::pair<size_t, std::vector<tools::wallet2::transfer_details>> export_outputs() const;
std::string export_outputs_to_str() const; std::string export_outputs_to_str() const;
size_t import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs); size_t import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs);
size_t import_outputs_from_str(const std::string &outputs_st); size_t import_outputs_from_str(const std::string &outputs_st);
payment_container export_payments() const; payment_container export_payments() const;
void import_payments(const payment_container &payments); void import_payments(const payment_container &payments);
@ -1081,8 +1083,8 @@ namespace tools
std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const; std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc); void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
bool export_key_images(const std::string &filename) const; bool export_key_images(const std::string &filename) const;
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const; std::pair<size_t, 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, bool check_spent = true); uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
bool import_key_images(std::vector<crypto::key_image> key_images); bool import_key_images(std::vector<crypto::key_image> key_images);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
@ -1396,7 +1398,7 @@ namespace tools
}; };
} }
BOOST_CLASS_VERSION(tools::wallet2, 26) BOOST_CLASS_VERSION(tools::wallet2, 26)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
@ -1454,6 +1456,10 @@ namespace boost
x.m_multisig_k.clear(); x.m_multisig_k.clear();
x.m_multisig_info.clear(); x.m_multisig_info.clear();
} }
if (ver < 10)
{
x.m_key_image_requested = false;
}
} }
template <class Archive> template <class Archive>
@ -1535,6 +1541,12 @@ namespace boost
a & x.m_multisig_info; a & x.m_multisig_info;
a & x.m_multisig_k; a & x.m_multisig_k;
a & x.m_key_image_partial; a & x.m_key_image_partial;
if (ver < 10)
{
initialize_transfer_details(a, x, ver);
return;
}
a & x.m_key_image_requested;
} }
template <class Archive> template <class Archive>

View file

@ -2460,12 +2460,13 @@ namespace tools
if (!m_wallet) return not_open(er); if (!m_wallet) return not_open(er);
try try
{ {
std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet->export_key_images(); std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images();
res.signed_key_images.resize(ski.size()); res.offset = ski.first;
for (size_t n = 0; n < ski.size(); ++n) res.signed_key_images.resize(ski.second.size());
for (size_t n = 0; n < ski.second.size(); ++n)
{ {
res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski[n].first); res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski.second[n].first);
res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski[n].second); res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski.second[n].second);
} }
} }
@ -2518,7 +2519,7 @@ namespace tools
ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data()); ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data());
} }
uint64_t spent = 0, unspent = 0; uint64_t spent = 0, unspent = 0;
uint64_t height = m_wallet->import_key_images(ski, spent, unspent); uint64_t height = m_wallet->import_key_images(ski, req.offset, spent, unspent);
res.spent = spent; res.spent = spent;
res.unspent = unspent; res.unspent = unspent;
res.height = height; res.height = height;

View file

@ -47,7 +47,7 @@
// advance which version they will stop working with // advance which version they will stop working with
// Don't go over 32767 for any of these // Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1 #define WALLET_RPC_VERSION_MAJOR 1
#define WALLET_RPC_VERSION_MINOR 5 #define WALLET_RPC_VERSION_MINOR 6
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools namespace tools
@ -1579,9 +1579,11 @@ namespace wallet_rpc
struct response struct response
{ {
uint32_t offset;
std::vector<signed_key_image> signed_key_images; std::vector<signed_key_image> signed_key_images;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(offset);
KV_SERIALIZE(signed_key_images); KV_SERIALIZE(signed_key_images);
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
@ -1602,9 +1604,11 @@ namespace wallet_rpc
struct request struct request
{ {
uint32_t offset;
std::vector<signed_key_image> signed_key_images; std::vector<signed_key_image> signed_key_images;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_OPT(offset, (uint32_t)0);
KV_SERIALIZE(signed_key_images); KV_SERIALIZE(signed_key_images);
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };

View file

@ -77,7 +77,7 @@ int ColdOutputsFuzzer::run(const std::string &filename)
s = std::string("\x01\x16serialization::archive") + s; s = std::string("\x01\x16serialization::archive") + s;
try try
{ {
std::vector<tools::wallet2::transfer_details> outputs; std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
std::stringstream iss; std::stringstream iss;
iss << s; iss << s;
boost::archive::portable_binary_iarchive ar(iss); boost::archive::portable_binary_iarchive ar(iss);

View file

@ -908,6 +908,17 @@ TEST(Serialization, portability_outputs)
ASSERT_TRUE(td2.m_pk_index == 0); ASSERT_TRUE(td2.m_pk_index == 0);
} }
struct unsigned_tx_set
{
std::vector<tools::wallet2::tx_construction_data> txes;
tools::wallet2::transfer_container transfers;
};
template <class Archive>
inline void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization::version_type ver)
{
a & x.txes;
a & x.transfers;
}
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003"
TEST(Serialization, portability_unsigned_tx) TEST(Serialization, portability_unsigned_tx)
{ {
@ -918,7 +929,7 @@ TEST(Serialization, portability_unsigned_tx)
ASSERT_TRUE(r); ASSERT_TRUE(r);
const size_t magiclen = strlen(UNSIGNED_TX_PREFIX); const size_t magiclen = strlen(UNSIGNED_TX_PREFIX);
ASSERT_FALSE(strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen)); ASSERT_FALSE(strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen));
tools::wallet2::unsigned_tx_set exported_txs; unsigned_tx_set exported_txs;
s = s.substr(magiclen); s = s.substr(magiclen);
r = false; r = false;
try try