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.cpp969
1 files changed, 651 insertions, 318 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 617fb9a87..66f1fecbc 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -117,8 +117,11 @@ using namespace cryptonote;
#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
+#define FIRST_REFRESH_GRANULARITY 1024
+
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());
@@ -656,6 +660,7 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_default_priority(0),
m_refresh_type(RefreshOptimizeCoinbase),
m_auto_refresh(true),
+ m_first_refresh_done(false),
m_refresh_from_block_height(0),
m_explicit_refresh_from_block_height(true),
m_confirm_missing_payment_id(true),
@@ -671,6 +676,7 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_segregate_pre_fork_outputs(true),
m_key_reuse_mitigation2(true),
m_segregation_height(0),
+ m_ignore_fractional_outputs(true),
m_is_initialized(false),
m_restricted(restricted),
is_old_file_format(false),
@@ -685,7 +691,8 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_light_wallet_unlocked_balance(0),
m_key_on_device(false),
m_ring_history_saved(false),
- m_ringdb()
+ m_ringdb(),
+ m_last_block_reward(0)
{
}
@@ -769,8 +776,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);
}
//----------------------------------------------------------------------------------------------------
@@ -946,6 +951,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
}
m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
m_subaddress_labels[index.major].resize(index.minor + 1);
+ get_account_tags();
}
else if (m_subaddress_labels[index.major].size() <= index.minor)
{
@@ -1035,6 +1041,33 @@ 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;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::check_acc_out_precomp_once(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, bool &already_seen) const
+{
+ tx_scan_info.received = boost::none;
+ if (already_seen)
+ return;
+ check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
+ if (tx_scan_info.received)
+ already_seen = true;
+}
+//----------------------------------------------------------------------------------------------------
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;
@@ -1088,16 +1121,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);
+
+ 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});
- // In this function, tx (probably) only contains the base information
+ // 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);
@@ -1105,16 +1170,21 @@ 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;
std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
+ std::deque<bool> output_found(tx.vout.size(), false);
while (!tx.vout.empty())
{
// if tx.vout is not empty, we loop through all tx pubkeys
@@ -1129,6 +1199,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;
@@ -1137,28 +1212,55 @@ 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));
+ }
+
+ if (pk_index == 1)
+ {
+ // 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;
+ if (pk_index == 1)
{
- MWARNING("Failed to generate key derivation from tx pubkey, skipping");
- additional_derivations.pop_back();
+ for (size_t n = 0; n < tx_cache_data.additional.size(); ++n)
+ {
+ 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)
{
@@ -1166,7 +1268,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_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[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
@@ -1176,60 +1278,60 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// the first one was already checked
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])));
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[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])));
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[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_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[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();
}
}
}
@@ -1305,20 +1407,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)
{
@@ -1455,6 +1557,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
// We got a payment ID to go with this tx
LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8);
+ MINFO("Consider using subaddresses instead of encrypted payment IDs");
if (tx_pub_key != null_pkey)
{
if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
@@ -1478,12 +1581,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
+ MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
}
}
- else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
- {
- LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
- }
for (const auto& i : tx_money_got_in_outs)
{
@@ -1569,12 +1669,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
@@ -1582,39 +1681,38 @@ 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);
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
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);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
+void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const
{
size_t i = 0;
size_t current_multiplier = 1;
- size_t sz = m_blockchain.size() - m_blockchain.offset();
+ size_t blockchain_size = std::max((size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset());
+ size_t sz = blockchain_size - m_blockchain.offset();
if(!sz)
{
ids.push_back(m_blockchain.genesis());
@@ -1649,7 +1747,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);
@@ -1684,6 +1782,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();
@@ -1695,11 +1794,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);
@@ -1714,88 +1813,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(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain");
+ 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::out_of_hashchain_bounds_error);
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])
@@ -1807,32 +1935,30 @@ 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();
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh()
+void wallet2::refresh(bool trusted_daemon)
{
uint64_t blocks_fetched = 0;
- refresh(0, blocks_fetched);
+ refresh(trusted_daemon, 0, blocks_fetched);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched)
+void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched)
{
bool received_money = false;
- refresh(start_height, blocks_fetched, received_money);
+ 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;
@@ -1841,18 +1967,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(...)
{
@@ -1893,8 +2054,8 @@ void wallet2::update_pool_state(bool refreshed)
MDEBUG("update_pool_state start");
// get the pool state
- cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req;
- cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res;
+ cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
+ cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
@@ -2065,7 +2226,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)
{
@@ -2107,19 +2268,18 @@ void wallet2::update_pool_state(bool refreshed)
MDEBUG("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
-void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
+void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force)
{
- 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)
+ if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
{
// we will drop all these, so don't bother getting them
uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
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);
}
@@ -2137,7 +2297,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--;
@@ -2149,10 +2309,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.
@@ -2198,8 +2357,10 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
+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.
@@ -2213,7 +2374,6 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// 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)
{
@@ -2242,12 +2402,12 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
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
- get_short_chain_history(short_chain_history);
+ get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
m_run.store(true, std::memory_order_relaxed);
if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
if (!start_height)
@@ -2256,7 +2416,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
fast_refresh(start_height, blocks_start_height, short_chain_history);
// regenerate the history now that we've got a full set of hashes
short_chain_history.clear();
- get_short_chain_history(short_chain_history);
+ get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
start_height = 0;
// and then fall through to regular refresh processing
}
@@ -2264,31 +2424,61 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// 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())
+ added_blocks = 0;
+ 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)
+ {
+ try
+ {
+ process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks);
+ }
+ catch (const tools::error::out_of_hashchain_bounds_error&)
+ {
+ MINFO("Daemon claims next refresh block is out of hash chain bounds, resetting hash chain");
+ uint64_t stop_height = m_blockchain.offset();
+ std::vector<crypto::hash> tip(m_blockchain.size() - m_blockchain.offset());
+ for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i)
+ tip[i - m_blockchain.offset()] = m_blockchain[i];
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.clear();
+ m_blockchain.push_back(get_block_hash(b));
+ short_chain_history.clear();
+ get_short_chain_history(short_chain_history);
+ fast_refresh(stop_height, blocks_start_height, short_chain_history, true);
+ THROW_WALLET_EXCEPTION_IF(m_blockchain.size() != stop_height, error::wallet_internal_error, "Unexpected hashchain size");
+ THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error, "Unexpected hashchain offset");
+ for (const auto &h: tip)
+ m_blockchain.push_back(h);
+ short_chain_history.clear();
+ get_short_chain_history(short_chain_history);
+ start_height = stop_height;
+ throw std::runtime_error(""); // loop again
+ }
+ 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;
@@ -2297,8 +2487,9 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// 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)
@@ -2309,10 +2500,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
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 << ")...");
+ first = true;
++try_count;
}
else
@@ -2336,14 +2528,16 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
LOG_PRINT_L1("Failed to check pending transactions");
}
+ m_first_refresh_done = true;
+
LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all()));
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
+bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok)
{
try
{
- refresh(0, blocks_fetched, received_money);
+ refresh(trusted_daemon, 0, blocks_fetched, received_money);
ok = true;
}
catch (...)
@@ -2353,7 +2547,7 @@ bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
return ok;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
+bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
{
uint32_t rpc_version;
boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
@@ -2464,7 +2658,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(); )
{
@@ -2488,6 +2681,7 @@ void wallet2::detach_blockchain(uint64_t height)
bool wallet2::deinit()
{
m_is_initialized=false;
+ unlock_keys_file();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -2506,7 +2700,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;
@@ -2634,6 +2827,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_segregation_height);
json.AddMember("segregation_height", value2, json.GetAllocator());
+ value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
+ json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
+
value2.SetUint(m_subaddress_lookahead_major);
json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
@@ -2655,10 +2851,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
keys_file_data.account_data = cipher;
+ unlock_keys_file();
std::string buf;
r = ::serialization::dump_binary(keys_file_data, buf);
r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name);
+ lock_keys_file();
return true;
}
@@ -2714,6 +2912,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregate_pre_fork_outputs = true;
m_key_reuse_mitigation2 = true;
m_segregation_height = 0;
+ m_ignore_fractional_outputs = true;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_key_on_device = false;
@@ -2840,6 +3039,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0);
m_segregation_height = field_segregation_height;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
+ m_ignore_fractional_outputs = field_ignore_fractional_outputs;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
@@ -2879,9 +3080,13 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
* can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
*
*/
-bool wallet2::verify_password(const epee::wipeable_string& password) const
+bool wallet2::verify_password(const epee::wipeable_string& password)
{
- return verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device());
+ // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
+ unlock_keys_file();
+ bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device());
+ lock_keys_file();
+ return r;
}
/*!
@@ -3028,6 +3233,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3086,6 +3292,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3181,6 +3388,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3233,6 +3441,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3273,6 +3482,13 @@ 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;
+ }
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty()) {
store();
@@ -3369,6 +3585,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!m_wallet_file.empty())
@@ -3776,12 +3993,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
boost::system::error_code e;
bool exists = boost::filesystem::exists(m_keys_file, e);
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
+ lock_keys_file();
+ THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
+ // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
+ unlock_keys_file();
if (!load_keys(m_keys_file, password))
{
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
}
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
+ lock_keys_file();
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
@@ -3794,7 +4016,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
@@ -3871,6 +4093,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (m_blockchain.empty())
{
m_blockchain.push_back(genesis_hash);
+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
}
else
{
@@ -3882,8 +4105,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);
@@ -4262,11 +4483,11 @@ void wallet2::rescan_blockchain(bool refresh)
generate_genesis(genesis);
crypto::hash genesis_hash = get_block_hash(genesis);
m_blockchain.push_back(genesis_hash);
+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
add_subaddress_account(tr("Primary account"));
- m_local_bc_height = 1;
if (refresh)
- this->refresh();
+ this->refresh(false);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
@@ -4279,7 +4500,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;
@@ -4290,7 +4511,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;
@@ -4814,11 +5035,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
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,
@@ -4882,7 +5102,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
return false;
}
- if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext))
+ if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext))
{
LOG_PRINT_L0("Failed to save file to " << signed_filename);
return false;
@@ -5231,8 +5451,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),
@@ -5436,15 +5655,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
}
// get the current full reward zone
- cryptonote::COMMAND_RPC_GET_INFO::request getinfo_req = AUTO_VAL_INIT(getinfo_req);
- cryptonote::COMMAND_RPC_GET_INFO::response getinfo_res = AUTO_VAL_INIT(getinfo_res);
- m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", getinfo_req, getinfo_res, m_http_client);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info");
- THROW_WALLET_EXCEPTION_IF(getinfo_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info");
- THROW_WALLET_EXCEPTION_IF(getinfo_res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
- const uint64_t full_reward_zone = getinfo_res.block_size_limit / 2;
+ uint64_t block_size_limit = 0;
+ const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
+ throw_on_rpc_response_error(result, "get_info");
+ const uint64_t full_reward_zone = block_size_limit / 2;
// get the last N block headers and sum the block sizes
const size_t N = 10;
@@ -5458,7 +5672,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
m_daemon_rpc_mutex.lock();
getbh_req.start_height = m_blockchain.size() - N;
getbh_req.end_height = m_blockchain.size() - 1;
- r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
@@ -5625,6 +5839,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)
@@ -5635,9 +5867,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; }
}
@@ -5645,9 +5876,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; }
}
@@ -5684,10 +5914,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; }
}
@@ -5696,10 +5924,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; }
}
@@ -5710,6 +5936,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);
@@ -5727,9 +5954,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)
@@ -5766,7 +5990,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");
}
}
@@ -5815,6 +6039,33 @@ bool wallet2::is_output_blackballed(const crypto::public_key &output) const
catch (const std::exception &e) { return false; }
}
+bool wallet2::lock_keys_file()
+{
+ if (m_keys_file_locker)
+ {
+ MDEBUG(m_keys_file << " is already locked.");
+ return false;
+ }
+ m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ return true;
+}
+
+bool wallet2::unlock_keys_file()
+{
+ if (!m_keys_file_locker)
+ {
+ MDEBUG(m_keys_file << " is already unlocked.");
+ return false;
+ }
+ m_keys_file_locker.reset();
+ return true;
+}
+
+bool wallet2::is_keys_file_locked() const
+{
+ return m_keys_file_locker->locked();
+}
+
bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
{
if (!unlocked) // don't add locked outs
@@ -5945,9 +6196,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();
@@ -5958,22 +6206,42 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
bool is_after_segregation_fork = height >= segregation_fork_height;
+ // if we have at least one rct out, get the distribution, or fall back to the previous system
+ uint64_t rct_start_height;
+ std::vector<uint64_t> rct_offsets;
+ bool has_rct = false;
+ for (size_t idx: selected_transfers)
+ if (m_transfers[idx].is_rct())
+ { has_rct = true; break; }
+ const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
+ if (has_rct_distribution)
+ {
+ // check we're clear enough of rct start, to avoid corner cases below
+ THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE,
+ error::get_output_distribution, "Not enough rct outputs");
+ }
+
// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
+ // request histogram for all outputs, except 0 if we have the rct distribution
for(size_t idx: selected_transfers)
- req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
- std::sort(req_t.amounts.begin(), req_t.amounts.end());
- auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
- req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
- req_t.unlocked = true;
- req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
- THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
+ if (!m_transfers[idx].is_rct() || !has_rct_distribution)
+ req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
+ if (!req_t.amounts.empty())
+ {
+ std::sort(req_t.amounts.begin(), req_t.amounts.end());
+ auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
+ req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
+ req_t.unlocked = true;
+ req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
+ m_daemon_rpc_mutex.lock();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
+ THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
+ THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
+ }
// if we want to segregate fake outs pre or post fork, get distribution
std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
@@ -6029,6 +6297,36 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
+ struct gamma_engine
+ {
+ typedef uint64_t result_type;
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
+ result_type operator()() { return crypto::rand<result_type>(); }
+ } engine;
+ static const double shape = 19.28/*16.94*/;
+ //static const double shape = m_testnet ? 17.02 : 17.28;
+ static const double scale = 1/1.61;
+ std::gamma_distribution<double> gamma(shape, scale);
+ auto pick_gamma = [&]()
+ {
+ double x = gamma(engine);
+ x = exp(x);
+ uint64_t block_offset = x / DIFFICULTY_TARGET_V2; // this assumes constant target over the whole rct range
+ if (block_offset >= rct_offsets.size() - 1)
+ return std::numeric_limits<uint64_t>::max(); // bad pick
+ block_offset = rct_offsets.size() - 2 - block_offset;
+ THROW_WALLET_EXCEPTION_IF(block_offset >= rct_offsets.size() - 1, error::wallet_internal_error, "Bad offset calculation");
+ THROW_WALLET_EXCEPTION_IF(rct_offsets[block_offset + 1] < rct_offsets[block_offset],
+ error::get_output_distribution, "Decreasing offsets in rct distribution: " +
+ std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " +
+ std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1]));
+ uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset];
+ if (n_rct == 0)
+ return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
+ return rct_offsets[block_offset] + crypto::rand<uint64_t>() % n_rct;
+ };
+
size_t num_selected_transfers = 0;
for(size_t idx: selected_transfers)
{
@@ -6039,6 +6337,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
size_t start = req.outputs.size();
+ bool use_histogram = amount != 0 || !has_rct_distribution;
const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
uint64_t num_outs = 0, num_recent_outs = 0;
@@ -6094,26 +6393,41 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
num_post_fork_outs = num_outs - segregation_limit[amount].first;
}
- LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
- THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
- "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
- THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
- "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
+ if (use_histogram)
+ {
+ LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
+ THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
+ "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
+ THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
+ "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
+ }
+ else
+ {
+ // the base offset of the first rct output in the first unlocked block (or the one to be if there's none)
+ num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE];
+ LOG_PRINT_L1("" << num_outs << " unlocked rct outputs");
+ THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
+ "histogram reports no unlocked rct outputs, not even ours");
+ }
- // how many fake outs to draw on a pre-fork triangular distribution
+ // how many fake outs to draw on a pre-fork distribution
size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
// how many fake outs to draw otherwise
size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
- // X% of those outs are to be taken from recent outputs
- size_t recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
- if (recent_outputs_count == 0)
- recent_outputs_count = 1; // ensure we have at least one, if possible
- if (recent_outputs_count > num_recent_outs)
- recent_outputs_count = num_recent_outs;
- if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
- --recent_outputs_count; // if the real out is recent, pick one less recent fake out
+ size_t recent_outputs_count = 0;
+ if (use_histogram)
+ {
+ // X% of those outs are to be taken from recent outputs
+ recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
+ if (recent_outputs_count == 0)
+ recent_outputs_count = 1; // ensure we have at least one, if possible
+ if (recent_outputs_count > num_recent_outs)
+ recent_outputs_count = num_recent_outs;
+ if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
+ --recent_outputs_count; // if the real out is recent, pick one less recent fake out
+ }
LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
(requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
@@ -6125,7 +6439,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,
@@ -6193,7 +6507,26 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t i;
const char *type = "";
- if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
+ if (amount == 0 && has_rct_distribution)
+ {
+ // gamma distribution
+ if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
+ {
+ do i = pick_gamma(); while (i >= segregation_limit[amount].first);
+ type = "pre-fork gamma";
+ }
+ else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
+ {
+ do i = pick_gamma(); while (i < segregation_limit[amount].first || i >= num_outs);
+ type = "post-fork gamma";
+ }
+ else
+ {
+ do i = pick_gamma(); while (i >= num_outs);
+ type = "gamma";
+ }
+ }
+ else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
{
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
@@ -6258,7 +6591,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// get the keys for those
m_daemon_rpc_mutex.lock();
- r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
+ bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
@@ -6317,7 +6650,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)
{
@@ -6535,6 +6868,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;
@@ -6790,6 +7124,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;
@@ -7417,8 +7752,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)
{
@@ -7497,12 +7835,24 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (uint32_t i : subaddr_indices)
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
+ // determine threshold for fractional amount
+ const size_t tx_size_one_ring = estimate_tx_size(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
+ const size_t tx_size_two_rings = estimate_tx_size(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
+ THROW_WALLET_EXCEPTION_IF(tx_size_one_ring > tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!");
+ const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring;
+ const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024;
+
// gather all dust and non-dust outputs belonging to specified subaddresses
size_t num_nondust_outputs = 0;
size_t num_dust_outputs = 0;
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
+ if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
+ {
+ MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
+ continue;
+ }
if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
const uint32_t index_minor = td.m_subaddr_index.minor;
@@ -7811,6 +8161,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;
@@ -7862,7 +8213,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, */
@@ -7873,7 +8224,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),
@@ -7894,7 +8245,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);
@@ -7993,7 +8344,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;
@@ -8091,6 +8445,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())
@@ -8111,10 +8466,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);
@@ -8131,7 +8486,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);
@@ -8318,6 +8673,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
{
@@ -9193,39 +9558,23 @@ uint64_t wallet2::get_daemon_blockchain_height(string &err) const
uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
{
- cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t);
- cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
- bool ok = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client);
- m_daemon_rpc_mutex.unlock();
- if (ok)
- {
- if (resp_t.status == CORE_RPC_STATUS_BUSY)
- {
- err = "daemon is busy. Please try again later.";
- }
- else if (resp_t.status != CORE_RPC_STATUS_OK)
- {
- err = resp_t.status;
- }
- else // success, cleaning up error message
- {
- err = "";
- }
- }
- else
+ err = "";
+ uint64_t target_height = 0;
+ const auto result = m_node_rpc_proxy.get_target_height(target_height);
+ if (result && *result != CORE_RPC_STATUS_OK)
{
- err = "possibly lost connection to daemon";
+ err= *result;
+ return 0;
}
- return resp_t.target_height;
+ return target_height;
}
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
@@ -9635,7 +9984,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
// was created by sweep_all, so we can't know the spent height and other detailed info.
- for(size_t i = 0; i < m_transfers.size(); ++i)
+ for(size_t i = 0; i < signed_key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
uint64_t amount = td.amount();
@@ -9858,7 +10207,7 @@ 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();
+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
}
//----------------------------------------------------------------------------------------------------
std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
@@ -10258,7 +10607,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
m_multisig_rescan_info = &info;
try
{
- refresh();
+ refresh(false);
}
catch (...) {}
m_multisig_rescan_info = NULL;
@@ -10575,15 +10924,10 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
- cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t);
- cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
- r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info");
- THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
- uint64_t full_reward_zone = resp_t.block_size_limit / 2;
+ uint64_t block_size_limit = 0;
+ const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
+ throw_on_rpc_response_error(result, "get_info");
+ uint64_t full_reward_zone = block_size_limit / 2;
std::vector<std::pair<uint64_t, uint64_t>> blocks;
for (const auto &fee_level: fee_levels)
@@ -10685,17 +11029,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);
}
}