diff options
author | Riccardo Spagni <ric@spagni.net> | 2016-07-23 09:17:21 +0200 |
---|---|---|
committer | Riccardo Spagni <ric@spagni.net> | 2016-07-23 09:17:21 +0200 |
commit | b582764bd659372d99144a62be4ded45cc47be2d (patch) | |
tree | ad3ca78a09384e33cee7de4fe5159b0bbde2be6e /src | |
parent | Merge pull request #910 (diff) | |
parent | refreshing wallet even if error happened (diff) | |
download | monero-b582764bd659372d99144a62be4ded45cc47be2d.tar.xz |
Merge pull request #915
d7597c5 refreshing wallet even if error happened (Ilya Kitaev)
6d32a3d wallet_api: async init, Wallet::connected status, log level (Ilya Kitaev)
193d251 libwallet_api cmake: conditionally creating libwallet_merged2 only for STATIC build (Ilya Kitaev)
10c06dd wallet_api: segfault on refresh fixed (Ilya Kitaev)
9d2cb4f WalletListener functionality (Ilya Kitaev)
d27b883 hack to successfull linking for MSYS2 (Ilya Kitaev)
083380c Transaction fee multiplier aka priority integraged (Ilya Kitaev)
00ed12b Wallet::paymentIdValid (Ilya Kitaev)
Diffstat (limited to 'src')
-rw-r--r-- | src/wallet/CMakeLists.txt | 21 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 148 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 30 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.cpp | 5 | ||||
-rw-r--r-- | src/wallet/wallet2_api.h | 77 |
5 files changed, 251 insertions, 30 deletions
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_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); }; + } |