diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 22ddb0639..0581d9de9 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2999,7 +2999,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) } LOCK_REFRESH_THREAD_SCOPE(); - + // optional in/out selector if (local_args.size() > 0) { if (local_args[0] == "in" || local_args[0] == "incoming") { diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index c7e7472e9..db42e2141 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -82,8 +82,9 @@ void TransactionHistoryImpl::refresh() // delete old transactions; for (auto t : m_history) delete t; + m_history.clear(); + - std::list> payments; // transactions are stored in wallet2: // - confirmed_transfer_details - out transfers @@ -91,8 +92,11 @@ void TransactionHistoryImpl::refresh() // - payment_details - input transfers // payments are "input transactions"; - m_wallet->m_wallet->get_payments(payments, min_height, max_height); - for (std::list>::const_iterator i = payments.begin(); i != payments.end(); ++i) { + // one input transaction contains only one transfer. e.g. - <100XMR> + + std::list> in_payments; + m_wallet->m_wallet->get_payments(in_payments, min_height, max_height); + for (std::list>::const_iterator i = in_payments.begin(); i != in_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) @@ -111,8 +115,75 @@ void TransactionHistoryImpl::refresh() /* output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%20.20s %s %s %s") % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) - % payment_id % "-").str())));*/ + % payment_id % "-").str()))); */ } + + // confirmed output transactions + // one output transaction may contain more than one money transfer, e.g. + // : + // transfer1: 100XMR to + // transfer2: 50XMR to + // fee: fee charged per transaction + // + + std::list> out_payments; + m_wallet->m_wallet->get_payments_out(out_payments, min_height, max_height); + + for (std::list>::const_iterator i = out_payments.begin(); + i != out_payments.end(); ++i) { + + const crypto::hash &hash = i->first; + const tools::wallet2::confirmed_transfer_details &pd = i->second; + + uint64_t fee = pd.m_amount_in - pd.m_amount_out; + uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known + + + std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); + if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) + payment_id = payment_id.substr(0,16); + + + TransactionInfoImpl * ti = new TransactionInfoImpl(); + ti->m_paymentid = payment_id; + ti->m_amount = pd.m_amount_in - change - fee; + ti->m_fee = fee; + ti->m_direction = TransactionInfo::Direction_Out; + ti->m_hash = string_tools::pod_to_hex(hash); + ti->m_blockheight = pd.m_block_height; + + // single output transaction might contain multiple transfers + for (const auto &d: pd.m_dests) { + ti->m_transfers.push_back({d.amount, get_account_address_as_str(m_wallet->m_wallet->testnet(), d.addr)}); + } + m_history.push_back(ti); + } + + // unconfirmed output transactions + std::list> upayments; + m_wallet->m_wallet->get_unconfirmed_payments_out(upayments); + for (std::list>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { + const tools::wallet2::unconfirmed_transfer_details &pd = i->second; + const crypto::hash &hash = i->first; + uint64_t amount = 0; + cryptonote::get_inputs_money_amount(pd.m_tx, amount); + uint64_t fee = amount - get_outs_money_amount(pd.m_tx); + std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); + if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) + payment_id = payment_id.substr(0,16); + bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; + + TransactionInfoImpl * ti = new TransactionInfoImpl(); + ti->m_paymentid = payment_id; + ti->m_amount = amount - pd.m_change; + ti->m_fee = fee; + ti->m_direction = TransactionInfo::Direction_Out; + ti->m_failed = is_failed; + ti->m_pending = true; + ti->m_hash = string_tools::pod_to_hex(hash); + m_history.push_back(ti); + } + } TransactionInfo *TransactionHistoryImpl::transaction(int index) const diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 578b84832..f25c53a90 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -37,9 +37,13 @@ namespace Bitmonero { TransactionInfo::~TransactionInfo() {} +TransactionInfo::Transfer::Transfer(uint64_t _amount, const string &_address) + : amount(_amount), address(_address) {} + + TransactionInfoImpl::TransactionInfoImpl() : m_direction(Direction_Out) - , m_hold(false) + , m_pending(false) , m_failed(false) , m_amount(0) , m_fee(0) @@ -56,13 +60,13 @@ TransactionInfoImpl::~TransactionInfoImpl() int TransactionInfoImpl::direction() const { - return TransactionInfo::Direction_In; + return m_direction; } -bool TransactionInfoImpl::isHold() const +bool TransactionInfoImpl::isPending() const { - return m_hold; + return m_pending; } bool TransactionInfoImpl::isFailed() const @@ -100,4 +104,9 @@ string TransactionInfoImpl::paymentId() const return m_paymentid; } +const std::vector &TransactionInfoImpl::transfers() const +{ + return m_transfers; +} + } // namespace diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index a06bc367e..82ab2cc6b 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -44,7 +44,7 @@ public: //! in/out virtual int direction() const; //! true if hold - virtual bool isHold() const; + virtual bool isPending() const; virtual bool isFailed() const; virtual uint64_t amount() const; //! always 0 for incoming txes @@ -54,10 +54,11 @@ public: virtual std::string hash() const; virtual std::time_t timestamp() const; virtual std::string paymentId() const; + virtual const std::vector &transfers() const; private: int m_direction; - bool m_hold; + bool m_pending; bool m_failed; uint64_t m_amount; uint64_t m_fee; @@ -65,7 +66,7 @@ private: std::string m_hash; std::time_t m_timestamp; std::string m_paymentid; - + std::vector m_transfers; friend class TransactionHistoryImpl; diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index ab715dbf9..43f5c9472 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -68,16 +68,26 @@ struct TransactionInfo Direction_In, Direction_Out }; + + struct Transfer { + Transfer(uint64_t _amount, const std::string &address); + const uint64_t amount; + const std::string address; + }; + virtual ~TransactionInfo() = 0; virtual int direction() const = 0; - virtual bool isHold() const = 0; + virtual bool isPending() const = 0; virtual bool isFailed() const = 0; virtual uint64_t amount() const = 0; virtual uint64_t fee() const = 0; virtual uint64_t blockHeight() const = 0; + //! transaction_id virtual std::string hash() const = 0; virtual std::time_t timestamp() const = 0; virtual std::string paymentId() const = 0; + //! only applicable for output transactions + virtual const std::vector & transfers() const = 0; }; /** * @brief The TransactionHistory - interface for displaying transaction history diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index 51c0a3ca3..35d0c8e9d 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -76,12 +76,19 @@ struct WalletManagerTest : public testing::Test // TODO: add test wallets to the source tree (as they have some balance mined)? const char * TESTNET_WALLET_NAME = "/home/mbg033/dev/monero/testnet/wallet_01.bin"; + const char * TESTNET_WALLET3_NAME = "/home/mbg033/dev/monero/testnet/wallet_03.bin"; + const char * TESTNET_WALLET4_NAME = "/home/mbg033/dev/monero/testnet/wallet_04.bin"; const char * TESTNET_WALLET_PASS = ""; + + const char * TESTNET_DAEMON_ADDRESS = "localhost:38081"; const uint64_t AMOUNT_10XMR = 10000000000000L; const uint64_t AMOUNT_5XMR = 50000000000000L; + const char * RECIPIENT_WALLET_ADDRESS = "9uekQVGj7NjSAREnZ8cUsRagWDdjvdhpwUKhsL95oXngBnZXZ1RzH8R6UJbU1R7wim9yKbSjxuoQ22ERRkEochGECj66oP3"; + const char * TESTNET_WALLET3_ADDRESS = "A11cBpRDqpTCneSL3KNBvGWM6PfxG7QrxNVCcMiZeuAD3fQA9Z366DegFLYHKrMnDm8QixPziRn4kVcWPFtn6aCSR1Hp7sg"; + const char * TESTNET_WALLET4_ADDRESS = "A21wicxbhUSKa6twequhKCCG8wYEGZ7viYRLW7mBXtWyheyY8C8XwUJG5PSjULDs1q7hndkihtFgybWjagvchrNg1Y588hM"; WalletManagerTest() { @@ -97,6 +104,20 @@ struct WalletManagerTest : public testing::Test std::cout << __FUNCTION__ << std::endl; //deleteWallet(WALLET_NAME); } + + static void print_transaction(Bitmonero::TransactionInfo * t) + { + + std::cout << "d: " + << (t->direction() == Bitmonero::TransactionInfo::Direction_In ? "in" : "out") + << ", pe: " << (t->isPending() ? "true" : "false") + << ", bh: " << t->blockHeight() + << ", a: " << Bitmonero::Wallet::displayAmount(t->amount()) + << ", f: " << Bitmonero::Wallet::displayAmount(t->fee()) + << ", h: " << t->hash() + << ", pid: " << t->paymentId() + << std::endl; + } }; @@ -279,6 +300,7 @@ TEST_F(WalletManagerTest, WalletTransaction) RECIPIENT_WALLET_ADDRESS, AMOUNT_10XMR); ASSERT_TRUE(transaction->status() == Bitmonero::PendingTransaction::Status_Ok); + ASSERT_TRUE(wallet1->balance() == balance); ASSERT_TRUE(transaction->amount() == AMOUNT_10XMR); ASSERT_TRUE(transaction->commit()); @@ -295,20 +317,42 @@ TEST_F(WalletManagerTest, WalletHistory) Bitmonero::TransactionHistory * history = wallet1->history(); history->refresh(); ASSERT_TRUE(history->count() > 0); - auto transaction_print = [=] (Bitmonero::TransactionInfo * t) { - std::cout << "d: " - << (t->direction() == Bitmonero::TransactionInfo::Direction_In ? "in" : "out") - << ", bh: " << t->blockHeight() - << ", a: " << Bitmonero::Wallet::displayAmount(t->amount()) - << ", f: " << Bitmonero::Wallet::displayAmount(t->fee()) - << ", h: " << t->hash() - << ", pid: " << t->paymentId() - << std::endl; - }; + for (auto t: history->getAll()) { ASSERT_TRUE(t != nullptr); - transaction_print(t); + print_transaction(t); + } +} + +TEST_F(WalletManagerTest, WalletTransactionAndHistory) +{ + Bitmonero::Wallet * wallet_src = wmgr->openWallet(TESTNET_WALLET3_NAME, TESTNET_WALLET_PASS, true); + // make sure testnet daemon is running + ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0)); + ASSERT_TRUE(wallet_src->refresh()); + Bitmonero::TransactionHistory * history = wallet_src->history(); + history->refresh(); + ASSERT_TRUE(history->count() > 0); + size_t count1 = history->count(); + + std::cout << "**** Transactions before transfer (" << count1 << ")" << std::endl; + for (auto t: history->getAll()) { + ASSERT_TRUE(t != nullptr); + print_transaction(t); + } + + Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(TESTNET_WALLET4_ADDRESS, AMOUNT_10XMR * 5); + ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok); + ASSERT_TRUE(tx->commit()); + history = wallet_src->history(); + history->refresh(); + ASSERT_TRUE(count1 != history->count()); + + std::cout << "**** Transactions after transfer (" << history->count() << ")" << std::endl; + for (auto t: history->getAll()) { + ASSERT_TRUE(t != nullptr); + print_transaction(t); } }