aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp787
1 files changed, 541 insertions, 246 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 4840137af..cd4c1102c 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -110,6 +110,8 @@ using namespace cryptonote;
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
+#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
+
#define SEGREGATION_FORK_HEIGHT 1546000
#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
@@ -119,6 +121,7 @@ using namespace cryptonote;
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
+std::atomic<unsigned int> tools::wallet2::key_ref::refs(0);
namespace
{
@@ -198,6 +201,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
+ const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
const bool restricted = command_line::get_arg(vm, opts.restricted);
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
@@ -226,13 +230,13 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
if (!daemon_port)
{
- daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : stagenet ? config::stagenet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
+ daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
}
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
- std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet ? TESTNET : stagenet ? STAGENET : MAINNET, restricted));
+ std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, restricted));
wallet->init(std::move(daemon_address), std::move(login));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
@@ -770,8 +774,6 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
// When switching from light wallet to full wallet, we need to reset the height we got from lw node.
- if(m_light_wallet)
- m_local_bc_height = m_blockchain.size();
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl);
}
//----------------------------------------------------------------------------------------------------
@@ -1036,6 +1038,23 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio
tx_scan_info.error = false;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const
+{
+ if (!is_out_data || i >= is_out_data->received.size())
+ return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info);
+
+ tx_scan_info.received = is_out_data->received[i];
+ if(tx_scan_info.received)
+ {
+ tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs
+ }
+ else
+ {
+ tx_scan_info.money_transfered = 0;
+ }
+ tx_scan_info.error = false;
+}
+//----------------------------------------------------------------------------------------------------
static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
{
crypto::secret_key scalar1;
@@ -1089,16 +1108,48 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi
++num_vouts_received;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen)
+void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const
{
- //ensure device is let in NONE mode in any case
- hw::device &hwdev = m_account.get_device();
-
- boost::unique_lock<hw::device> hwdev_lock (hwdev);
- hw::reset_mode rst(hwdev);
- hwdev_lock.unlock();
+ const cryptonote::account_keys& keys = m_account.get_keys();
+
+ if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields))
+ {
+ // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
+ tx_cache_data.tx_extra_fields.clear();
+ return;
+ }
+
+ // Don't try to extract tx public key if tx has no ouputs
+ const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
+ if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
+ {
+ const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size();
+ if (!tx.vout.empty())
+ {
+ // if tx.vout is not empty, we loop through all tx pubkeys
+ const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
- // In this function, tx (probably) only contains the base information
+ tx_extra_pub_key pub_key_field;
+ size_t pk_index = 0;
+ while (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, pub_key_field, pk_index++))
+ tx_cache_data.primary.push_back({pub_key_field.pub_key, {}, rec});
+
+ // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
+ tx_extra_additional_pub_keys additional_tx_pub_keys;
+ std::vector<crypto::key_derivation> additional_derivations;
+ if (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, additional_tx_pub_keys))
+ {
+ for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
+ tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}});
+ }
+ }
+ }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data)
+{
+ // In this function, tx (probably) only contains the base information
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
@@ -1106,12 +1157,16 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
- std::vector<tx_extra_field> tx_extra_fields;
- if(!parse_tx_extra(tx.extra, tx_extra_fields))
+ std::vector<tx_extra_field> local_tx_extra_fields;
+ if (tx_cache_data.tx_extra_fields.empty())
{
- // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
- LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
+ if(!parse_tx_extra(tx.extra, local_tx_extra_fields))
+ {
+ // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
+ }
}
+ const std::vector<tx_extra_field> &tx_extra_fields = tx_cache_data.tx_extra_fields.empty() ? local_tx_extra_fields : tx_cache_data.tx_extra_fields;
// Don't try to extract tx public key if tx has no ouputs
size_t pk_index = 0;
@@ -1130,6 +1185,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_skip_transaction(height, txid, tx);
break;
}
+ if (!tx_cache_data.primary.empty())
+ {
+ THROW_WALLET_EXCEPTION_IF(tx_cache_data.primary.size() < pk_index || pub_key_field.pub_key != tx_cache_data.primary[pk_index - 1].pkey,
+ error::wallet_internal_error, "tx_cache_data is out of sync");
+ }
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
@@ -1138,28 +1198,49 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
- hwdev_lock.lock();
- hwdev.set_mode(hw::device::TRANSACTION_PARSE);
- if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
+ std::vector<crypto::key_derivation> additional_derivations;
+ tx_extra_additional_pub_keys additional_tx_pub_keys;
+ const wallet2::is_out_data *is_out_data_ptr = NULL;
+ if (tx_cache_data.primary.empty())
{
- MWARNING("Failed to generate key derivation from tx pubkey, skipping");
- static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
- memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
- }
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
+ hw::reset_mode rst(hwdev);
- // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
- std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
- std::vector<crypto::key_derivation> additional_derivations;
- for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
+ hwdev.set_mode(hw::device::TRANSACTION_PARSE);
+ if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
+ {
+ MWARNING("Failed to generate key derivation from tx pubkey in " << txid << ", skipping");
+ static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
+ memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
+ }
+
+ // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
+ if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys))
+ {
+ for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
+ {
+ additional_derivations.push_back({});
+ if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back()))
+ {
+ MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping");
+ memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation));
+ }
+ }
+ }
+ }
+ else
{
- additional_derivations.push_back({});
- if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
+ THROW_WALLET_EXCEPTION_IF(pk_index - 1 >= tx_cache_data.primary.size(),
+ error::wallet_internal_error, "pk_index out of range of tx_cache_data");
+ is_out_data_ptr = &tx_cache_data.primary[pk_index - 1];
+ derivation = tx_cache_data.primary[pk_index - 1].derivation;
+ for (size_t n = 0; n < tx_cache_data.additional.size(); ++n)
{
- MWARNING("Failed to generate key derivation from tx pubkey, skipping");
- additional_derivations.pop_back();
+ additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey);
+ additional_derivations.push_back(tx_cache_data.additional[n].derivation);
}
}
- hwdev_lock.unlock();
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
{
@@ -1167,7 +1248,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
- check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0]);
+ check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
// this assumes that the miner tx pays a single address
@@ -1178,59 +1259,59 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
for (size_t i = 1; i < tx.vout.size(); ++i)
{
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::ref(tx_scan_info[i])));
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true);
}
- waiter.wait();
+ waiter.wait(&tpool);
// then scan all outputs from 0
- hwdev_lock.lock();
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::NONE);
for (size_t i = 0; i < tx.vout.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations);
+ hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
- hwdev_lock.unlock();
}
}
- else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1)
+ else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr)
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::ref(tx_scan_info[i])));
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true);
}
- waiter.wait();
+ waiter.wait(&tpool);
- hwdev_lock.lock();
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::NONE);
for (size_t i = 0; i < tx.vout.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations);
+ hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
- hwdev_lock.unlock();
}
else
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, tx_scan_info[i]);
+ check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- hwdev_lock.lock();
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::NONE);
- hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations);
+ hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
- hwdev_lock.unlock();
}
}
}
@@ -1306,20 +1387,20 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
}
}
- else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
+ else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount)
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
- << " from received " << print_money(tx.vout[o].amount) << " output already exists with "
+ << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
<< (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
- << print_money(m_transfers[kit->second].amount()) << ", received output ignored");
+ << print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored");
}
else
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
- << " from received " << print_money(tx.vout[o].amount) << " output already exists with "
+ << " from received " << print_money(tx_scan_info[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
- tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx.vout[o].amount;
+ tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
if (!pool)
{
@@ -1570,12 +1651,11 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
add_rings(tx);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices)
+void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset)
{
- size_t txidx = 0;
- THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != o_indices.indices.size(), error::wallet_internal_error,
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
"block transactions=" + std::to_string(bche.txs.size()) +
- " not match with daemon response size=" + std::to_string(o_indices.indices.size()));
+ " not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size()));
//handle transactions from new block
@@ -1583,29 +1663,26 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false);
+ if (m_refresh_type != RefreshNoCoinbase)
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset]);
+ ++tx_cache_data_offset;
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
- size_t idx = 0;
- for (const auto& txblob: bche.txs)
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
+ for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
{
- cryptonote::transaction tx;
- bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
- THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false);
- ++idx;
+ process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]);
}
TIME_MEASURE_FINISH(txs_handle_time);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
}else
{
- if (!(height % 100))
+ if (!(height % 128))
LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
}
m_blockchain.push_back(bl_id);
- ++m_local_bc_height;
if (0 != m_callback)
m_callback->on_new_block(height, b);
@@ -1651,7 +1728,7 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl
bl_id = get_block_hash(bl);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
+void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
{
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
@@ -1686,6 +1763,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
}
req.start_height = start_height;
+ req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
@@ -1697,11 +1775,11 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
blocks_start_height = res.start_height;
- blocks = res.blocks;
- o_indices = res.output_indices;
+ blocks = std::move(res.blocks);
+ o_indices = std::move(res.output_indices);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes)
+void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes)
{
cryptonote::COMMAND_RPC_GET_HASHES_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_HASHES_FAST::response res = AUTO_VAL_INIT(res);
@@ -1716,88 +1794,117 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, res.status);
blocks_start_height = res.start_height;
- hashes = res.m_block_ids;
+ hashes = std::move(res.m_block_ids);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added)
+void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added)
{
size_t current_index = start_height;
blocks_added = 0;
- size_t tx_o_indices_idx = 0;
- THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
+ THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain");
tools::threadpool& tpool = tools::threadpool::getInstance();
- int threads = tpool.get_max_concurrency();
- if (threads > 1)
+ tools::threadpool::waiter waiter;
+
+ size_t num_txes = 0;
+ std::vector<tx_cache_data> tx_cache_data;
+ for (size_t i = 0; i < blocks.size(); ++i)
+ num_txes += 1 + parsed_blocks[i].txes.size();
+ tx_cache_data.resize(num_txes);
+ size_t txidx = 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
{
- std::vector<crypto::hash> round_block_hashes(threads);
- std::vector<cryptonote::block> round_blocks(threads);
- std::deque<bool> error(threads);
- size_t blocks_size = blocks.size();
- std::list<block_complete_entry>::const_iterator blocki = blocks.begin();
- for (size_t b = 0; b < blocks_size; b += threads)
+ THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
+ error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
+ if (m_refresh_type != RefreshNoCoinbase)
+ tpool.submit(&waiter, [&, i, txidx](){ cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx), tx_cache_data[txidx]); });
+ ++txidx;
+ for (size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx)
{
- size_t round_size = std::min((size_t)threads, blocks_size - b);
- tools::threadpool::waiter waiter;
+ tpool.submit(&waiter, [&, i, idx, txidx](){ cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].block.tx_hashes[idx], tx_cache_data[txidx]); });
+ ++txidx;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
+ waiter.wait(&tpool);
- std::list<block_complete_entry>::const_iterator tmpblocki = blocki;
- for (size_t i = 0; i < round_size; ++i)
- {
- tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
- std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i])));
- ++tmpblocki;
- }
- waiter.wait();
- tmpblocki = blocki;
- for (size_t i = 0; i < round_size; ++i)
- {
- THROW_WALLET_EXCEPTION_IF(error[i], error::block_parse_error, tmpblocki->block);
- ++tmpblocki;
- }
- for (size_t i = 0; i < round_size; ++i)
- {
- const crypto::hash &bl_id = round_block_hashes[i];
- cryptonote::block &bl = round_blocks[i];
+ hw::device &hwdev = m_account.get_device();
+ hw::reset_mode rst(hwdev);
+ hwdev.set_mode(hw::device::TRANSACTION_PARSE);
+ const cryptonote::account_keys &keys = m_account.get_keys();
- if(current_index >= m_blockchain.size())
- {
- process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]);
- ++blocks_added;
- }
- else if(bl_id != m_blockchain[current_index])
- {
- //split detected here !!!
- THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error,
- "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
- " (height " + std::to_string(start_height) + "), local block id at this height: " +
- string_tools::pod_to_hex(m_blockchain[current_index]));
-
- detach_blockchain(current_index);
- process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]);
- }
- else
+ auto gender = [&](wallet2::is_out_data &iod) {
+ boost::unique_lock<hw::device> hwdev_lock(hwdev);
+ if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation))
+ {
+ MWARNING("Failed to generate key derivation from tx pubkey, skipping");
+ static_assert(sizeof(iod.derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
+ memcpy(&iod.derivation, rct::identity().bytes, sizeof(iod.derivation));
+ }
+ };
+
+ for (auto &slot: tx_cache_data)
+ {
+ for (auto &iod: slot.primary)
+ tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }, true);
+ for (auto &iod: slot.additional)
+ tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }, true);
+ }
+ waiter.wait(&tpool);
+
+ auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
+ for (size_t k = 0; k < n_vouts; ++k)
+ {
+ const auto &o = tx.vout[k];
+ if (o.target.type() == typeid(cryptonote::txout_to_key))
+ {
+ std::vector<crypto::key_derivation> additional_derivations;
+ for (const auto &iod: tx_cache_data[txidx].additional)
+ additional_derivations.push_back(iod.derivation);
+ const auto &key = boost::get<txout_to_key>(o.target).key;
+ for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l)
{
- LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
+ THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
+ error::wallet_internal_error, "Unexpected received array size");
+ tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
+ additional_derivations.clear();
}
- ++current_index;
- ++blocki;
}
}
- }
- else
+ };
+
+ txidx = 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
{
- for(auto& bl_entry: blocks)
+ if (m_refresh_type != RefreshType::RefreshNoCoinbase)
+ {
+ THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
+ const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
+ tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
+ }
+ ++txidx;
+ for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
+ {
+ THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
+ tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
+ ++txidx;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
+ waiter.wait(&tpool);
+ hwdev.set_mode(hw::device::NONE);
+
+ size_t tx_cache_data_offset = 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
{
- cryptonote::block bl;
- bool r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl);
- THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block);
+ const crypto::hash &bl_id = parsed_blocks[i].hash;
+ const cryptonote::block &bl = parsed_blocks[i].block;
- crypto::hash bl_id = get_block_hash(bl);
if(current_index >= m_blockchain.size())
{
- process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]);
+ process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
++blocks_added;
}
else if(bl_id != m_blockchain[current_index])
@@ -1809,16 +1916,14 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index);
- process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]);
+ process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
}
else
{
LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
}
-
++current_index;
- ++tx_o_indices_idx;
- }
+ tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
}
}
//----------------------------------------------------------------------------------------------------
@@ -1834,7 +1939,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
refresh(trusted_daemon, start_height, blocks_fetched, received_money);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error)
+void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error)
{
error = false;
@@ -1843,18 +1948,53 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
drop_from_short_history(short_chain_history, 3);
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
- cryptonote::block bl;
- std::list<cryptonote::block_complete_entry>::const_reverse_iterator i = prev_blocks.rbegin();
+ std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin();
for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n)
{
- bool ok = cryptonote::parse_and_validate_block_from_blob(i->block, bl);
- THROW_WALLET_EXCEPTION_IF(!ok, error::block_parse_error, i->block);
- short_chain_history.push_front(cryptonote::get_block_hash(bl));
+ short_chain_history.push_front(i->hash);
++i;
}
// pull the new blocks
+ std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
+ THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
+
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ parsed_blocks.resize(blocks.size());
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
+ std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
+ }
+ waiter.wait(&tpool);
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ if (parsed_blocks[i].error)
+ {
+ error = true;
+ break;
+ }
+ parsed_blocks[i].o_indices = std::move(o_indices[i]);
+ }
+
+ boost::mutex error_lock;
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ parsed_blocks[i].txes.resize(blocks[i].txs.size());
+ for (size_t j = 0; j < blocks[i].txs.size(); ++j)
+ {
+ tpool.submit(&waiter, [&, i, j](){
+ if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j], parsed_blocks[i].txes[j]))
+ {
+ boost::unique_lock<boost::mutex> lock(error_lock);
+ error = true;
+ }
+ }, true);
+ }
+ }
+ waiter.wait(&tpool);
}
catch(...)
{
@@ -2067,7 +2207,7 @@ void wallet2::update_pool_state(bool refreshed)
[tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen, {});
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
@@ -2111,7 +2251,7 @@ void wallet2::update_pool_state(bool refreshed)
//----------------------------------------------------------------------------------------------------
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
{
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
const uint64_t checkpoint_height = m_checkpoints.get_max_height();
if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height)
@@ -2121,7 +2261,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
- m_local_bc_height = m_blockchain.size();
short_chain_history.clear();
get_short_chain_history(short_chain_history);
}
@@ -2139,7 +2278,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
}
if (hashes.size() + current_index < stop_height) {
drop_from_short_history(short_chain_history, 3);
- std::list<crypto::hash>::iterator right = hashes.end();
+ std::vector<crypto::hash>::iterator right = hashes.end();
// prepend 3 more
for (int i = 0; i<3; i++) {
right--;
@@ -2151,10 +2290,9 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
{
if(current_index >= m_blockchain.size())
{
- if (!(current_index % 1000))
+ if (!(current_index % 1024))
LOG_PRINT_L2( "Skipped block by height: " << current_index);
m_blockchain.push_back(bl_id);
- ++m_local_bc_height;
if (0 != m_callback)
{ // FIXME: this isn't right, but simplewallet just logs that we got a block.
@@ -2202,6 +2340,8 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
//----------------------------------------------------------------------------------------------------
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
{
+ key_ref kref(*this);
+
if(m_light_wallet) {
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
@@ -2215,7 +2355,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// Update lw heights
m_light_wallet_scanned_block_height = res.scanned_block_height;
m_light_wallet_blockchain_height = res.blockchain_height;
- m_local_bc_height = res.blockchain_height;
// If new height - call new_block callback
if(m_light_wallet_blockchain_height != prev_height)
{
@@ -2244,8 +2383,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
uint64_t blocks_start_height;
- std::list<cryptonote::block_complete_entry> blocks;
- std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
+ std::vector<cryptonote::block_complete_entry> blocks;
+ std::vector<parsed_block> parsed_blocks;
bool refreshed = false;
// pull the first set of blocks
@@ -2266,31 +2405,34 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// If stop() is called during fast refresh we don't need to continue
if(!m_run.load(std::memory_order_relaxed))
return;
- pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
// always reset start_height to 0 to force short_chain_ history to be used on
// subsequent pulls in this refresh.
start_height = 0;
+ bool first = true;
while(m_run.load(std::memory_order_relaxed))
{
try
{
// pull the next set of blocks while we're processing the current one
uint64_t next_blocks_start_height;
- std::list<cryptonote::block_complete_entry> next_blocks;
- std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> next_o_indices;
+ std::vector<cryptonote::block_complete_entry> next_blocks;
+ std::vector<parsed_block> next_parsed_blocks;
bool error = false;
- if (blocks.empty())
+ if (!first && blocks.empty())
{
refreshed = false;
break;
}
- tpool.submit(&waiter, [&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
+ tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, error);});
- process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
- blocks_fetched += added_blocks;
- waiter.wait();
- if(blocks_start_height == next_blocks_start_height)
+ if (!first)
+ {
+ process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks);
+ blocks_fetched += added_blocks;
+ }
+ waiter.wait(&tpool);
+ if(!first && blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
refreshed = true;
@@ -2299,8 +2441,9 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height;
- blocks = next_blocks;
- o_indices = next_o_indices;
+ blocks = std::move(next_blocks);
+ parsed_blocks = std::move(next_parsed_blocks);
+ first = false;
// handle error from async fetching thread
if (error)
@@ -2311,7 +2454,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- waiter.wait();
+ waiter.wait(&tpool);
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
@@ -2468,7 +2611,6 @@ void wallet2::detach_blockchain(uint64_t height)
size_t blocks_detached = m_blockchain.size() - height;
m_blockchain.crop(height);
- m_local_bc_height -= blocks_detached;
for (auto it = m_payments.begin(); it != m_payments.end(); )
{
@@ -2510,7 +2652,6 @@ bool wallet2::clear()
m_scanned_pool_txs[0].clear();
m_scanned_pool_txs[1].clear();
m_address_book.clear();
- m_local_bc_height = 1;
m_subaddresses.clear();
m_subaddress_labels.clear();
return true;
@@ -3277,6 +3418,12 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
+ {
+ // the default lookahead setting (50:200) is clearly too much for hardware wallet
+ m_subaddress_lookahead_major = 5;
+ m_subaddress_lookahead_minor = 20;
+ }
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty()) {
store();
@@ -3628,6 +3775,14 @@ bool wallet2::has_multisig_partial_key_images() const
return false;
}
+bool wallet2::has_unknown_key_images() const
+{
+ for (const auto &td: m_transfers)
+ if (!td.m_key_image_known)
+ return true;
+ return false;
+}
+
/*!
* \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there)
* \param wallet_name Name of wallet file (should exist)
@@ -3790,7 +3945,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
wallet2::cache_file_data cache_file_data;
std::string buf;
- bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf);
+ bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits<size_t>::max());
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file);
// try to read it as an encrypted cache
@@ -3878,8 +4033,6 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (get_num_subaddress_accounts() == 0)
add_subaddress_account(tr("Primary account"));
- m_local_bc_height = m_blockchain.size();
-
try
{
find_and_save_rings(false);
@@ -4259,7 +4412,6 @@ void wallet2::rescan_blockchain(bool refresh)
crypto::hash genesis_hash = get_block_hash(genesis);
m_blockchain.push_back(genesis_hash);
add_subaddress_account(tr("Primary account"));
- m_local_bc_height = 1;
if (refresh)
this->refresh(false);
@@ -4275,7 +4427,7 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height)
if(!is_tx_spendtime_unlocked(unlock_time, block_height))
return false;
- if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > m_local_bc_height)
+ if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > get_blockchain_current_height())
return false;
return true;
@@ -4286,7 +4438,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig
if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
//interpret as block index
- if(m_local_bc_height-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
+ if(get_blockchain_current_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
return true;
else
return false;
@@ -4671,6 +4823,15 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
{
LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
+ std::string ciphertext = dump_tx_to_str(ptx_vector);
+ if (ciphertext.empty())
+ return false;
+ return epee::file_io_utils::save_string_to_file(filename, ciphertext);
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const
+{
+ LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
unsigned_tx_set txs;
for (auto &tx: ptx_vector)
{
@@ -4690,11 +4851,11 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
}
catch (...)
{
- return false;
+ return std::string();
}
LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
std::string ciphertext = encrypt_with_view_secret_key(oss.str());
- return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + ciphertext);
+ return std::string(UNSIGNED_TX_PREFIX) + ciphertext;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
@@ -4712,10 +4873,17 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
LOG_PRINT_L0("Failed to load from " << unsigned_filename);
return false;
}
+
+ return parse_unsigned_tx_from_str(s, exported_txs);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
+{
+ std::string s = unsigned_tx_st;
const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1;
if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen))
{
- LOG_PRINT_L0("Bad magic from " << unsigned_filename);
+ LOG_PRINT_L0("Bad magic from unsigned tx");
return false;
}
s = s.substr(magiclen);
@@ -4731,7 +4899,7 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
+ LOG_PRINT_L0("Failed to parse data from unsigned tx");
return false;
}
}
@@ -4748,19 +4916,19 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
+ LOG_PRINT_L0("Failed to parse data from unsigned tx");
return false;
}
}
catch (const std::exception &e)
{
- LOG_PRINT_L0("Failed to decrypt " << unsigned_filename << ": " << e.what());
+ LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what());
return false;
}
}
else
{
- LOG_PRINT_L0("Unsupported version in " << unsigned_filename);
+ LOG_PRINT_L0("Unsupported version in unsigned tx");
return false;
}
LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions");
@@ -4781,14 +4949,12 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
}
return sign_tx(exported_txs, signed_filename, txs, export_raw);
}
-
//----------------------------------------------------------------------------------------------------
-bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
+bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
{
import_outputs(exported_txs.transfers);
// sign the transactions
- signed_tx_set signed_txes;
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
@@ -4796,11 +4962,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
- bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty();
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, bulletproof, m_multisig ? &msout : NULL);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, sd.use_bulletproofs, m_multisig ? &msout : NULL);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
// 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,
@@ -4850,19 +5015,20 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
signed_txes.key_images[i] = m_transfers[i].m_key_image;
}
- // save as binary
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << signed_txes;
- }
- catch(...)
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
+{
+ // sign the transactions
+ signed_tx_set signed_txes;
+ std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
+ if (ciphertext.empty())
{
+ LOG_PRINT_L0("Failed to sign unsigned_tx_set");
return false;
}
- LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
- std::string ciphertext = encrypt_with_view_secret_key(oss.str());
+
if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext))
{
LOG_PRINT_L0("Failed to save file to " << signed_filename);
@@ -4885,6 +5051,32 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
return true;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes)
+{
+ // sign the transactions
+ bool r = sign_tx(exported_txs, ptx, signed_txes);
+ if (!r)
+ {
+ LOG_PRINT_L0("Failed to sign unsigned_tx_set");
+ return std::string();
+ }
+
+ // save as binary
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << signed_txes;
+ }
+ catch(...)
+ {
+ return std::string();
+ }
+ LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
+ std::string ciphertext = encrypt_with_view_secret_key(oss.str());
+ return std::string(SIGNED_TX_PREFIX) + ciphertext;
+}
+//----------------------------------------------------------------------------------------------------
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;
@@ -4902,10 +5094,20 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
LOG_PRINT_L0("Failed to load from " << signed_filename);
return false;
}
+
+ return parse_tx_from_str(s, ptx, accept_func);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func)
+{
+ std::string s = signed_tx_st;
+ boost::system::error_code errcode;
+ signed_tx_set signed_txs;
+
const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen))
{
- LOG_PRINT_L0("Bad magic from " << signed_filename);
+ LOG_PRINT_L0("Bad magic from signed transaction");
return false;
}
s = s.substr(magiclen);
@@ -4921,7 +5123,7 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << signed_filename);
+ LOG_PRINT_L0("Failed to parse data from signed transaction");
return false;
}
}
@@ -4938,23 +5140,23 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse decrypted data from " << signed_filename);
+ LOG_PRINT_L0("Failed to parse decrypted data from signed transaction");
return false;
}
}
catch (const std::exception &e)
{
- LOG_PRINT_L0("Failed to decrypt " << signed_filename << ": " << e.what());
+ LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what());
return false;
}
}
else
{
- LOG_PRINT_L0("Unsupported version in " << signed_filename);
+ LOG_PRINT_L0("Unsupported version in signed transaction");
return false;
}
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));
+ for (auto &c_ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
if (accept_func && !accept_func(signed_txs))
{
@@ -5176,8 +5378,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
cryptonote::transaction tx;
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources;
- const bool bulletproof = sd.use_rct && (ptx.tx.rct_signatures.type == rct::RCTTypeFullBulletproof || ptx.tx.rct_signatures.type == rct::RCTTypeSimpleBulletproof);
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, bulletproof, &msout, false);
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, sd.use_bulletproofs, &msout, false);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
@@ -5570,6 +5771,24 @@ bool wallet2::set_ring_database(const std::string &filename)
return true;
}
+crypto::chacha_key wallet2::get_ringdb_key()
+{
+ if (!m_ringdb_key)
+ {
+ MINFO("caching ringdb key");
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
+ m_ringdb_key = key;
+ }
+ return *m_ringdb_key;
+}
+
+void wallet2::clear_ringdb_key()
+{
+ MINFO("clearing ringdb key");
+ m_ringdb_key = boost::none;
+}
+
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
@@ -5580,9 +5799,8 @@ bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transac
bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
{
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
- try { return add_rings(key, tx); }
+ key_ref kref(*this);
+ try { return add_rings(get_ringdb_key(), tx); }
catch (const std::exception &e) { return false; }
}
@@ -5590,9 +5808,8 @@ bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
return false;
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
- try { return m_ringdb->remove_rings(key, tx); }
+ key_ref kref(*this);
+ try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
catch (const std::exception &e) { return false; }
}
@@ -5629,10 +5846,8 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::
bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
-
- try { return get_ring(key, key_image, outs); }
+ key_ref kref(*this);
+ try { return get_ring(get_ringdb_key(), key_image, outs); }
catch (const std::exception &e) { return false; }
}
@@ -5641,10 +5856,8 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
if (!m_ringdb)
return false;
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
-
- try { return m_ringdb->set_ring(key, key_image, outs, relative); }
+ key_ref kref(*this);
+ try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
catch (const std::exception &e) { return false; }
}
@@ -5655,6 +5868,7 @@ bool wallet2::find_and_save_rings(bool force)
if (!m_ringdb)
return false;
+ key_ref kref(*this);
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
@@ -5672,9 +5886,6 @@ bool wallet2::find_and_save_rings(bool force)
MDEBUG("Found " << std::to_string(txs_hashes.size()) << " transactions");
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
-
// get those transactions from the daemon
static const size_t SLICE_SIZE = 200;
for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE)
@@ -5711,7 +5922,7 @@ bool wallet2::find_and_save_rings(bool force)
crypto::hash tx_hash, tx_prefix_hash;
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
THROW_WALLET_EXCEPTION_IF(epee::string_tools::pod_to_hex(tx_hash) != tx_info.tx_hash, error::wallet_internal_error, "txid mismatch");
- THROW_WALLET_EXCEPTION_IF(!add_rings(key, tx), error::wallet_internal_error, "Failed to save ring");
+ THROW_WALLET_EXCEPTION_IF(!add_rings(get_ringdb_key(), tx), error::wallet_internal_error, "Failed to save ring");
}
}
@@ -5890,9 +6101,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
return;
}
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
-
if (fake_outputs_count > 0)
{
uint64_t segregation_fork_height = get_segregation_fork_height();
@@ -6070,7 +6278,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (td.m_key_image_known && !td.m_key_image_partial)
{
std::vector<uint64_t> ring;
- if (get_ring(key, td.m_key_image, ring))
+ if (get_ring(get_ringdb_key(), td.m_key_image, ring))
{
MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
@@ -6262,7 +6470,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (td.m_key_image_known && !td.m_key_image_partial)
{
std::vector<uint64_t> ring;
- if (get_ring(key, td.m_key_image, ring))
+ if (get_ring(get_ringdb_key(), td.m_key_image, ring))
{
for (uint64_t out: ring)
{
@@ -6480,6 +6688,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
+ ptx.construction_data.use_bulletproofs = false;
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -6735,6 +6944,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
+ ptx.construction_data.use_bulletproofs = !tx.rct_signatures.p.bulletproofs.empty();
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -7362,8 +7572,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction tx;
pending_tx ptx;
size_t bytes;
+ uint64_t needed_fee;
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
+ TX() : bytes(0), needed_fee(0) {}
+
void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
if (merge_destinations)
{
@@ -7756,6 +7969,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.outs = outs;
+ tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
accumulated_change += test_ptx.change_dts.amount;
adding_fee = false;
@@ -7807,7 +8021,7 @@ skip_tx:
fake_outs_count, /* CONST size_t fake_outputs_count, */
tx.outs, /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */
unlock_time, /* CONST uint64_t unlock_time, */
- needed_fee, /* CONST uint64_t fee, */
+ tx.needed_fee, /* CONST uint64_t fee, */
extra, /* const std::vector<uint8_t>& extra, */
test_tx, /* OUT cryptonote::transaction& tx, */
test_ptx, /* OUT cryptonote::transaction& tx, */
@@ -7818,7 +8032,7 @@ skip_tx:
fake_outs_count,
tx.outs,
unlock_time,
- needed_fee,
+ tx.needed_fee,
extra,
detail::digit_split_strategy,
tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD),
@@ -7839,7 +8053,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -7938,7 +8152,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
cryptonote::transaction tx;
pending_tx ptx;
size_t bytes;
+ uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs;
+
+ TX() : bytes(0), needed_fee(0) {}
};
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
@@ -8036,6 +8253,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.outs = outs;
+ tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
accumulated_change += test_ptx.change_dts.amount;
if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
@@ -8056,10 +8274,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
cryptonote::transaction test_tx;
pending_tx test_ptx;
if (use_rct) {
- transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
test_tx, test_ptx, bulletproof);
} else {
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
}
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
@@ -8076,7 +8294,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -8263,6 +8481,16 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), trusted_daemon);
}
+//----------------------------------------------------------------------------------------------------
+void wallet2::discard_unmixable_outputs(bool trusted_daemon)
+{
+ // may throw
+ std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
+ for (size_t idx : unmixable_outputs)
+ {
+ m_transfers[idx].m_spent = true;
+ }
+}
bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
{
@@ -8436,8 +8664,9 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
}
std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
- THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * sig_len,
- error::wallet_internal_error, "incorrect signature size");
+ if( sig_str.size() != header_len + num_sigs * sig_len ) {
+ return false;
+ }
// decode base58
signatures.clear();
@@ -9167,9 +9396,9 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
uint64_t wallet2::get_approximate_blockchain_height() const
{
// time of v2 fork
- const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? (time_t)-1/*TODO*/ : 1458748658;
+ const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658;
// v2 fork block
- const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827;
+ const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827;
// avg seconds per block
const int seconds_per_block = DIFFICULTY_TARGET_V2;
// Calculated blockchain height
@@ -9802,7 +10031,6 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
generate_genesis(genesis);
crypto::hash genesis_hash = get_block_hash(genesis);
check_genesis(genesis_hash);
- m_local_bc_height = m_blockchain.size();
}
//----------------------------------------------------------------------------------------------------
std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
@@ -9820,6 +10048,23 @@ std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
return outs;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::export_outputs_to_str() const
+{
+ std::vector<tools::wallet2::transfer_details> outs = export_outputs();
+
+ std::stringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ ar << outs;
+
+ 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;
+ std::string header;
+ 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));
+ std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
+ return magic + ciphertext;
+}
+//----------------------------------------------------------------------------------------------------
size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs)
{
m_transfers.clear();
@@ -9852,6 +10097,67 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
return m_transfers.size();
}
//----------------------------------------------------------------------------------------------------
+size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
+{
+ std::string data = outputs_st;
+ const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
+ if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad magic from outputs"));
+ }
+
+ try
+ {
+ data = decrypt_with_view_secret_key(std::string(data, magiclen));
+ }
+ catch (const std::exception &e)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt outputs: ") + e.what());
+ }
+
+ const size_t headerlen = 2 * sizeof(crypto::public_key);
+ if (data.size() < headerlen)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad data size for outputs"));
+ }
+ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
+ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
+ 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)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Outputs from are for a different account"));
+ }
+
+ size_t imported_outputs = 0;
+ try
+ {
+ std::string body(data, headerlen);
+ std::stringstream iss;
+ iss << body;
+ std::vector<tools::wallet2::transfer_details> outputs;
+ try
+ {
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> outputs;
+ }
+ catch (...)
+ {
+ iss.str("");
+ iss << body;
+ boost::archive::binary_iarchive ar(iss);
+ ar >> outputs;
+ }
+
+ imported_outputs = import_outputs(outputs);
+ }
+ catch (const std::exception &e)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
+ }
+
+ return imported_outputs;
+}
+//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
{
crypto::public_key pkey;
@@ -10551,17 +10857,6 @@ uint64_t wallet2::get_segregation_fork_height() const
}
//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) const {
- if (m_nettype == TESTNET)
- {
- cryptonote::generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else if (m_nettype == STAGENET)
- {
- cryptonote::generate_genesis_block(b, config::stagenet::GENESIS_TX, config::stagenet::GENESIS_NONCE);
- }
- else
- {
- cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
- }
+ cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
}
}