aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/simplewallet/simplewallet.cpp32
-rw-r--r--src/wallet/CMakeLists.txt21
-rw-r--r--src/wallet/api/wallet.cpp148
-rw-r--r--src/wallet/api/wallet.h30
-rw-r--r--src/wallet/api/wallet_manager.cpp5
-rw-r--r--src/wallet/wallet2.cpp223
-rw-r--r--src/wallet/wallet2.h13
-rw-r--r--src/wallet/wallet2_api.h77
-rw-r--r--src/wallet/wallet_rpc_server.cpp27
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h4
10 files changed, 496 insertions, 84 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 10dc9d509..eb763b0cd 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -3083,6 +3083,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
bool out = true;
bool pending = true;
bool failed = true;
+ bool pool = true;
uint64_t min_height = 0;
uint64_t max_height = (uint64_t)-1;
@@ -3100,7 +3101,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
local_args.erase(local_args.begin());
}
else if (local_args[0] == "out" || local_args[0] == "outgoing") {
- in = false;
+ in = pool = false;
local_args.erase(local_args.begin());
}
else if (local_args[0] == "pending") {
@@ -3108,7 +3109,11 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
local_args.erase(local_args.begin());
}
else if (local_args[0] == "failed") {
- in = out = pending = false;
+ in = out = pending = pool = false;
+ local_args.erase(local_args.begin());
+ }
+ else if (local_args[0] == "pool") {
+ in = out = pending = failed = false;
local_args.erase(local_args.begin());
}
else if (local_args[0] == "all" || local_args[0] == "both") {
@@ -3183,6 +3188,27 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
((unsigned long long)i->first) % (i->second.first ? tr("in") : tr("out")) % i->second.second;
}
+ if (pool) {
+ try
+ {
+ m_wallet->update_pool_state();
+ std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ m_wallet->get_unconfirmed_payments(payments);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second;
+ std::string payment_id = string_tools::pod_to_hex(i->first);
+ if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
+ payment_id = payment_id.substr(0,16);
+ std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
+ message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %s %s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % "-" % note).str();
+ }
+ }
+ catch (...)
+ {
+ fail_msg_writer() << "Failed to get pool state";
+ }
+ }
+
// print unconfirmed last
if (pending || failed) {
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
@@ -3238,6 +3264,8 @@ bool simple_wallet::run()
// check and display warning, but go on anyway
try_connect_to_daemon();
+ refresh_main(0, false);
+
std::string addr_start = m_wallet->get_account().get_public_address_str(m_wallet->testnet()).substr(0, 6);
m_auto_refresh_run = m_wallet->auto_refresh();
if (m_auto_refresh_run)
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 0b8fe4cb1..086758c39 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -75,13 +75,26 @@ target_link_libraries(wallet
${Boost_REGEX_LIBRARY}
${EXTRA_LIBRARIES})
+
+# in case of static build, randon.c.obj from UNBOUND_LIBARY conflicts with the same file from 'crypto'
+# and in case of dynamic build, merge_static_libs called with ${UNBOUND_LIBRARY} will fail
+if (STATIC)
+ set(libs_to_merge wallet cryptonote_core mnemonics common crypto)
+ # hack - repack libunbound into another static lib - there's conflicting object file "random.c.obj"
+ merge_static_libs(wallet_merged "${libs_to_merge}")
+ merge_static_libs(wallet_merged2 "${UNBOUND_LIBRARY}")
+ install(TARGETS wallet_merged wallet_merged2
+ ARCHIVE DESTINATION lib)
+else (STATIC)
+ set(libs_to_merge wallet cryptonote_core mnemonics common crypto ${UNBOUND_LIBRARY} )
+ merge_static_libs(wallet_merged "${libs_to_merge}")
+ install(TARGETS wallet_merged
+ ARCHIVE DESTINATION lib)
+endif (STATIC)
-set(libs_to_merge wallet cryptonote_core mnemonics common crypto)
#MERGE_STATIC_LIBS(wallet_merged wallet_merged "${libs_to_merge}")
-merge_static_libs(wallet_merged "${libs_to_merge}")
-install(TARGETS wallet_merged
- ARCHIVE DESTINATION lib)
+
install(FILES ${wallet_api_headers}
DESTINATION include/wallet)
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index be379cb99..67d2ebecb 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -46,6 +46,7 @@ namespace Bitmonero {
namespace {
// copy-pasted from simplewallet
static const size_t DEFAULT_MIXIN = 4;
+ static const int DEFAULT_REFRESH_INTERVAL_SECONDS = 10;
}
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
@@ -89,6 +90,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", amount: " << print_money(amount));
if (m_listener) {
m_listener->moneyReceived(tx_hash, amount);
+ m_listener->updated();
}
}
@@ -103,6 +105,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", amount: " << print_money(amount));
if (m_listener) {
m_listener->moneySpent(tx_hash, amount);
+ m_listener->updated();
}
}
@@ -118,6 +121,7 @@ Wallet::~Wallet() {}
WalletListener::~WalletListener() {}
+
string Wallet::displayAmount(uint64_t amount)
{
return cryptonote::print_money(amount);
@@ -144,6 +148,12 @@ std::string Wallet::genPaymentId()
}
+bool Wallet::paymentIdValid(const string &paiment_id)
+{
+ crypto::hash8 pid;
+ return tools::wallet2::parse_short_payment_id(paiment_id, pid);
+}
+
///////////////////////// WalletImpl implementation ////////////////////////
WalletImpl::WalletImpl(bool testnet)
@@ -153,13 +163,22 @@ WalletImpl::WalletImpl(bool testnet)
m_wallet = new tools::wallet2(testnet);
m_history = new TransactionHistoryImpl(this);
m_wallet2Callback = new Wallet2CallbackImpl;
+ m_wallet->callback(m_wallet2Callback);
+ m_refreshThreadDone = false;
+ m_refreshEnabled = false;
+ m_refreshIntervalSeconds = DEFAULT_REFRESH_INTERVAL_SECONDS;
+ m_refreshThread = std::thread([this] () {
+ this->refreshThreadFunc();
+ });
+
}
WalletImpl::~WalletImpl()
{
- delete m_wallet2Callback;
+ stopRefresh();
delete m_history;
delete m_wallet;
+ delete m_wallet2Callback;
}
bool WalletImpl::create(const std::string &path, const std::string &password, const std::string &language)
@@ -251,8 +270,12 @@ bool WalletImpl::close()
clearStatus();
bool result = false;
try {
+ // LOG_PRINT_L0("Calling wallet::store...");
m_wallet->store();
+ // LOG_PRINT_L0("wallet::store done");
+ // LOG_PRINT_L0("Calling wallet::stop...");
m_wallet->stop();
+ // LOG_PRINT_L0("wallet::stop done");
result = true;
} catch (const std::exception &e) {
m_status = Status_Error;
@@ -348,21 +371,30 @@ string WalletImpl::keysFilename() const
bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit)
{
clearStatus();
- try {
- m_wallet->init(daemon_address, upper_transaction_size_limit);
- if (Utils::isAddressLocal(daemon_address)) {
- this->setTrustedDaemon(true);
- }
- } catch (const std::exception &e) {
- LOG_ERROR("Error initializing wallet: " << e.what());
- m_status = Status_Error;
- m_errorString = e.what();
+ m_wallet->init(daemon_address, upper_transaction_size_limit);
+ if (Utils::isAddressLocal(daemon_address)) {
+ this->setTrustedDaemon(true);
}
+ bool result = this->refresh();
+ // enabling background refresh thread
+ startRefresh();
+ return result;
- return m_status == Status_Ok;
}
+void WalletImpl::initAsync(const string &daemon_address, uint64_t upper_transaction_size_limit)
+{
+ clearStatus();
+ m_wallet->init(daemon_address, upper_transaction_size_limit);
+ if (Utils::isAddressLocal(daemon_address)) {
+ this->setTrustedDaemon(true);
+ }
+ startRefresh();
+}
+
+
+
uint64_t WalletImpl::balance() const
{
return m_wallet->balance();
@@ -377,15 +409,17 @@ uint64_t WalletImpl::unlockedBalance() const
bool WalletImpl::refresh()
{
clearStatus();
- try {
- m_wallet->refresh();
- } catch (const std::exception &e) {
- m_status = Status_Error;
- m_errorString = e.what();
- }
+ doRefresh();
return m_status == Status_Ok;
}
+void WalletImpl::refreshAsync()
+{
+ LOG_PRINT_L3(__FUNCTION__ << ": Refreshing asyncronously..");
+ clearStatus();
+ m_refreshCV.notify_one();
+}
+
// TODO:
// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param)
// 2 - check / design how "Transaction" can be single interface
@@ -396,7 +430,9 @@ bool WalletImpl::refresh()
// - unconfirmed_transfer_details;
// - confirmed_transfer_details)
-PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, uint64_t amount, uint32_t mixin_count)
+PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, uint64_t amount, uint32_t mixin_count,
+ PendingTransaction::Priority priority)
+
{
clearStatus();
vector<cryptonote::tx_destination_entry> dsts;
@@ -458,8 +494,10 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
//std::vector<tools::wallet2::pending_tx> ptx_vector;
try {
+ // priority called "fee_multiplied in terms of underlying wallet interface
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
- 0 /* unused fee arg*/, extra, m_trustedDaemon);
+ static_cast<uint64_t>(priority),
+ extra, m_trustedDaemon);
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
@@ -564,10 +602,17 @@ bool WalletImpl::connectToDaemon()
m_status = result ? Status_Ok : Status_Error;
if (!result) {
m_errorString = "Error connecting to daemon at " + m_wallet->get_daemon_address();
+ } else {
+ // start refreshing here
}
return result;
}
+bool WalletImpl::connected() const
+{
+ return m_wallet->check_connection();
+}
+
void WalletImpl::setTrustedDaemon(bool arg)
{
m_trustedDaemon = arg;
@@ -584,5 +629,70 @@ void WalletImpl::clearStatus()
m_errorString.clear();
}
+void WalletImpl::refreshThreadFunc()
+{
+ LOG_PRINT_L3(__FUNCTION__ << ": starting refresh thread");
+
+ while (true) {
+ std::unique_lock<std::mutex> lock(m_refreshMutex);
+ if (m_refreshThreadDone) {
+ break;
+ }
+ LOG_PRINT_L3(__FUNCTION__ << ": waiting for refresh...");
+ m_refreshCV.wait_for(lock, std::chrono::seconds(m_refreshIntervalSeconds));
+ LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
+ LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << m_status);
+ if (m_refreshEnabled /*&& m_status == Status_Ok*/) {
+ LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
+ doRefresh();
+ }
+ }
+ LOG_PRINT_L3(__FUNCTION__ << ": refresh thread stopped");
+}
+
+void WalletImpl::doRefresh()
+{
+ // synchronizing async and sync refresh calls
+ std::lock_guard<std::mutex> guarg(m_refreshMutex2);
+ try {
+ m_wallet->refresh();
+ } catch (const std::exception &e) {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+ if (m_wallet2Callback->getListener()) {
+ m_wallet2Callback->getListener()->refreshed();
+ }
+}
+
+
+void WalletImpl::startRefresh()
+{
+ if (!m_refreshEnabled) {
+ m_refreshEnabled = true;
+ m_refreshCV.notify_one();
+ }
+}
+
+
+
+void WalletImpl::stopRefresh()
+{
+ if (!m_refreshThreadDone) {
+ m_refreshEnabled = false;
+ m_refreshThreadDone = true;
+ m_refreshThread.join();
+ }
+}
+
+void WalletImpl::pauseRefresh()
+{
+ // TODO synchronize access
+ if (!m_refreshThreadDone) {
+ m_refreshEnabled = false;
+ }
+}
+
} // namespace
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 164aede1a..9a290e0bc 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -35,6 +35,9 @@
#include "wallet/wallet2.h"
#include <string>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
namespace Bitmonero {
@@ -65,14 +68,19 @@ public:
std::string filename() const;
std::string keysFilename() const;
bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit);
+ void initAsync(const std::string &daemon_address, uint64_t upper_transaction_size_limit);
bool connectToDaemon();
+ bool connected() const;
void setTrustedDaemon(bool arg);
bool trustedDaemon() const;
uint64_t balance() const;
uint64_t unlockedBalance() const;
bool refresh();
+ void refreshAsync();
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
- uint64_t amount, uint32_t mixin_count);
+ uint64_t amount, uint32_t mixin_count,
+ PendingTransaction::Priority priority = PendingTransaction::Priority_Low);
+
virtual void disposeTransaction(PendingTransaction * t);
virtual TransactionHistory * history() const;
virtual void setListener(WalletListener * l);
@@ -81,19 +89,37 @@ public:
private:
void clearStatus();
+ void refreshThreadFunc();
+ void doRefresh();
+ void startRefresh();
+ void stopRefresh();
+ void pauseRefresh();
private:
friend class PendingTransactionImpl;
friend class TransactionHistoryImpl;
tools::wallet2 * m_wallet;
- int m_status;
+ std::atomic<int> m_status;
std::string m_errorString;
std::string m_password;
TransactionHistoryImpl * m_history;
bool m_trustedDaemon;
WalletListener * m_walletListener;
Wallet2CallbackImpl * m_wallet2Callback;
+
+ // multi-threaded refresh stuff
+ std::atomic<bool> m_refreshEnabled;
+ std::atomic<bool> m_refreshThreadDone;
+ std::atomic<int> m_refreshIntervalSeconds;
+ // synchronizing refresh loop;
+ std::mutex m_refreshMutex;
+
+ // synchronizing sync and async refresh
+ std::mutex m_refreshMutex2;
+ std::condition_variable m_refreshCV;
+ std::thread m_refreshThread;
+
};
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index bf072ccca..ca83806ff 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -137,6 +137,11 @@ WalletManager *WalletManagerFactory::getWalletManager()
return g_walletManager;
}
+void WalletManagerFactory::setLogLevel(int level)
+{
+ epee::log_space::log_singletone::get_set_log_detalisation_level(true, level);
+}
+
}
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index d18681f36..201f0a1f4 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -186,7 +186,7 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp
error = false;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx)
+void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
{
if (!miner_tx)
process_unconfirmed(tx, height);
@@ -318,16 +318,19 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
//usually we have only one transfer for user in transaction
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res);
- req.txid = get_transaction_hash(tx);
- m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status);
- THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error,
- "transactions outputs size=" + std::to_string(tx.vout.size()) +
- " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));
+ if (!pool)
+ {
+ req.txid = get_transaction_hash(tx);
+ m_daemon_rpc_mutex.lock();
+ bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status);
+ THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error,
+ "transactions outputs size=" + std::to_string(tx.vout.size()) +
+ " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));
+ }
BOOST_FOREACH(size_t o, outs)
{
@@ -347,18 +350,21 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
+ ", m_transfers.size() is " + boost::lexical_cast<std::string>(m_transfers.size()));
if (kit == m_key_images.end())
{
- m_transfers.push_back(boost::value_initialized<transfer_details>());
- transfer_details& td = m_transfers.back();
- td.m_block_height = height;
- td.m_internal_output_index = o;
- td.m_global_output_index = res.o_indexes[o];
- td.m_tx = tx;
- td.m_key_image = ki;
- td.m_spent = false;
- m_key_images[td.m_key_image] = m_transfers.size()-1;
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
- if (0 != m_callback)
- m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
+ if (!pool)
+ {
+ m_transfers.push_back(boost::value_initialized<transfer_details>());
+ transfer_details& td = m_transfers.back();
+ td.m_block_height = height;
+ td.m_internal_output_index = o;
+ td.m_global_output_index = res.o_indexes[o];
+ td.m_tx = tx;
+ td.m_key_image = ki;
+ td.m_spent = false;
+ m_key_images[td.m_key_image] = m_transfers.size()-1;
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
+ if (0 != m_callback)
+ m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
+ }
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
{
@@ -375,17 +381,20 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
// The new larger output replaced a previous smaller one
tx_money_got_in_outs -= tx.vout[o].amount;
- transfer_details &td = m_transfers[kit->second];
- td.m_block_height = height;
- td.m_internal_output_index = o;
- td.m_global_output_index = res.o_indexes[o];
- td.m_tx = tx;
- THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki, error::wallet_internal_error, "Inconsistent key images");
- THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
-
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
- if (0 != m_callback)
- m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
+ if (!pool)
+ {
+ transfer_details &td = m_transfers[kit->second];
+ td.m_block_height = height;
+ td.m_internal_output_index = o;
+ td.m_global_output_index = res.o_indexes[o];
+ td.m_tx = tx;
+ THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki, error::wallet_internal_error, "Inconsistent key images");
+ THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
+
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
+ if (0 != m_callback)
+ m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
+ }
}
}
}
@@ -462,8 +471,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
payment.m_timestamp = ts;
- m_payments.emplace(payment_id, payment);
- LOG_PRINT_L2("Payment found: " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
+ if (pool)
+ m_unconfirmed_payments.emplace(payment_id, payment);
+ else
+ m_payments.emplace(payment_id, payment);
+ LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
}
}
//----------------------------------------------------------------------------------------------------
@@ -506,7 +518,7 @@ 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(b.miner_tx, height, b.timestamp, true);
+ process_new_transaction(b.miner_tx, height, b.timestamp, true, false);
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
@@ -515,7 +527,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
cryptonote::transaction tx;
bool r = parse_and_validate_tx_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(tx, height, b.timestamp, false);
+ process_new_transaction(tx, height, b.timestamp, false, false);
}
TIME_MEASURE_FINISH(txs_handle_time);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
@@ -743,20 +755,16 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::check_pending_txes()
+void wallet2::update_pool_state()
{
- // if we don't have any pending txes, we don't need to check anything
- if (m_unconfirmed_txs.empty())
- return;
-
- // we have at least one pending tx, so get the pool state
+ // get the pool state
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res;
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/get_transaction_pool", req, res, m_http_client, 200000);
m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "check_pending_txes");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
// remove any pending tx that's not in the pool
@@ -793,6 +801,120 @@ void wallet2::check_pending_txes()
}
}
}
+
+ // remove pool txes to us that aren't in the pool anymore
+ std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
+ while (uit != m_unconfirmed_payments.end())
+ {
+ const std::string txid = string_tools::pod_to_hex(uit->first);
+ bool found = false;
+ for (auto it2: res.transactions)
+ {
+ if (it2.id_hash == txid)
+ {
+ found = true;
+ break;
+ }
+ }
+ auto pit = uit++;
+ if (!found)
+ {
+ m_unconfirmed_payments.erase(pit);
+ }
+ }
+
+ // add new pool txes to us
+ for (auto it: res.transactions)
+ {
+ cryptonote::blobdata txid_data;
+ if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data))
+ {
+ const crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
+ if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end())
+ {
+ LOG_PRINT_L1("Found new pool tx: " << txid);
+ bool found = false;
+ for (const auto &i: m_unconfirmed_txs)
+ {
+ if (i.first == txid)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ // not one of those we sent ourselves
+ cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
+ cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(it.id_hash);
+ req.decode_as_json = false;
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/gettransactions", req, res, m_http_client, 200000);
+ m_daemon_rpc_mutex.unlock();
+ if (r && res.status == CORE_RPC_STATUS_OK)
+ {
+ if (res.txs.size() == 1)
+ {
+ // might have just been put in a block
+ if (res.txs[0].in_pool)
+ {
+ cryptonote::transaction tx;
+ cryptonote::blobdata bd;
+ crypto::hash tx_hash, tx_prefix_hash;
+ if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd))
+ {
+ if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
+ {
+ if (tx_hash == txid)
+ {
+ process_new_transaction(tx, 0, time(NULL), false, true);
+ }
+ else
+ {
+ LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool");
+ }
+ }
+ else
+ {
+ LOG_PRINT_L0("failed to validate transaction from daemon");
+ }
+ }
+ else
+ {
+ LOG_PRINT_L0("Failed to parse tx " << txid);
+ }
+ }
+ else
+ {
+ LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more");
+ }
+ }
+ else
+ {
+ LOG_PRINT_L0("Expected 1 tx, got " << res.txs.size());
+ }
+ }
+ else
+ {
+ LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status);
+ }
+ }
+ else
+ {
+ LOG_PRINT_L1("We sent that one");
+ }
+ }
+ else
+ {
+ LOG_PRINT_L1("Already saw that one");
+ }
+ }
+ else
+ {
+ LOG_PRINT_L0("Failed to parse txid");
+ }
+ }
}
//----------------------------------------------------------------------------------------------------
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
@@ -931,7 +1053,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
try
{
- check_pending_txes();
+ update_pool_state();
}
catch (...)
{
@@ -1733,6 +1855,13 @@ void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wall
}
}
//----------------------------------------------------------------------------------------------------
+void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments) const
+{
+ for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
+ unconfirmed_payments.push_back(*i);
+ }
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::rescan_spent()
{
std::vector<std::string> key_images;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index d004b7da9..de3580777 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -303,6 +303,8 @@ namespace tools
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
uint64_t min_height, uint64_t max_height = (uint64_t)-1) const;
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments) const;
+ void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments) const;
+
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
void rescan_spent();
void rescan_blockchain(bool refresh = true);
@@ -334,6 +336,9 @@ namespace tools
if(ver < 12)
return;
a & m_tx_notes;
+ if(ver < 13)
+ return;
+ a & m_unconfirmed_payments;
}
/*!
@@ -389,6 +394,8 @@ namespace tools
std::string sign(const std::string &data) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
+ void update_pool_state();
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -404,7 +411,7 @@ namespace tools
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const std::string& password);
- void process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx);
+ void process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height);
void detach_blockchain(uint64_t height);
void get_short_chain_history(std::list<crypto::hash>& ids) const;
@@ -428,7 +435,6 @@ namespace tools
void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, 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();
- void check_pending_txes();
std::vector<uint64_t> get_unspent_amounts_vector();
uint64_t sanitize_fee_multiplier(uint64_t fee_multiplier) const;
@@ -441,6 +447,7 @@ namespace tools
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
+ std::unordered_map<crypto::hash, payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
transfer_container m_transfers;
@@ -469,7 +476,7 @@ namespace tools
uint64_t m_refresh_from_block_height;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 12)
+BOOST_CLASS_VERSION(tools::wallet2, 13)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 3)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 66987e4c5..2c5836573 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -50,6 +50,14 @@ struct PendingTransaction
Status_Ok,
Status_Error
};
+
+ enum Priority {
+ Priority_Low = 1,
+ Priority_Medium = 2,
+ Priority_High = 3,
+ Priority_Last
+ };
+
virtual ~PendingTransaction() = 0;
virtual int status() const = 0;
virtual std::string errorString() const = 0;
@@ -108,7 +116,10 @@ struct WalletListener
virtual ~WalletListener() = 0;
virtual void moneySpent(const std::string &txId, uint64_t amount) = 0;
virtual void moneyReceived(const std::string &txId, uint64_t amount) = 0;
- // TODO: on_skip_transaction;
+ // generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet;
+ virtual void updated() = 0;
+ // called when wallet refreshed by background thread or explicitly called be calling "refresh" synchronously
+ virtual void refreshed() = 0;
};
@@ -163,9 +174,38 @@ struct Wallet
* \return
*/
virtual std::string keysFilename() const = 0;
-
+ /*!
+ * \brief init - initializes wallet with daemon connection params. implicitly connects to the daemon
+ * and refreshes the wallet. "refreshed" callback will be invoked. if daemon_address is
+ * local address, "trusted daemon" will be set to true forcibly
+ *
+ * \param daemon_address - daemon address in "hostname:port" format
+ * \param upper_transaction_size_limit
+ * \return - true if initialized and refreshed successfully
+ */
virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0;
+
+ /*!
+ * \brief init - initalizes wallet asynchronously. logic is the same as "init" but returns immediately.
+ * "refreshed" callback will be invoked.
+ *
+ * \param daemon_address - daemon address in "hostname:port" format
+ * \param upper_transaction_size_limit
+ * \return - true if initialized and refreshed successfully
+ */
+ virtual void initAsync(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0;
+
+ /**
+ * @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed
+ * @return
+ */
virtual bool connectToDaemon() = 0;
+
+ /**
+ * @brief connected - checks if the wallet connected to the daemon
+ * @return - true if connected
+ */
+ virtual bool connected() const = 0;
virtual void setTrustedDaemon(bool arg) = 0;
virtual bool trustedDaemon() const = 0;
virtual uint64_t balance() const = 0;
@@ -175,23 +215,36 @@ struct Wallet
static uint64_t amountFromString(const std::string &amount);
static uint64_t amountFromDouble(double amount);
static std::string genPaymentId();
+ static bool paymentIdValid(const std::string &paiment_id);
- // TODO?
- // virtual uint64_t unlockedDustBalance() const = 0;
+ /**
+ * @brief refresh - refreshes the wallet, updating transactions from daemon
+ * @return - true if refreshed successfully;
+ */
virtual bool refresh() = 0;
+ /**
+ * @brief refreshAsync - refreshes wallet asynchronously.
+ */
+ virtual void refreshAsync() = 0;
/*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
* \param dst_addr destination address as string
* \param payment_id optional payment_id, can be empty string
* \param amount amount
* \param mixin_count mixin count. if 0 passed, wallet will use default value
+ * \param priority
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned
*/
virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
- uint64_t amount, uint32_t mixin_count) = 0;
+ uint64_t amount, uint32_t mixin_count,
+ PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0;
+ /*!
+ * \brief disposeTransaction - destroys transaction object
+ * \param t - pointer to the "PendingTransaction" object. Pointer is not valid after function returned;
+ */
virtual void disposeTransaction(PendingTransaction * t) = 0;
virtual TransactionHistory * history() const = 0;
virtual void setListener(WalletListener *) = 0;
@@ -271,8 +324,22 @@ struct WalletManager
struct WalletManagerFactory
{
+ // logging levels for underlying library
+ enum LogLevel {
+ LogLevel_Silent = -1,
+ LogLevel_0 = 0,
+ LogLevel_1 = 1,
+ LogLevel_2 = 2,
+ LogLevel_3 = 3,
+ LogLevel_4 = 4,
+ LogLevel_Min = LogLevel_Silent,
+ LogLevel_Max = LogLevel_4
+ };
+
static WalletManager * getWalletManager();
+ static void setLogLevel(int level);
};
+
}
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 5c42e1c53..dbfb880a1 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -918,8 +918,8 @@ namespace tools
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
m_wallet.get_payments_out(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- res.in.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
- wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = res.in.back();
+ res.out.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
+ wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = res.out.back();
const tools::wallet2::confirmed_transfer_details &pd = i->second;
entry.txid = string_tools::pod_to_hex(i->first);
@@ -969,6 +969,29 @@ namespace tools
}
}
+ if (req.pool)
+ {
+ m_wallet.update_pool_state();
+
+ std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ m_wallet.get_unconfirmed_payments(payments);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ res.pool.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
+ wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = res.pool.back();
+ const tools::wallet2::payment_details &pd = i->second;
+
+ entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
+ entry.payment_id = string_tools::pod_to_hex(i->first);
+ if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
+ entry.payment_id = entry.payment_id.substr(0,16);
+ entry.height = 0;
+ entry.timestamp = pd.m_timestamp;
+ entry.amount = pd.m_amount;
+ entry.fee = 0; // TODO
+ entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ }
+ }
+
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 1404340da..f4eefcd1a 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -496,6 +496,7 @@ namespace wallet_rpc
bool out;
bool pending;
bool failed;
+ bool pool;
bool filter_by_height;
uint64_t min_height;
@@ -506,6 +507,7 @@ namespace wallet_rpc
KV_SERIALIZE(out);
KV_SERIALIZE(pending);
KV_SERIALIZE(failed);
+ KV_SERIALIZE(pool);
KV_SERIALIZE(filter_by_height);
KV_SERIALIZE(min_height);
KV_SERIALIZE(max_height);
@@ -541,12 +543,14 @@ namespace wallet_rpc
std::list<entry> out;
std::list<entry> pending;
std::list<entry> failed;
+ std::list<entry> pool;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(in);
KV_SERIALIZE(out);
KV_SERIALIZE(pending);
KV_SERIALIZE(failed);
+ KV_SERIALIZE(pool);
END_KV_SERIALIZE_MAP()
};
};