aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/transaction_history.cpp2
-rw-r--r--src/wallet/wallet2.cpp85
-rw-r--r--src/wallet/wallet2.h30
3 files changed, 93 insertions, 24 deletions
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 2ba5f3620..63c4ea3cc 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -157,7 +157,7 @@ void TransactionHistoryImpl::refresh()
const tools::wallet2::confirmed_transfer_details &pd = i->second;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
- uint64_t fee = pd.m_amount_in - pd.m_amount_out - change;
+ uint64_t fee = pd.m_amount_in - pd.m_amount_out;
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 8ea605375..0c413546c 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -80,6 +80,8 @@ using namespace cryptonote;
#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone
#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone
+#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
+
#define KILL_IOSERVICE() \
do { \
work.reset(); \
@@ -195,7 +197,7 @@ void wallet2::set_unspent(size_t idx)
td.m_spent_height = 0;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const crypto::public_key &tx_pub_key, size_t i, bool &received, uint64_t &money_transfered, bool &error) const
+void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const
{
if (o.target.type() != typeid(txout_to_key))
{
@@ -203,7 +205,7 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp
LOG_ERROR("wrong type id in transaction out");
return;
}
- received = is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i);
+ received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i);
if(received)
{
money_transfered = o.amount; // may be 0 for ringct outputs
@@ -308,6 +310,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
std::deque<uint64_t> amount(tx.vout.size());
std::deque<rct::key> mask(tx.vout.size());
int threads = tools::get_max_concurrency();
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ crypto::key_derivation derivation;
+ generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
{
// assume coinbase isn't for us
@@ -316,7 +321,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
{
uint64_t money_transfered = 0;
bool error = false, received = false;
- check_acc_out(m_account.get_keys(), tx.vout[0], tx_pub_key, 0, received, money_transfered, error);
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, received, money_transfered, error);
if (error)
{
r = false;
@@ -326,14 +331,13 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
// this assumes that the miner tx pays a single address
if (received)
{
- wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]);
+ wallet_generate_key_image_helper(keys, tx_pub_key, 0, in_ephemeral[0], ki[0]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
outs.push_back(0);
if (money_transfered == 0)
{
- const cryptonote::account_keys& keys = m_account.get_keys();
money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, 0, mask[0]);
}
amount[0] = money_transfered;
@@ -349,14 +353,13 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
}
- const account_keys &keys = m_account.get_keys();
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
std::deque<bool> received(tx.vout.size());
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i,
+ ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
KILL_IOSERVICE();
@@ -369,14 +372,13 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
if (received[i])
{
- wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
outs.push_back(i);
if (money_transfered[i] == 0)
{
- const cryptonote::account_keys& keys = m_account.get_keys();
money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
}
tx_money_got_in_outs += money_transfered[i];
@@ -397,13 +399,12 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
}
- const account_keys &keys = m_account.get_keys();
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
std::deque<bool> received(tx.vout.size());
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i,
+ ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
KILL_IOSERVICE();
@@ -417,14 +418,13 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
if (received[i])
{
- wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
outs.push_back(i);
if (money_transfered[i] == 0)
{
- const cryptonote::account_keys& keys = m_account.get_keys();
money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
}
tx_money_got_in_outs += money_transfered[i];
@@ -439,7 +439,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
{
uint64_t money_transfered = 0;
bool error = false, received = false;
- check_acc_out(m_account.get_keys(), tx.vout[i], tx_pub_key, i, received, money_transfered, error);
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, received, money_transfered, error);
if (error)
{
r = false;
@@ -449,14 +449,13 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
{
if (received)
{
- wallet_generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
outs.push_back(i);
if (money_transfered == 0)
{
- const cryptonote::account_keys& keys = m_account.get_keys();
money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
}
amount[i] = money_transfered;
@@ -704,6 +703,17 @@ void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t heigh
else
entry.first->second.m_amount_out = spent - tx.rct_signatures.txnFee;
entry.first->second.m_change = received;
+
+ std::vector<tx_extra_field> tx_extra_fields;
+ if(parse_tx_extra(tx.extra, tx_extra_fields))
+ {
+ tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ // we do not care about failure here
+ get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, entry.first->second.m_payment_id);
+ }
+ }
}
entry.first->second.m_block_height = height;
entry.first->second.m_timestamp = ts;
@@ -2355,6 +2365,7 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
utd.m_amount_out = 0;
for (const auto &d: dests)
utd.m_amount_out += d.amount;
+ utd.m_amount_out += change_amount;
utd.m_change = change_amount;
utd.m_sent_time = time(NULL);
utd.m_tx = (const cryptonote::transaction_prefix&)tx;
@@ -2753,6 +2764,40 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const
return 1;
}
//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_dynamic_per_kb_fee_estimate()
+{
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request> req_t = AUTO_VAL_INIT(req_t);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
+
+ m_daemon_rpc_mutex.lock();
+ req_t.jsonrpc = "2.0";
+ req_t.id = epee::serialization::storage_entry(0);
+ req_t.method = "get_fee_estimate";
+ req_t.params.grace_blocks = FEE_ESTIMATE_GRACE_BLOCKS;
+ bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ CHECK_AND_ASSERT_THROW_MES(r, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_THROW_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_THROW_MES(resp_t.result.status == CORE_RPC_STATUS_OK, "Failed to get fee estimate");
+ return resp_t.result.fee;
+}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_per_kb_fee()
+{
+ bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 14);
+ if (!use_dyn_fee)
+ return FEE_PER_KB;
+ try
+ {
+ return get_dynamic_per_kb_fee_estimate();
+ }
+ catch (...)
+ {
+ LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB));
+ return FEE_PER_KB;
+ }
+}
+//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
// this function will make multiple calls to wallet2::transfer if multiple
@@ -2762,7 +2807,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon);
const bool use_new_fee = use_fork_rules(3, -720 * 14);
- const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD;
+ const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
// failsafe split attempt counter
@@ -3483,7 +3528,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const bool use_rct = use_fork_rules(4, 0);
const bool use_new_fee = use_fork_rules(3, -720 * 14);
- const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD;
+ const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
// throw if attempting a transaction with no destinations
@@ -3765,7 +3810,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool use_new_fee = use_fork_rules(3, -720 * 14);
- const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD;
+ const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@@ -4071,7 +4116,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
const bool use_new_fee = use_fork_rules(3, -720 * 14);
- const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD;
+ const uint64_t fee_per_kb = get_per_kb_fee();
// may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 6cd288ac1..04c4fc3a5 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -515,11 +515,13 @@ namespace tools
void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
- void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, bool &received, uint64_t &money_transfered, bool &error) const;
+ void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_tranaction_size_limit();
std::vector<uint64_t> get_unspent_amounts_vector();
uint64_t get_fee_multiplier(uint32_t priority, bool use_new_fee) const;
+ uint64_t get_dynamic_per_kb_fee_estimate();
+ uint64_t get_per_kb_fee();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_prefered_rct_inputs(uint64_t needed_money) const;
void set_spent(size_t idx, uint64_t height);
@@ -570,8 +572,8 @@ namespace tools
BOOST_CLASS_VERSION(tools::wallet2, 14)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 4)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
-BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 5)
-BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
+BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6)
+BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 3)
namespace boost
{
@@ -675,6 +677,14 @@ namespace boost
return;
a & x.m_amount_in;
a & x.m_amount_out;
+ if (ver < 6)
+ {
+ // v<6 may not have change accumulated in m_amount_out, which is a pain,
+ // as it's readily understood to be sum of outputs.
+ // We convert it to include change from v6
+ if (!typename Archive::is_saving() && x.m_change != (uint64_t)-1)
+ x.m_amount_out += x.m_change;
+ }
}
template <class Archive>
@@ -691,6 +701,20 @@ namespace boost
if (ver < 2)
return;
a & x.m_timestamp;
+ if (ver < 3)
+ {
+ // v<3 may not have change accumulated in m_amount_out, which is a pain,
+ // as it's readily understood to be sum of outputs. Whether it got added
+ // or not depends on whether it came from a unconfirmed_transfer_details
+ // (not included) or not (included). We can't reliably tell here, so we
+ // check whether either yields a "negative" fee, or use the other if so.
+ // We convert it to include change from v3
+ if (!typename Archive::is_saving() && x.m_change != (uint64_t)-1)
+ {
+ if (x.m_amount_in > (x.m_amount_out + x.m_change))
+ x.m_amount_out += x.m_change;
+ }
+ }
}
template <class Archive>