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)
This commit is contained in:
Riccardo Spagni 2016-07-23 09:17:21 +02:00
commit b582764bd6
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
7 changed files with 449 additions and 74 deletions

View file

@ -76,12 +76,25 @@ target_link_libraries(wallet
${EXTRA_LIBRARIES}) ${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) set(libs_to_merge wallet cryptonote_core mnemonics common crypto)
#MERGE_STATIC_LIBS(wallet_merged wallet_merged "${libs_to_merge}") # 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}") merge_static_libs(wallet_merged "${libs_to_merge}")
install(TARGETS wallet_merged install(TARGETS wallet_merged
ARCHIVE DESTINATION lib) ARCHIVE DESTINATION lib)
endif (STATIC)
#MERGE_STATIC_LIBS(wallet_merged wallet_merged "${libs_to_merge}")
install(FILES ${wallet_api_headers} install(FILES ${wallet_api_headers}
DESTINATION include/wallet) DESTINATION include/wallet)

View file

@ -46,6 +46,7 @@ namespace Bitmonero {
namespace { namespace {
// copy-pasted from simplewallet // copy-pasted from simplewallet
static const size_t DEFAULT_MIXIN = 4; static const size_t DEFAULT_MIXIN = 4;
static const int DEFAULT_REFRESH_INTERVAL_SECONDS = 10;
} }
struct Wallet2CallbackImpl : public tools::i_wallet2_callback struct Wallet2CallbackImpl : public tools::i_wallet2_callback
@ -89,6 +90,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", amount: " << print_money(amount)); << ", amount: " << print_money(amount));
if (m_listener) { if (m_listener) {
m_listener->moneyReceived(tx_hash, amount); m_listener->moneyReceived(tx_hash, amount);
m_listener->updated();
} }
} }
@ -103,6 +105,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
<< ", amount: " << print_money(amount)); << ", amount: " << print_money(amount));
if (m_listener) { if (m_listener) {
m_listener->moneySpent(tx_hash, amount); m_listener->moneySpent(tx_hash, amount);
m_listener->updated();
} }
} }
@ -118,6 +121,7 @@ Wallet::~Wallet() {}
WalletListener::~WalletListener() {} WalletListener::~WalletListener() {}
string Wallet::displayAmount(uint64_t amount) string Wallet::displayAmount(uint64_t amount)
{ {
return cryptonote::print_money(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 implementation ////////////////////////
WalletImpl::WalletImpl(bool testnet) WalletImpl::WalletImpl(bool testnet)
@ -153,13 +163,22 @@ WalletImpl::WalletImpl(bool testnet)
m_wallet = new tools::wallet2(testnet); m_wallet = new tools::wallet2(testnet);
m_history = new TransactionHistoryImpl(this); m_history = new TransactionHistoryImpl(this);
m_wallet2Callback = new Wallet2CallbackImpl; 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() WalletImpl::~WalletImpl()
{ {
delete m_wallet2Callback; stopRefresh();
delete m_history; delete m_history;
delete m_wallet; delete m_wallet;
delete m_wallet2Callback;
} }
bool WalletImpl::create(const std::string &path, const std::string &password, const std::string &language) bool WalletImpl::create(const std::string &path, const std::string &password, const std::string &language)
@ -251,8 +270,12 @@ bool WalletImpl::close()
clearStatus(); clearStatus();
bool result = false; bool result = false;
try { try {
// LOG_PRINT_L0("Calling wallet::store...");
m_wallet->store(); m_wallet->store();
// LOG_PRINT_L0("wallet::store done");
// LOG_PRINT_L0("Calling wallet::stop...");
m_wallet->stop(); m_wallet->stop();
// LOG_PRINT_L0("wallet::stop done");
result = true; result = true;
} catch (const std::exception &e) { } catch (const std::exception &e) {
m_status = Status_Error; m_status = Status_Error;
@ -348,20 +371,29 @@ string WalletImpl::keysFilename() const
bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit)
{ {
clearStatus(); clearStatus();
try {
m_wallet->init(daemon_address, upper_transaction_size_limit); m_wallet->init(daemon_address, upper_transaction_size_limit);
if (Utils::isAddressLocal(daemon_address)) { if (Utils::isAddressLocal(daemon_address)) {
this->setTrustedDaemon(true); this->setTrustedDaemon(true);
} }
bool result = this->refresh();
// enabling background refresh thread
startRefresh();
return result;
} catch (const std::exception &e) {
LOG_ERROR("Error initializing wallet: " << e.what());
m_status = Status_Error;
m_errorString = e.what();
} }
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 uint64_t WalletImpl::balance() const
{ {
@ -377,15 +409,17 @@ uint64_t WalletImpl::unlockedBalance() const
bool WalletImpl::refresh() bool WalletImpl::refresh()
{ {
clearStatus(); clearStatus();
try { doRefresh();
m_wallet->refresh();
} catch (const std::exception &e) {
m_status = Status_Error;
m_errorString = e.what();
}
return m_status == Status_Ok; return m_status == Status_Ok;
} }
void WalletImpl::refreshAsync()
{
LOG_PRINT_L3(__FUNCTION__ << ": Refreshing asyncronously..");
clearStatus();
m_refreshCV.notify_one();
}
// TODO: // TODO:
// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param) // 1 - properly handle payment id (add another menthod with explicit 'payment_id' param)
// 2 - check / design how "Transaction" can be single interface // 2 - check / design how "Transaction" can be single interface
@ -396,7 +430,9 @@ bool WalletImpl::refresh()
// - unconfirmed_transfer_details; // - unconfirmed_transfer_details;
// - confirmed_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(); clearStatus();
vector<cryptonote::tx_destination_entry> dsts; 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; //std::vector<tools::wallet2::pending_tx> ptx_vector;
try { 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 */, 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&) { } catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"? // TODO: make it translatable with "tr"?
@ -564,10 +602,17 @@ bool WalletImpl::connectToDaemon()
m_status = result ? Status_Ok : Status_Error; m_status = result ? Status_Ok : Status_Error;
if (!result) { if (!result) {
m_errorString = "Error connecting to daemon at " + m_wallet->get_daemon_address(); m_errorString = "Error connecting to daemon at " + m_wallet->get_daemon_address();
} else {
// start refreshing here
} }
return result; return result;
} }
bool WalletImpl::connected() const
{
return m_wallet->check_connection();
}
void WalletImpl::setTrustedDaemon(bool arg) void WalletImpl::setTrustedDaemon(bool arg)
{ {
m_trustedDaemon = arg; m_trustedDaemon = arg;
@ -584,5 +629,70 @@ void WalletImpl::clearStatus()
m_errorString.clear(); 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 } // namespace

View file

@ -35,6 +35,9 @@
#include "wallet/wallet2.h" #include "wallet/wallet2.h"
#include <string> #include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
namespace Bitmonero { namespace Bitmonero {
@ -65,14 +68,19 @@ public:
std::string filename() const; std::string filename() const;
std::string keysFilename() const; std::string keysFilename() const;
bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit); 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 connectToDaemon();
bool connected() const;
void setTrustedDaemon(bool arg); void setTrustedDaemon(bool arg);
bool trustedDaemon() const; bool trustedDaemon() const;
uint64_t balance() const; uint64_t balance() const;
uint64_t unlockedBalance() const; uint64_t unlockedBalance() const;
bool refresh(); bool refresh();
void refreshAsync();
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, 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 void disposeTransaction(PendingTransaction * t);
virtual TransactionHistory * history() const; virtual TransactionHistory * history() const;
virtual void setListener(WalletListener * l); virtual void setListener(WalletListener * l);
@ -81,19 +89,37 @@ public:
private: private:
void clearStatus(); void clearStatus();
void refreshThreadFunc();
void doRefresh();
void startRefresh();
void stopRefresh();
void pauseRefresh();
private: private:
friend class PendingTransactionImpl; friend class PendingTransactionImpl;
friend class TransactionHistoryImpl; friend class TransactionHistoryImpl;
tools::wallet2 * m_wallet; tools::wallet2 * m_wallet;
int m_status; std::atomic<int> m_status;
std::string m_errorString; std::string m_errorString;
std::string m_password; std::string m_password;
TransactionHistoryImpl * m_history; TransactionHistoryImpl * m_history;
bool m_trustedDaemon; bool m_trustedDaemon;
WalletListener * m_walletListener; WalletListener * m_walletListener;
Wallet2CallbackImpl * m_wallet2Callback; 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;
}; };

View file

@ -137,6 +137,11 @@ WalletManager *WalletManagerFactory::getWalletManager()
return g_walletManager; return g_walletManager;
} }
void WalletManagerFactory::setLogLevel(int level)
{
epee::log_space::log_singletone::get_set_log_detalisation_level(true, level);
}
} }

View file

@ -50,6 +50,14 @@ struct PendingTransaction
Status_Ok, Status_Ok,
Status_Error Status_Error
}; };
enum Priority {
Priority_Low = 1,
Priority_Medium = 2,
Priority_High = 3,
Priority_Last
};
virtual ~PendingTransaction() = 0; virtual ~PendingTransaction() = 0;
virtual int status() const = 0; virtual int status() const = 0;
virtual std::string errorString() const = 0; virtual std::string errorString() const = 0;
@ -108,7 +116,10 @@ struct WalletListener
virtual ~WalletListener() = 0; virtual ~WalletListener() = 0;
virtual void moneySpent(const std::string &txId, uint64_t amount) = 0; virtual void moneySpent(const std::string &txId, uint64_t amount) = 0;
virtual void moneyReceived(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 * \return
*/ */
virtual std::string keysFilename() const = 0; 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; 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; 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 void setTrustedDaemon(bool arg) = 0;
virtual bool trustedDaemon() const = 0; virtual bool trustedDaemon() const = 0;
virtual uint64_t balance() 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 amountFromString(const std::string &amount);
static uint64_t amountFromDouble(double amount); static uint64_t amountFromDouble(double amount);
static std::string genPaymentId(); 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; 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 * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
* \param dst_addr destination address as string * \param dst_addr destination address as string
* \param payment_id optional payment_id, can be empty string * \param payment_id optional payment_id, can be empty string
* \param amount amount * \param amount amount
* \param mixin_count mixin count. if 0 passed, wallet will use default value * \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() * \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned * after object returned
*/ */
virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, 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 void disposeTransaction(PendingTransaction * t) = 0;
virtual TransactionHistory * history() const = 0; virtual TransactionHistory * history() const = 0;
virtual void setListener(WalletListener *) = 0; virtual void setListener(WalletListener *) = 0;
@ -271,8 +324,22 @@ struct WalletManager
struct WalletManagerFactory struct WalletManagerFactory
{ {
static WalletManager * getWalletManager(); // 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);
};
} }

View file

@ -31,6 +31,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "wallet/wallet2_api.h" #include "wallet/wallet2_api.h"
#include "include_base_utils.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -39,6 +40,8 @@
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <atomic>
#include <condition_variable>
using namespace std; using namespace std;
@ -137,6 +140,7 @@ struct WalletManagerTest : public testing::Test
{ {
std::cout << __FUNCTION__ << std::endl; std::cout << __FUNCTION__ << std::endl;
wmgr = Bitmonero::WalletManagerFactory::getWalletManager(); wmgr = Bitmonero::WalletManagerFactory::getWalletManager();
// Bitmonero::WalletManagerFactory::setLogLevel(Bitmonero::WalletManagerFactory::LogLevel_4);
Utils::deleteWallet(WALLET_NAME); Utils::deleteWallet(WALLET_NAME);
Utils::deleteDir(boost::filesystem::path(WALLET_NAME_WITH_DIR).parent_path().string()); Utils::deleteDir(boost::filesystem::path(WALLET_NAME_WITH_DIR).parent_path().string());
} }
@ -235,7 +239,7 @@ TEST_F(WalletManagerTest, WalletManagerMovesWallet)
} }
/*
TEST_F(WalletManagerTest, WalletManagerChangesPassword) TEST_F(WalletManagerTest, WalletManagerChangesPassword)
{ {
Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG); Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
@ -243,7 +247,7 @@ TEST_F(WalletManagerTest, WalletManagerChangesPassword)
ASSERT_TRUE(wallet1->setPassword(WALLET_PASS2)); ASSERT_TRUE(wallet1->setPassword(WALLET_PASS2));
ASSERT_TRUE(wmgr->closeWallet(wallet1)); ASSERT_TRUE(wmgr->closeWallet(wallet1));
Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME, WALLET_PASS2); Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME, WALLET_PASS2);
ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);quint64 ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);
ASSERT_TRUE(wallet2->seed() == seed1); ASSERT_TRUE(wallet2->seed() == seed1);
ASSERT_TRUE(wmgr->closeWallet(wallet2)); ASSERT_TRUE(wmgr->closeWallet(wallet2));
Bitmonero::Wallet * wallet3 = wmgr->openWallet(WALLET_NAME, WALLET_PASS); Bitmonero::Wallet * wallet3 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
@ -343,7 +347,9 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet4)
ASSERT_TRUE(wallet1->address() == address1); ASSERT_TRUE(wallet1->address() == address1);
ASSERT_TRUE(wmgr->closeWallet(wallet1)); ASSERT_TRUE(wmgr->closeWallet(wallet1));
} }
*/
TEST_F(WalletManagerTest, WalletManagerFindsWallet) TEST_F(WalletManagerTest, WalletManagerFindsWallet)
{ {
@ -415,7 +421,7 @@ TEST_F(WalletTest1, WalletConvertsToString)
} }
/*
TEST_F(WalletTest1, WalletTransaction) TEST_F(WalletTest1, WalletTransaction)
{ {
Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true); Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
@ -429,8 +435,10 @@ TEST_F(WalletTest1, WalletTransaction)
wallet1->setDefaultMixin(1); wallet1->setDefaultMixin(1);
ASSERT_TRUE(wallet1->defaultMixin() == 1); ASSERT_TRUE(wallet1->defaultMixin() == 1);
Bitmonero::PendingTransaction * transaction = wallet1->createTransaction( Bitmonero::PendingTransaction * transaction = wallet1->createTransaction(recepient_address,
recepient_address, AMOUNT_10XMR); PAYMENT_ID_EMPTY,
AMOUNT_10XMR,
1);
ASSERT_TRUE(transaction->status() == Bitmonero::PendingTransaction::Status_Ok); ASSERT_TRUE(transaction->status() == Bitmonero::PendingTransaction::Status_Ok);
wallet1->refresh(); wallet1->refresh();
@ -440,7 +448,6 @@ TEST_F(WalletTest1, WalletTransaction)
ASSERT_FALSE(wallet1->balance() == balance); ASSERT_FALSE(wallet1->balance() == balance);
ASSERT_TRUE(wmgr->closeWallet(wallet1)); ASSERT_TRUE(wmgr->closeWallet(wallet1));
} }
*/
TEST_F(WalletTest1, WalletTransactionWithMixin) TEST_F(WalletTest1, WalletTransactionWithMixin)
{ {
@ -482,6 +489,48 @@ TEST_F(WalletTest1, WalletTransactionWithMixin)
ASSERT_TRUE(wmgr->closeWallet(wallet1)); ASSERT_TRUE(wmgr->closeWallet(wallet1));
} }
TEST_F(WalletTest1, WalletTransactionWithPriority)
{
std::string payment_id = "";
Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
// make sure testnet daemon is running
ASSERT_TRUE(wallet1->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet1->refresh());
uint64_t balance = wallet1->balance();
ASSERT_TRUE(wallet1->status() == Bitmonero::PendingTransaction::Status_Ok);
std::string recepient_address = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
uint32_t mixin = 2;
uint64_t fee = 0;
std::vector<Bitmonero::PendingTransaction::Priority> priorities = {
Bitmonero::PendingTransaction::Priority_Low,
Bitmonero::PendingTransaction::Priority_Medium,
Bitmonero::PendingTransaction::Priority_High
};
for (auto it = priorities.begin(); it != priorities.end(); ++it) {
std::cerr << "Transaction priority: " << *it << std::endl;
Bitmonero::PendingTransaction * transaction = wallet1->createTransaction(
recepient_address, payment_id, AMOUNT_5XMR, mixin, *it);
std::cerr << "Transaction status: " << transaction->status() << std::endl;
std::cerr << "Transaction fee: " << Bitmonero::Wallet::displayAmount(transaction->fee()) << std::endl;
std::cerr << "Transaction error: " << transaction->errorString() << std::endl;
ASSERT_TRUE(transaction->fee() > fee);
ASSERT_TRUE(transaction->status() == Bitmonero::PendingTransaction::Status_Ok);
fee = transaction->fee();
wallet1->disposeTransaction(transaction);
}
wallet1->refresh();
ASSERT_TRUE(wallet1->balance() == balance);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
TEST_F(WalletTest1, WalletHistory) TEST_F(WalletTest1, WalletHistory)
{ {
Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true); Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
@ -522,7 +571,7 @@ TEST_F(WalletTest1, WalletTransactionAndHistory)
Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr, Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr,
PAYMENT_ID_EMPTY, PAYMENT_ID_EMPTY,
AMOUNT_10XMR * 5, 0); AMOUNT_10XMR * 5, 1);
ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok);
ASSERT_TRUE(tx->commit()); ASSERT_TRUE(tx->commit());
@ -586,96 +635,189 @@ TEST_F(WalletTest1, WalletTransactionWithPaymentId)
ASSERT_TRUE(payment_id_in_history); ASSERT_TRUE(payment_id_in_history);
} }
struct MyWalletListener : public Bitmonero::WalletListener struct MyWalletListener : public Bitmonero::WalletListener
{ {
Bitmonero::Wallet * wallet; Bitmonero::Wallet * wallet;
uint64_t total_tx; uint64_t total_tx;
uint64_t total_rx; uint64_t total_rx;
std::timed_mutex guard; std::mutex mutex;
std::condition_variable cv_send;
std::condition_variable cv_receive;
std::condition_variable cv_update;
std::condition_variable cv_refresh;
bool send_triggered;
bool receive_triggered;
bool update_triggered;
bool refresh_triggered;
MyWalletListener(Bitmonero::Wallet * wallet) MyWalletListener(Bitmonero::Wallet * wallet)
: total_tx(0), total_rx(0) : total_tx(0), total_rx(0)
{ {
reset();
this->wallet = wallet; this->wallet = wallet;
this->wallet->setListener(this); this->wallet->setListener(this);
} }
void reset()
{
send_triggered = receive_triggered = update_triggered = refresh_triggered = false;
}
virtual void moneySpent(const string &txId, uint64_t amount) virtual void moneySpent(const string &txId, uint64_t amount)
{ {
std::cout << "wallet: " << wallet->address() << " just spent money (" std::cerr << "wallet: " << wallet->address() << "**** just spent money ("
<< txId << ", " << wallet->displayAmount(amount) << ")" << std::endl; << txId << ", " << wallet->displayAmount(amount) << ")" << std::endl;
total_tx += amount; total_tx += amount;
guard.unlock(); send_triggered = true;
cv_send.notify_one();
} }
virtual void moneyReceived(const string &txId, uint64_t amount) virtual void moneyReceived(const string &txId, uint64_t amount)
{ {
std::cout << "wallet: " << wallet->address() << " just received money (" std::cout << "wallet: " << wallet->address() << "**** just received money ("
<< txId << ", " << wallet->displayAmount(amount) << ")" << std::endl; << txId << ", " << wallet->displayAmount(amount) << ")" << std::endl;
total_rx += amount; total_rx += amount;
guard.unlock(); receive_triggered = true;
cv_receive.notify_one();
} }
virtual void updated()
{
std::cout << __FUNCTION__ << "Wallet updated";
update_triggered = true;
cv_update.notify_one();
}
virtual void refreshed()
{
std::cout << __FUNCTION__ << "Wallet refreshed";
refresh_triggered = true;
cv_refresh.notify_one();
}
}; };
/*
TEST_F(WalletTest2, WalletCallBackRefreshedSync)
{
Bitmonero::Wallet * wallet_src = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
MyWalletListener * wallet_src_listener = new MyWalletListener(wallet_src);
ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_src_listener->refresh_triggered);
ASSERT_TRUE(wallet_src->connected());
// std::chrono::seconds wait_for = std::chrono::seconds(60*3);
// std::unique_lock<std::mutex> lock (wallet_src_listener->mutex);
// wallet_src_listener->cv_refresh.wait_for(lock, wait_for);
wmgr->closeWallet(wallet_src);
}
TEST_F(WalletTest2, WalletCallBackRefreshedAsync)
{
Bitmonero::Wallet * wallet_src = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
MyWalletListener * wallet_src_listener = new MyWalletListener(wallet_src);
std::chrono::seconds wait_for = std::chrono::seconds(20);
std::unique_lock<std::mutex> lock (wallet_src_listener->mutex);
wallet_src->initAsync(TESTNET_DAEMON_ADDRESS, 0);
std::cerr << "TEST: waiting on refresh lock...\n";
wallet_src_listener->cv_refresh.wait_for(lock, wait_for);
std::cerr << "TEST: refresh lock acquired...\n";
ASSERT_TRUE(wallet_src_listener->refresh_triggered);
ASSERT_TRUE(wallet_src->connected());
std::cerr << "TEST: closing wallet...\n";
wmgr->closeWallet(wallet_src);
}
TEST_F(WalletTest2, WalletCallbackSent) TEST_F(WalletTest2, WalletCallbackSent)
{ {
Bitmonero::Wallet * wallet_src = wmgr->openWallet(TESTNET_WALLET3_NAME, TESTNET_WALLET_PASS, true); Bitmonero::Wallet * wallet_src = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
// make sure testnet daemon is running // make sure testnet daemon is running
ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0)); ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_src->refresh()); ASSERT_TRUE(wallet_src->refresh());
MyWalletListener * wallet_src_listener = new MyWalletListener(wallet_src); MyWalletListener * wallet_src_listener = new MyWalletListener(wallet_src);
uint64_t balance = wallet_src->balance();
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl; std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl;
Bitmonero::Wallet * wallet_dst = wmgr->openWallet(CURRENT_DST_WALLET, TESTNET_WALLET_PASS, true);
uint64_t amount = AMOUNT_1XMR * 5;
std::cout << "** Sending " << Bitmonero::Wallet::displayAmount(amount) << " to " << wallet_dst->address();
uint64_t amount = AMOUNT_10XMR * 5; Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->address(),
std::cout << "** Sending " << Bitmonero::Wallet::displayAmount(amount) << " to " << TESTNET_WALLET4_ADDRESS; PAYMENT_ID_EMPTY,
Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(TESTNET_WALLET4_ADDRESS, AMOUNT_1XMR * 5); amount, 1);
std::cout << "** Committing transaction: " << Bitmonero::Wallet::displayAmount(tx->amount())
<< " with fee: " << Bitmonero::Wallet::displayAmount(tx->fee());
ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok);
ASSERT_TRUE(tx->commit()); ASSERT_TRUE(tx->commit());
std::chrono::seconds wait_for = std::chrono::seconds(60*3); std::chrono::seconds wait_for = std::chrono::seconds(60*3);
std::unique_lock<std::mutex> lock (wallet_src_listener->mutex);
wallet_src_listener->guard.lock(); std::cerr << "TEST: waiting on send lock...\n";
wallet_src_listener->guard.try_lock_for(wait_for); wallet_src_listener->cv_send.wait_for(lock, wait_for);
std::cerr << "TEST: send lock acquired...\n";
ASSERT_TRUE(wallet_src_listener->total_tx != 0); ASSERT_TRUE(wallet_src_listener->send_triggered);
ASSERT_TRUE(wallet_src_listener->update_triggered);
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl;
ASSERT_TRUE(wallet_src->balance() < balance);
wmgr->closeWallet(wallet_src);
wmgr->closeWallet(wallet_dst);
} }
*/
/*
TEST_F(WalletTest2, WalletCallbackReceived) TEST_F(WalletTest2, WalletCallbackReceived)
{ {
Bitmonero::Wallet * wallet_src = wmgr->openWallet(TESTNET_WALLET3_NAME, TESTNET_WALLET_PASS, true); Bitmonero::Wallet * wallet_src = wmgr->openWallet(TESTNET_WALLET5_NAME, TESTNET_WALLET_PASS, true);
// make sure testnet daemon is running // make sure testnet daemon is running
ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0)); ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_src->refresh()); ASSERT_TRUE(wallet_src->refresh());
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl; std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl;
Bitmonero::Wallet * wallet_dst = wmgr->openWallet(TESTNET_WALLET4_NAME, TESTNET_WALLET_PASS, true); Bitmonero::Wallet * wallet_dst = wmgr->openWallet(CURRENT_DST_WALLET, TESTNET_WALLET_PASS, true);
ASSERT_TRUE(wallet_dst->init(TESTNET_DAEMON_ADDRESS, 0)); ASSERT_TRUE(wallet_dst->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_dst->refresh()); ASSERT_TRUE(wallet_dst->refresh());
uint64_t balance = wallet_dst->balance();
MyWalletListener * wallet_dst_listener = new MyWalletListener(wallet_dst); MyWalletListener * wallet_dst_listener = new MyWalletListener(wallet_dst);
uint64_t amount = AMOUNT_1XMR * 5; uint64_t amount = AMOUNT_1XMR * 5;
std::cout << "** Sending " << Bitmonero::Wallet::displayAmount(amount) << " to " << TESTNET_WALLET4_ADDRESS; std::cout << "** Sending " << Bitmonero::Wallet::displayAmount(amount) << " to " << wallet_dst->address();
Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(TESTNET_WALLET4_ADDRESS, AMOUNT_1XMR * 5); Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->address(),
PAYMENT_ID_EMPTY,
amount, 1);
std::cout << "** Committing transaction: " << Bitmonero::Wallet::displayAmount(tx->amount())
<< " with fee: " << Bitmonero::Wallet::displayAmount(tx->fee());
ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok);
ASSERT_TRUE(tx->commit()); ASSERT_TRUE(tx->commit());
std::chrono::seconds wait_for = std::chrono::seconds(60*4); std::chrono::seconds wait_for = std::chrono::seconds(60*4);
std::unique_lock<std::mutex> lock (wallet_dst_listener->mutex);
std::cerr << "TEST: waiting on receive lock...\n";
wallet_dst_listener->cv_receive.wait_for(lock, wait_for);
std::cerr << "TEST: receive lock acquired...\n";
ASSERT_TRUE(wallet_dst_listener->receive_triggered);
ASSERT_TRUE(wallet_dst_listener->update_triggered);
std::cout << "** Balance: " << wallet_dst->displayAmount(wallet_src->balance()) << std::endl;
ASSERT_TRUE(wallet_dst->balance() > balance);
wallet_dst_listener->guard.lock(); wmgr->closeWallet(wallet_src);
wallet_dst_listener->guard.try_lock_for(wait_for); wmgr->closeWallet(wallet_dst);
ASSERT_TRUE(wallet_dst_listener->total_tx != 0);
} }
*/
int main(int argc, char** argv) int main(int argc, char** argv)
{ {

View file

@ -12,11 +12,23 @@ function send_funds {
} }
send_funds 100 wallet_01.bin function seed_wallets {
send_funds 100 wallet_02.bin local amount=$1
send_funds 100 wallet_03.bin send_funds $amount wallet_01.bin
send_funds 100 wallet_04.bin send_funds $amount wallet_02.bin
send_funds 100 wallet_05.bin send_funds $amount wallet_03.bin
send_funds 100 wallet_06.bin send_funds $amount wallet_04.bin
send_funds $amount wallet_05.bin
send_funds $amount wallet_06.bin
}
seed_wallets 1
seed_wallets 2
seed_wallets 5
seed_wallets 10
seed_wallets 20
seed_wallets 50
seed_wallets 100