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/api/wallet_manager.cpp153
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/wallet2.cpp85
-rw-r--r--src/wallet/wallet2.h30
-rw-r--r--src/wallet/wallet2_api.h13
6 files changed, 260 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/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index d2395ace1..2d1c44d0e 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -31,6 +31,8 @@
#include "wallet_manager.h"
#include "wallet.h"
+#include "common_defines.h"
+#include "net/http_client.h"
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
@@ -133,6 +135,157 @@ void WalletManagerImpl::setDaemonHost(const std::string &hostname)
}
+bool WalletManagerImpl::checkPayment(const std::string &address_text, const std::string &txid_text, const std::string &txkey_text, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const
+{
+ error = "";
+ cryptonote::blobdata txid_data;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data))
+ {
+ error = tr("failed to parse txid");
+ return false;
+ }
+ crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
+
+ if (txkey_text.size() < 64 || txkey_text.size() % 64)
+ {
+ error = tr("failed to parse tx key");
+ return false;
+ }
+ crypto::secret_key tx_key;
+ cryptonote::blobdata tx_key_data;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data))
+ {
+ error = tr("failed to parse tx key");
+ return false;
+ }
+ tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
+
+ bool testnet = address_text[0] != '4';
+ cryptonote::account_public_address address;
+ bool has_payment_id;
+ crypto::hash8 payment_id;
+ if(!cryptonote::get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_text))
+ {
+ error = tr("failed to parse address");
+ return false;
+ }
+
+ cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
+ cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ epee::net_utils::http::http_simple_client http_client;
+ if (!epee::net_utils::invoke_http_json_remote_command2(daemon_address + "/gettransactions", req, res, http_client) ||
+ (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
+ {
+ error = tr("failed to get transaction from daemon");
+ return false;
+ }
+ cryptonote::blobdata tx_data;
+ bool ok;
+ if (res.txs.size() == 1)
+ ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ if (!ok)
+ {
+ error = tr("failed to parse transaction from daemon");
+ return false;
+ }
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash))
+ {
+ error = tr("failed to validate transaction from daemon");
+ return false;
+ }
+ if (tx_hash != txid)
+ {
+ error = tr("failed to get the right transaction from daemon");
+ return false;
+ }
+
+ crypto::key_derivation derivation;
+ if (!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation))
+ {
+ error = tr("failed to generate key derivation from supplied parameters");
+ return false;
+ }
+
+ received = 0;
+ try {
+ for (size_t n = 0; n < tx.vout.size(); ++n)
+ {
+ if (typeid(cryptonote::txout_to_key) != tx.vout[n].target.type())
+ continue;
+ const cryptonote::txout_to_key tx_out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[n].target);
+ crypto::public_key pubkey;
+ derive_public_key(derivation, n, address.m_spend_public_key, pubkey);
+ if (pubkey == tx_out_to_key.key)
+ {
+ uint64_t amount;
+ if (tx.version == 1)
+ {
+ amount = tx.vout[n].amount;
+ }
+ else
+ {
+ try
+ {
+ rct::key Ctmp;
+ //rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
+ crypto::key_derivation derivation;
+ bool r = crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation);
+ if (!r)
+ {
+ LOG_ERROR("Failed to generate key derivation to decode rct output " << n);
+ amount = 0;
+ }
+ else
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(derivation, n, scalar1);
+ rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
+ rct::key C = tx.rct_signatures.outPk[n].mask;
+ rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
+ if (rct::equalKeys(C, Ctmp))
+ amount = rct::h2d(ecdh_info.amount);
+ else
+ amount = 0;
+ }
+ }
+ catch (...) { amount = 0; }
+ }
+ received += amount;
+ }
+ }
+ }
+ catch(const std::exception &e)
+ {
+ LOG_ERROR("error: " << e.what());
+ error = std::string(tr("error: ")) + e.what();
+ return false;
+ }
+
+ if (received > 0)
+ {
+ LOG_PRINT_L1(get_account_address_as_str(testnet, address) << " " << tr("received") << " " << cryptonote::print_money(received) << " " << tr("in txid") << " " << txid);
+ }
+ else
+ {
+ LOG_PRINT_L1(get_account_address_as_str(testnet, address) << " " << tr("received nothing in txid") << " " << txid);
+ }
+ if (res.txs.front().in_pool)
+ {
+ height = 0;
+ }
+ else
+ {
+ height = res.txs.front().block_height;
+ }
+
+ return true;
+}
///////////////////// WalletManagerFactory implementation //////////////////////
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 2e932a2a1..489abe764 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -46,6 +46,7 @@ public:
std::vector<std::string> findWallets(const std::string &path);
std::string errorString() const;
void setDaemonHost(const std::string &hostname);
+ bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const;
private:
WalletManagerImpl() {}
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>
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index bf86167df..bdea8b22c 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -415,6 +415,19 @@ struct WalletManager
*/
virtual std::vector<std::string> findWallets(const std::string &path) = 0;
+ /*!
+ * \brief checkPayment - checks a payment was made using a txkey
+ * \param address - the address the payment was sent to
+ * \param txid - the transaction id for that payment
+ * \param txkey - the transaction's secret key
+ * \param daemon_address - the address (host and port) to the daemon to request transaction data
+ * \param received - if succesful, will hold the amount of monero received
+ * \param height - if succesful, will hold the height of the transaction (0 if only in the pool)
+ * \param error - if unsuccesful, will hold an error string with more information about the error
+ * \return - true is succesful, false otherwise
+ */
+ virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
+
//! returns verbose error string regarding last error;
virtual std::string errorString() const = 0;