1150 lines
53 KiB
Diff
1150 lines
53 KiB
Diff
From 753d7eee48d8e9ab5950e48dc984cc1038c11dd1 Mon Sep 17 00:00:00 2001
|
|
From: tobtoht <tob@featherwallet.org>
|
|
Date: Tue, 12 Mar 2024 11:07:57 +0100
|
|
Subject: [PATCH 08/14] coin control
|
|
|
|
---
|
|
src/simplewallet/simplewallet.cpp | 2 +-
|
|
src/wallet/api/CMakeLists.txt | 8 +-
|
|
src/wallet/api/coins.cpp | 186 ++++++++++++++++++++++++++++++
|
|
src/wallet/api/coins.h | 40 +++++++
|
|
src/wallet/api/coins_info.cpp | 122 ++++++++++++++++++++
|
|
src/wallet/api/coins_info.h | 71 ++++++++++++
|
|
src/wallet/api/wallet.cpp | 170 +++++++++++++++++++++------
|
|
src/wallet/api/wallet.h | 10 +-
|
|
src/wallet/api/wallet2_api.h | 52 ++++++++-
|
|
src/wallet/wallet2.cpp | 46 +++++++-
|
|
src/wallet/wallet2.h | 11 +-
|
|
11 files changed, 667 insertions(+), 51 deletions(-)
|
|
create mode 100644 src/wallet/api/coins.cpp
|
|
create mode 100644 src/wallet/api/coins.h
|
|
create mode 100644 src/wallet/api/coins_info.cpp
|
|
create mode 100644 src/wallet/api/coins_info.h
|
|
|
|
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
|
|
index 89691e9f7..b26817e52 100644
|
|
--- a/src/simplewallet/simplewallet.cpp
|
|
+++ b/src/simplewallet/simplewallet.cpp
|
|
@@ -7183,25 +7183,25 @@ bool simple_wallet::transfer_main(
|
|
switch (transfer_type) {
|
|
case Burn:
|
|
unlock_block = 0;
|
|
- ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::BURN, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
|
+ ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::BURN, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
|
break;
|
|
case Convert:
|
|
unlock_block = CONVERT_LOCK_PERIOD;
|
|
- ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
|
+ ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
|
break;
|
|
case Stake:
|
|
unlock_block = get_config(m_wallet->nettype()).STAKE_LOCK_PERIOD;
|
|
- ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::STAKE, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
|
+ ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::STAKE, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
|
break;
|
|
case TransferLocked:
|
|
unlock_block = locked_blocks;
|
|
- ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
|
+ ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
|
break;
|
|
default:
|
|
LOG_ERROR("Unknown transfer method, using default");
|
|
/* FALLTHRU */
|
|
case Transfer:
|
|
- ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
|
+ ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt
|
|
index af7948d8a..bb740e2ac 100644
|
|
--- a/src/wallet/api/CMakeLists.txt
|
|
+++ b/src/wallet/api/CMakeLists.txt
|
|
@@ -40,7 +40,9 @@ set(wallet_api_sources
|
|
address_book.cpp
|
|
subaddress.cpp
|
|
subaddress_account.cpp
|
|
- unsigned_transaction.cpp)
|
|
+ unsigned_transaction.cpp
|
|
+ coins.cpp
|
|
+ coins_info.cpp)
|
|
|
|
set(wallet_api_headers
|
|
wallet2_api.h)
|
|
@@ -55,7 +57,9 @@ set(wallet_api_private_headers
|
|
address_book.h
|
|
subaddress.h
|
|
subaddress_account.h
|
|
- unsigned_transaction.h)
|
|
+ unsigned_transaction.h
|
|
+ coins.h
|
|
+ coins_info.h)
|
|
|
|
monero_private_headers(wallet_api
|
|
${wallet_api_private_headers})
|
|
diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp
|
|
new file mode 100644
|
|
index 000000000..ef12141cf
|
|
--- /dev/null
|
|
+++ b/src/wallet/api/coins.cpp
|
|
@@ -0,0 +1,194 @@
|
|
+#include "coins.h"
|
|
+#include "coins_info.h"
|
|
+#include "wallet.h"
|
|
+#include "crypto/hash.h"
|
|
+#include "wallet/wallet2.h"
|
|
+#include "common_defines.h"
|
|
+
|
|
+#include <string>
|
|
+#include <vector>
|
|
+
|
|
+using namespace epee;
|
|
+
|
|
+namespace Monero {
|
|
+
|
|
+Coins::~Coins() = default;
|
|
+
|
|
+CoinsImpl::CoinsImpl(WalletImpl *wallet)
|
|
+ : m_wallet(wallet) {}
|
|
+
|
|
+CoinsImpl::~CoinsImpl()
|
|
+{
|
|
+ for (auto t : m_rows)
|
|
+ delete t;
|
|
+}
|
|
+
|
|
+int CoinsImpl::count() const
|
|
+{
|
|
+ boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
|
+ int result = m_rows.size();
|
|
+ return result;
|
|
+}
|
|
+
|
|
+CoinsInfo *CoinsImpl::coin(int index) const
|
|
+{
|
|
+ boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
|
+ // sanity check
|
|
+ if (index < 0)
|
|
+ return nullptr;
|
|
+ auto index_ = static_cast<unsigned>(index);
|
|
+ return index_ < m_rows.size() ? m_rows[index_] : nullptr;
|
|
+}
|
|
+
|
|
+std::vector<CoinsInfo *> CoinsImpl::getAll() const
|
|
+{
|
|
+ boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
|
+ return m_rows;
|
|
+}
|
|
+
|
|
+
|
|
+void CoinsImpl::refresh()
|
|
+{
|
|
+ LOG_PRINT_L2("Refreshing coins");
|
|
+
|
|
+ boost::unique_lock<boost::shared_mutex> lock(m_rowsMutex);
|
|
+ boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_wallet->m_transfers_mutex);
|
|
+
|
|
+ // delete old outputs;
|
|
+ for (auto t : m_rows)
|
|
+ delete t;
|
|
+ m_rows.clear();
|
|
+
|
|
+ for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i)
|
|
+ {
|
|
+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i);
|
|
+
|
|
+ // Make a subaddress_index_extended from td.m_subaddr_index
|
|
+ carrot::subaddress_index_extended csub{
|
|
+ {td.m_subaddr_index.major, td.m_subaddr_index.minor},
|
|
+ td.is_carrot() ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot,
|
|
+ false};
|
|
+
|
|
+ auto ci = new CoinsInfoImpl();
|
|
+ ci->m_blockHeight = td.m_block_height;
|
|
+ ci->m_hash = string_tools::pod_to_hex(td.m_txid);
|
|
+ ci->m_internalOutputIndex = td.m_internal_output_index;
|
|
+ ci->m_globalOutputIndex = td.m_global_output_index;
|
|
+ ci->m_spent = td.m_spent;
|
|
+ ci->m_frozen = td.m_frozen;
|
|
+ ci->m_spentHeight = td.m_spent_height;
|
|
+ ci->m_amount = td.m_amount;
|
|
+ ci->m_rct = td.m_rct;
|
|
+ ci->m_keyImageKnown = td.m_key_image_known;
|
|
+ ci->m_pkIndex = td.m_pk_index;
|
|
+ ci->m_subaddrIndex = td.m_subaddr_index.minor;
|
|
+ ci->m_subaddrAccount = td.m_subaddr_index.major;
|
|
+ ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(csub); // todo: this is expensive, cache maybe?
|
|
+ ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index);
|
|
+ ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image);
|
|
+ ci->m_unlockTime = td.m_tx.unlock_time;
|
|
+ ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td);
|
|
+ ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key());
|
|
+ ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
|
|
+ ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid);
|
|
+ ci->m_asset = td.asset_type;
|
|
+ ci->m_type = td.m_tx.type;
|
|
+
|
|
+ m_rows.push_back(ci);
|
|
+ }
|
|
+}
|
|
+
|
|
+void CoinsImpl::setFrozen(std::string public_key)
|
|
+{
|
|
+ crypto::public_key pk;
|
|
+ if (!epee::string_tools::hex_to_pod(public_key, pk))
|
|
+ {
|
|
+ LOG_ERROR("Invalid public key: " << public_key);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ try
|
|
+ {
|
|
+ m_wallet->m_wallet->freeze(pk);
|
|
+ refresh();
|
|
+ }
|
|
+ catch (const std::exception& e)
|
|
+ {
|
|
+ LOG_ERROR("setFrozen: " << e.what());
|
|
+ }
|
|
+}
|
|
+
|
|
+void CoinsImpl::setFrozen(int index)
|
|
+{
|
|
+ try
|
|
+ {
|
|
+ LOG_ERROR("Freezing coin: " << index);
|
|
+ m_wallet->m_wallet->freeze(index);
|
|
+ refresh();
|
|
+ }
|
|
+ catch (const std::exception& e)
|
|
+ {
|
|
+ LOG_ERROR("setLabel: " << e.what());
|
|
+ }
|
|
+}
|
|
+
|
|
+void CoinsImpl::thaw(std::string public_key)
|
|
+{
|
|
+ crypto::public_key pk;
|
|
+ if (!epee::string_tools::hex_to_pod(public_key, pk))
|
|
+ {
|
|
+ LOG_ERROR("Invalid public key: " << public_key);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ try
|
|
+ {
|
|
+ m_wallet->m_wallet->thaw(pk);
|
|
+ refresh();
|
|
+ }
|
|
+ catch (const std::exception& e)
|
|
+ {
|
|
+ LOG_ERROR("thaw: " << e.what());
|
|
+ }
|
|
+}
|
|
+
|
|
+void CoinsImpl::thaw(int index)
|
|
+{
|
|
+ try
|
|
+ {
|
|
+ m_wallet->m_wallet->thaw(index);
|
|
+ refresh();
|
|
+ }
|
|
+ catch (const std::exception& e)
|
|
+ {
|
|
+ LOG_ERROR("thaw: " << e.what());
|
|
+ }
|
|
+}
|
|
+
|
|
+bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) {
|
|
+ return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight);
|
|
+}
|
|
+
|
|
+void CoinsImpl::setDescription(const std::string &public_key, const std::string &description)
|
|
+{
|
|
+ crypto::public_key pk;
|
|
+ if (!epee::string_tools::hex_to_pod(public_key, pk))
|
|
+ {
|
|
+ LOG_ERROR("Invalid public key: " << public_key);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ try
|
|
+ {
|
|
+ const size_t index = m_wallet->m_wallet->get_transfer_details(pk);
|
|
+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index);
|
|
+ m_wallet->m_wallet->set_tx_note(td.m_txid, description);
|
|
+ refresh();
|
|
+ }
|
|
+ catch (const std::exception& e)
|
|
+ {
|
|
+ LOG_ERROR("setDescription: " << e.what());
|
|
+ }
|
|
+}
|
|
+
|
|
+} // namespace
|
|
diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h
|
|
new file mode 100644
|
|
index 000000000..b7a0a8642
|
|
--- /dev/null
|
|
+++ b/src/wallet/api/coins.h
|
|
@@ -0,0 +1,40 @@
|
|
+#ifndef FEATHER_COINS_H
|
|
+#define FEATHER_COINS_H
|
|
+
|
|
+#include "wallet/api/wallet2_api.h"
|
|
+#include "wallet/wallet2.h"
|
|
+
|
|
+namespace Monero {
|
|
+
|
|
+class WalletImpl;
|
|
+
|
|
+class CoinsImpl : public Coins
|
|
+{
|
|
+public:
|
|
+ explicit CoinsImpl(WalletImpl * wallet);
|
|
+ ~CoinsImpl() override;
|
|
+ int count() const override;
|
|
+ CoinsInfo * coin(int index) const override;
|
|
+ std::vector<CoinsInfo*> getAll() const override;
|
|
+ void refresh() override;
|
|
+
|
|
+ void setFrozen(std::string public_key) override;
|
|
+ void setFrozen(int index) override;
|
|
+ void thaw(std::string public_key) override;
|
|
+ void thaw(int index) override;
|
|
+
|
|
+ bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override;
|
|
+
|
|
+ void setDescription(const std::string &public_key, const std::string &description) override;
|
|
+
|
|
+private:
|
|
+ WalletImpl *m_wallet;
|
|
+ std::vector<CoinsInfo*> m_rows;
|
|
+ mutable boost::shared_mutex m_rowsMutex;
|
|
+};
|
|
+
|
|
+}
|
|
+
|
|
+namespace Bitmonero = Monero;
|
|
+
|
|
+#endif //FEATHER_COINS_H
|
|
diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp
|
|
new file mode 100644
|
|
index 000000000..5f2c4e1e4
|
|
--- /dev/null
|
|
+++ b/src/wallet/api/coins_info.cpp
|
|
@@ -0,0 +1,132 @@
|
|
+#include "coins_info.h"
|
|
+
|
|
+using namespace std;
|
|
+
|
|
+namespace Monero {
|
|
+
|
|
+CoinsInfo::~CoinsInfo() = default;
|
|
+
|
|
+CoinsInfoImpl::CoinsInfoImpl()
|
|
+ : m_blockHeight(0)
|
|
+ , m_internalOutputIndex(0)
|
|
+ , m_globalOutputIndex(0)
|
|
+ , m_spent(false)
|
|
+ , m_frozen(false)
|
|
+ , m_spentHeight(0)
|
|
+ , m_amount(0)
|
|
+ , m_rct(false)
|
|
+ , m_keyImageKnown(false)
|
|
+ , m_pkIndex(0)
|
|
+ , m_subaddrAccount(0)
|
|
+ , m_subaddrIndex(0)
|
|
+ , m_unlockTime(0)
|
|
+ , m_unlocked(false)
|
|
+ , m_type(0)
|
|
+{
|
|
+
|
|
+}
|
|
+
|
|
+CoinsInfoImpl::~CoinsInfoImpl() = default;
|
|
+
|
|
+uint64_t CoinsInfoImpl::blockHeight() const
|
|
+{
|
|
+ return m_blockHeight;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::hash() const
|
|
+{
|
|
+ return m_hash;
|
|
+}
|
|
+
|
|
+size_t CoinsInfoImpl::internalOutputIndex() const {
|
|
+ return m_internalOutputIndex;
|
|
+}
|
|
+
|
|
+uint64_t CoinsInfoImpl::globalOutputIndex() const
|
|
+{
|
|
+ return m_globalOutputIndex;
|
|
+}
|
|
+
|
|
+bool CoinsInfoImpl::spent() const
|
|
+{
|
|
+ return m_spent;
|
|
+}
|
|
+
|
|
+bool CoinsInfoImpl::frozen() const
|
|
+{
|
|
+ return m_frozen;
|
|
+}
|
|
+
|
|
+uint64_t CoinsInfoImpl::spentHeight() const
|
|
+{
|
|
+ return m_spentHeight;
|
|
+}
|
|
+
|
|
+uint64_t CoinsInfoImpl::amount() const
|
|
+{
|
|
+ return m_amount;
|
|
+}
|
|
+
|
|
+bool CoinsInfoImpl::rct() const {
|
|
+ return m_rct;
|
|
+}
|
|
+
|
|
+bool CoinsInfoImpl::keyImageKnown() const {
|
|
+ return m_keyImageKnown;
|
|
+}
|
|
+
|
|
+size_t CoinsInfoImpl::pkIndex() const {
|
|
+ return m_pkIndex;
|
|
+}
|
|
+
|
|
+uint32_t CoinsInfoImpl::subaddrIndex() const {
|
|
+ return m_subaddrIndex;
|
|
+}
|
|
+
|
|
+uint32_t CoinsInfoImpl::subaddrAccount() const {
|
|
+ return m_subaddrAccount;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::address() const {
|
|
+ return m_address;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::addressLabel() const {
|
|
+ return m_addressLabel;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::keyImage() const {
|
|
+ return m_keyImage;
|
|
+}
|
|
+
|
|
+uint64_t CoinsInfoImpl::unlockTime() const {
|
|
+ return m_unlockTime;
|
|
+}
|
|
+
|
|
+bool CoinsInfoImpl::unlocked() const {
|
|
+ return m_unlocked;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::pubKey() const {
|
|
+ return m_pubKey;
|
|
+}
|
|
+
|
|
+bool CoinsInfoImpl::coinbase() const {
|
|
+ return m_coinbase;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::description() const {
|
|
+ return m_description;
|
|
+}
|
|
+
|
|
+string CoinsInfoImpl::asset() const {
|
|
+ return m_asset;
|
|
+}
|
|
+
|
|
+uint8_t CoinsInfoImpl::type() const {
|
|
+ return m_type;
|
|
+}
|
|
+
|
|
+} // namespace
|
|
+
|
|
+namespace Bitmonero = Monero;
|
|
diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h
|
|
new file mode 100644
|
|
index 000000000..c43e45abd
|
|
--- /dev/null
|
|
+++ b/src/wallet/api/coins_info.h
|
|
@@ -0,0 +1,75 @@
|
|
+#ifndef FEATHER_COINS_INFO_H
|
|
+#define FEATHER_COINS_INFO_H
|
|
+
|
|
+#include "wallet/api/wallet2_api.h"
|
|
+#include <string>
|
|
+#include <ctime>
|
|
+
|
|
+namespace Monero {
|
|
+
|
|
+class CoinsImpl;
|
|
+
|
|
+class CoinsInfoImpl : public CoinsInfo
|
|
+{
|
|
+public:
|
|
+ CoinsInfoImpl();
|
|
+ ~CoinsInfoImpl();
|
|
+
|
|
+ virtual uint64_t blockHeight() const override;
|
|
+ virtual std::string hash() const override;
|
|
+ virtual size_t internalOutputIndex() const override;
|
|
+ virtual uint64_t globalOutputIndex() const override;
|
|
+ virtual bool spent() const override;
|
|
+ virtual bool frozen() const override;
|
|
+ virtual uint64_t spentHeight() const override;
|
|
+ virtual uint64_t amount() const override;
|
|
+ virtual bool rct() const override;
|
|
+ virtual bool keyImageKnown() const override;
|
|
+ virtual size_t pkIndex() const override;
|
|
+ virtual uint32_t subaddrIndex() const override;
|
|
+ virtual uint32_t subaddrAccount() const override;
|
|
+ virtual std::string address() const override;
|
|
+ virtual std::string addressLabel() const override;
|
|
+ virtual std::string keyImage() const override;
|
|
+ virtual uint64_t unlockTime() const override;
|
|
+ virtual bool unlocked() const override;
|
|
+ virtual std::string pubKey() const override;
|
|
+ virtual bool coinbase() const override;
|
|
+ virtual std::string description() const override;
|
|
+ virtual std::string asset() const override;
|
|
+ virtual uint8_t type() const override;
|
|
+
|
|
+private:
|
|
+ uint64_t m_blockHeight;
|
|
+ std::string m_hash;
|
|
+ size_t m_internalOutputIndex;
|
|
+ uint64_t m_globalOutputIndex;
|
|
+ bool m_spent;
|
|
+ bool m_frozen;
|
|
+ uint64_t m_spentHeight;
|
|
+ uint64_t m_amount;
|
|
+ bool m_rct;
|
|
+ bool m_keyImageKnown;
|
|
+ size_t m_pkIndex;
|
|
+ uint32_t m_subaddrIndex;
|
|
+ uint32_t m_subaddrAccount;
|
|
+ std::string m_address;
|
|
+ std::string m_addressLabel;
|
|
+ std::string m_keyImage;
|
|
+ uint64_t m_unlockTime;
|
|
+ bool m_unlocked;
|
|
+ std::string m_pubKey;
|
|
+ bool m_coinbase;
|
|
+ std::string m_description;
|
|
+ std::string m_asset;
|
|
+ uint8_t m_type;
|
|
+
|
|
+ friend class CoinsImpl;
|
|
+
|
|
+};
|
|
+
|
|
+} // namespace
|
|
+
|
|
+namespace Bitmonero = Monero;
|
|
+
|
|
+#endif //FEATHER_COINS_INFO_H
|
|
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
|
|
index 4b0a26a6b..644cf503b 100644
|
|
--- a/src/wallet/api/wallet.cpp
|
|
+++ b/src/wallet/api/wallet.cpp
|
|
@@ -36,6 +36,7 @@
|
|
#include "transaction_history.h"
|
|
#include "address_book.h"
|
|
#include "subaddress.h"
|
|
+#include "coins.h"
|
|
#include "subaddress_account.h"
|
|
#include "common_defines.h"
|
|
#include "common/util.h"
|
|
@@ -444,6 +445,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
|
|
m_wallet->set_refresh_enabled(false);
|
|
m_addressBook.reset(new AddressBookImpl(this));
|
|
m_subaddress.reset(new SubaddressImpl(this));
|
|
+ m_coins.reset(new CoinsImpl(this));
|
|
m_subaddressAccount.reset(new SubaddressAccountImpl(this));
|
|
|
|
|
|
@@ -2018,7 +2020,7 @@ PendingTransaction *WalletImpl::createAuditTransaction(
|
|
// - unconfirmed_transfer_details;
|
|
// - confirmed_transfer_details)
|
|
|
|
-PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transaction_type &tx_type, const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
|
+PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transaction_type &tx_type, const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
|
|
|
|
{
|
|
clearStatus();
|
|
@@ -2057,55 +2059,114 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
|
break;
|
|
}
|
|
}
|
|
- bool error = false;
|
|
- for (size_t i = 0; i < dst_addr.size() && !error; i++) {
|
|
- if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) {
|
|
- // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
|
- setStatusError(tr("Invalid destination address"));
|
|
- error = true;
|
|
- break;
|
|
- }
|
|
- if (info.has_payment_id) {
|
|
- if (!extra_nonce.empty()) {
|
|
- setStatusError(tr("a single transaction cannot use more than one payment id"));
|
|
+ uint64_t max_coin_control_input = 0;
|
|
+ uint64_t max_frozen_input = 0;
|
|
+ try {
|
|
+ bool error = false;
|
|
+ uint64_t amountSum = 0;
|
|
+ for (size_t i = 0; i < dst_addr.size() && !error; i++) {
|
|
+ if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) {
|
|
+ // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
|
+ setStatusError(tr("Invalid destination address"));
|
|
error = true;
|
|
break;
|
|
}
|
|
- set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
|
+ if (info.has_payment_id) {
|
|
+ if (!extra_nonce.empty()) {
|
|
+ setStatusError(tr("a single transaction cannot use more than one payment id"));
|
|
+ error = true;
|
|
+ break;
|
|
+ }
|
|
+ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
|
+ }
|
|
+
|
|
+ if (amount) {
|
|
+ cryptonote::tx_destination_entry de;
|
|
+ de.original = dst_addr[i];
|
|
+ de.addr = info.address;
|
|
+ de.amount = (*amount)[i];
|
|
+ de.asset_type = asset_type;
|
|
+ amountSum += (*amount)[i];
|
|
+ de.is_subaddress = info.is_subaddress;
|
|
+ de.is_integrated = info.has_payment_id;
|
|
+ de.is_return = is_return;
|
|
+ dsts.push_back(de);
|
|
+ } else {
|
|
+ if (subaddr_indices.empty()) {
|
|
+ for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index)
|
|
+ subaddr_indices.insert(index);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
- if (amount) {
|
|
- cryptonote::tx_destination_entry de;
|
|
- de.original = dst_addr[i];
|
|
- de.addr = info.address;
|
|
- de.amount = (*amount)[i];
|
|
- de.asset_type = asset_type;
|
|
- de.is_subaddress = info.is_subaddress;
|
|
- de.is_integrated = info.has_payment_id;
|
|
- de.is_return = is_return;
|
|
- dsts.push_back(de);
|
|
- } else {
|
|
- if (subaddr_indices.empty()) {
|
|
- for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index)
|
|
- subaddr_indices.insert(index);
|
|
+ // uint64_t maxAllowedSpend = m_wallet->unlocked_balance(subaddr_account, true);
|
|
+ // if (maxAllowedSpend < amountSum) {
|
|
+ // error = true;
|
|
+ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount"));
|
|
+ // break;
|
|
+ // }
|
|
+ std::vector<crypto::key_image> preferred_input_list;
|
|
+ if (!preferred_inputs.empty()) {
|
|
+ LOG_ERROR("not empty");
|
|
+
|
|
+ for (const auto &public_key : preferred_inputs) {
|
|
+ crypto::key_image keyImage;
|
|
+ bool r = epee::string_tools::hex_to_pod(public_key, keyImage);
|
|
+ if (!r) {
|
|
+ error = true;
|
|
+ setStatusError(tr("failed to parse key image"));
|
|
+ break;
|
|
+ }
|
|
+ if (m_wallet->frozen(keyImage)) {
|
|
+ error = true;
|
|
+ setStatusError(tr("refusing to spend frozen coin"));
|
|
+ break;
|
|
}
|
|
+
|
|
+ for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) {
|
|
+ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
|
|
+ if (td.m_key_image == keyImage) {
|
|
+ max_coin_control_input += td.amount();
|
|
+ }
|
|
+ if (td.m_frozen) {
|
|
+ max_frozen_input += td.amount();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ preferred_input_list.push_back(keyImage);
|
|
+ }
|
|
+ LOG_ERROR("not empty");
|
|
+
|
|
+ boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_transfers_mutex);
|
|
+ for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) {
|
|
+ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
|
|
+ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<<td.m_spent << ";" << td.m_frozen << ";" << m_wallet->frozen(td));
|
|
+ if (td.m_spent) continue;
|
|
+ LOG_ERROR("is frozen");
|
|
+ if (!td.m_frozen) {
|
|
+ LOG_ERROR("isn't:");
|
|
+ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount());
|
|
+ preferred_input_list.push_back(td.m_key_image);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ for (const auto &de : preferred_input_list) {
|
|
+ LOG_ERROR("preferred input: " << de);
|
|
+ }
|
|
+ if (error) {
|
|
+ break;
|
|
+ }
|
|
+ if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
|
|
+ setStatusError(tr("failed to set up payment id, though it was decoded correctly"));
|
|
+ break;
|
|
}
|
|
- }
|
|
- if (error) {
|
|
- break;
|
|
- }
|
|
- if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
|
|
- setStatusError(tr("failed to set up payment id, though it was decoded correctly"));
|
|
- break;
|
|
- }
|
|
- try {
|
|
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
|
|
fake_outs_count = m_wallet->adjust_mixin(mixin_count);
|
|
|
|
if (amount) {
|
|
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, asset_type, asset_type, converted_tx_type, fake_outs_count, 0 /* unlock_time */,
|
|
adjusted_priority,
|
|
- extra, subaddr_account, subaddr_indices);
|
|
+ extra, subaddr_account, subaddr_indices, preferred_input_list);
|
|
} else {
|
|
std::vector<tools::wallet2::pending_tx> m_pending_txs;
|
|
for (const auto subaddr_index : subaddr_indices) {
|
|
@@ -2127,7 +2188,8 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
|
adjusted_priority,
|
|
extra,
|
|
subaddr_account,
|
|
- std::set<uint32_t> {subaddr_index}
|
|
+ std::set<uint32_t> {subaddr_index},
|
|
+ preferred_input_list
|
|
);
|
|
m_pending_txs.insert(m_pending_txs.end(), result.begin(), result.end());
|
|
|
|
@@ -2165,6 +2227,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
|
writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) %
|
|
print_money(e.available()) %
|
|
print_money(e.tx_amount());
|
|
+ if (max_coin_control_input != 0 &&
|
|
+ max_coin_control_input != e.available()) {
|
|
+ writer << std::endl << boost::format(tr("In addition, coin control was enabled for this transaction, limiting available balance to %s. Make sure that you have enough outputs selected in coin control")) %
|
|
+ print_money(max_coin_control_input);
|
|
+ }
|
|
+ if (max_frozen_input != 0 &&
|
|
+ max_frozen_input != e.available()) {
|
|
+ writer << std::endl << boost::format(tr("In addition, some a total of %s is frozen. Make sure that you have enough outputs unforzen outputs in coin control")) %
|
|
+ print_money(max_frozen_input);
|
|
+ }
|
|
setStatusError(writer.str());
|
|
} catch (const tools::error::not_enough_money& e) {
|
|
std::ostringstream writer;
|
|
@@ -2172,6 +2244,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
|
writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) %
|
|
print_money(e.available()) %
|
|
print_money(e.tx_amount());
|
|
+ if (max_coin_control_input != 0 &&
|
|
+ max_coin_control_input != e.available()) {
|
|
+ writer << std::endl << boost::format(tr("In addition, coin control was enabled for this transaction, limiting available balance to %s. Make sure that you have enough outputs selected in coin control")) %
|
|
+ print_money(max_coin_control_input);
|
|
+ }
|
|
+ if (max_frozen_input != 0 &&
|
|
+ max_frozen_input != e.available()) {
|
|
+ writer << std::endl << boost::format(tr("In addition, some a total of %s is frozen. Make sure that you have enough outputs unforzen outputs in coin control")) %
|
|
+ print_money(max_frozen_input);
|
|
+ }
|
|
setStatusError(writer.str());
|
|
} catch (const tools::error::tx_not_possible& e) {
|
|
std::ostringstream writer;
|
|
@@ -2179,6 +2261,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
|
print_money(e.tx_amount() + e.fee()) %
|
|
print_money(e.tx_amount()) %
|
|
print_money(e.fee());
|
|
+ if (max_coin_control_input != 0 &&
|
|
+ max_coin_control_input != e.available()) {
|
|
+ writer << std::endl << boost::format(tr("In addition, coin control was enabled for this transaction, limiting available balance to %s. Make sure that you have enough outputs selected in coin control")) %
|
|
+ print_money(max_coin_control_input);
|
|
+ }
|
|
+ if (max_frozen_input != 0 &&
|
|
+ max_frozen_input != e.available()) {
|
|
+ writer << std::endl << boost::format(tr("In addition, some a total of %s is frozen. Make sure that you have enough outputs unforzen outputs in coin control")) %
|
|
+ print_money(max_frozen_input);
|
|
+ }
|
|
setStatusError(writer.str());
|
|
} catch (const tools::error::not_enough_outs_to_mix& e) {
|
|
std::ostringstream writer;
|
|
@@ -2222,10 +2304,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
|
}
|
|
|
|
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
|
|
- const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
|
+ const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
|
|
|
|
{
|
|
- return createTransactionMultDest(Monero::transaction_type::TRANSFER, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices);
|
|
+ return createTransactionMultDest(Monero::transaction_type::TRANSFER, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices, preferred_inputs);
|
|
}
|
|
|
|
PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
|
|
@@ -2350,6 +2442,11 @@ AddressBook *WalletImpl::addressBook()
|
|
return m_addressBook.get();
|
|
}
|
|
|
|
+Coins *WalletImpl::coins()
|
|
+{
|
|
+ return m_coins.get();
|
|
+}
|
|
+
|
|
Subaddress *WalletImpl::subaddress()
|
|
{
|
|
return m_subaddress.get();
|
|
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
|
|
index 32e12284b..a82f270e4 100644
|
|
--- a/src/wallet/api/wallet.h
|
|
+++ b/src/wallet/api/wallet.h
|
|
@@ -46,6 +46,7 @@ class PendingTransactionImpl;
|
|
class UnsignedTransactionImpl;
|
|
class AddressBookImpl;
|
|
class SubaddressImpl;
|
|
+class CoinsImpl;
|
|
class SubaddressAccountImpl;
|
|
struct Wallet2CallbackImpl;
|
|
|
|
@@ -167,13 +168,15 @@ public:
|
|
const std::string &asset_type, const bool is_return,
|
|
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
|
|
uint32_t subaddr_account = 0,
|
|
- std::set<uint32_t> subaddr_indices = {}) override;
|
|
+ std::set<uint32_t> subaddr_indices = {},
|
|
+ const std::set<std::string> &preferred_inputs = {}) override;
|
|
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
|
|
optional<uint64_t> amount, uint32_t mixin_count,
|
|
const std::string &asset_type, const bool is_return,
|
|
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
|
|
uint32_t subaddr_account = 0,
|
|
- std::set<uint32_t> subaddr_indices = {}) override;
|
|
+ std::set<uint32_t> subaddr_indices = {},
|
|
+ const std::set<std::string> &preferred_inputs = {}) override;
|
|
virtual PendingTransaction * createSweepUnmixableTransaction() override;
|
|
bool submitTransaction(const std::string &fileName) override;
|
|
bool submitTransactionUR(const std::string &input) override;
|
|
@@ -201,6 +204,7 @@ public:
|
|
PendingTransaction::Priority priority) const override;
|
|
virtual TransactionHistory * history() override;
|
|
virtual AddressBook * addressBook() override;
|
|
+ virtual Coins * coins() override;
|
|
virtual Subaddress * subaddress() override;
|
|
virtual SubaddressAccount * subaddressAccount() override;
|
|
virtual void setListener(WalletListener * l) override;
|
|
@@ -272,6 +276,7 @@ private:
|
|
friend class TransactionHistoryImpl;
|
|
friend struct Wallet2CallbackImpl;
|
|
friend class AddressBookImpl;
|
|
+ friend class CoinsImpl;
|
|
friend class SubaddressImpl;
|
|
friend class SubaddressAccountImpl;
|
|
|
|
@@ -288,6 +293,7 @@ private:
|
|
std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback;
|
|
std::unique_ptr<AddressBookImpl> m_addressBook;
|
|
std::unique_ptr<SubaddressImpl> m_subaddress;
|
|
+ std::unique_ptr<CoinsImpl> m_coins;
|
|
std::unique_ptr<SubaddressAccountImpl> m_subaddressAccount;
|
|
|
|
// multi-threaded refresh stuff
|
|
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
|
|
index be1c3704e..013b5bcba 100644
|
|
--- a/src/wallet/api/wallet2_api.h
|
|
+++ b/src/wallet/api/wallet2_api.h
|
|
@@ -263,6 +263,53 @@ struct AddressBook
|
|
virtual int lookupPaymentID(const std::string &payment_id) const = 0;
|
|
};
|
|
|
|
+/**
|
|
+ * @brief The CoinsInfo - interface for displaying coins information
|
|
+ */
|
|
+struct CoinsInfo
|
|
+{
|
|
+ virtual ~CoinsInfo() = 0;
|
|
+
|
|
+ virtual uint64_t blockHeight() const = 0;
|
|
+ virtual std::string hash() const = 0;
|
|
+ virtual size_t internalOutputIndex() const = 0;
|
|
+ virtual uint64_t globalOutputIndex() const = 0;
|
|
+ virtual bool spent() const = 0;
|
|
+ virtual bool frozen() const = 0;
|
|
+ virtual uint64_t spentHeight() const = 0;
|
|
+ virtual uint64_t amount() const = 0;
|
|
+ virtual bool rct() const = 0;
|
|
+ virtual bool keyImageKnown() const = 0;
|
|
+ virtual size_t pkIndex() const = 0;
|
|
+ virtual uint32_t subaddrIndex() const = 0;
|
|
+ virtual uint32_t subaddrAccount() const = 0;
|
|
+ virtual std::string address() const = 0;
|
|
+ virtual std::string addressLabel() const = 0;
|
|
+ virtual std::string keyImage() const = 0;
|
|
+ virtual uint64_t unlockTime() const = 0;
|
|
+ virtual bool unlocked() const = 0;
|
|
+ virtual std::string pubKey() const = 0;
|
|
+ virtual bool coinbase() const = 0;
|
|
+ virtual std::string description() const = 0;
|
|
+ virtual std::string asset() const = 0;
|
|
+ virtual uint8_t type() const = 0;
|
|
+};
|
|
+
|
|
+struct Coins
|
|
+{
|
|
+ virtual ~Coins() = 0;
|
|
+ virtual int count() const = 0;
|
|
+ virtual CoinsInfo * coin(int index) const = 0;
|
|
+ virtual std::vector<CoinsInfo*> getAll() const = 0;
|
|
+ virtual void refresh() = 0;
|
|
+ virtual void setFrozen(std::string public_key) = 0;
|
|
+ virtual void setFrozen(int index) = 0;
|
|
+ virtual void thaw(std::string public_key) = 0;
|
|
+ virtual void thaw(int index) = 0;
|
|
+ virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0;
|
|
+ virtual void setDescription(const std::string &public_key, const std::string &description) = 0;
|
|
+};
|
|
+
|
|
struct SubaddressRow {
|
|
public:
|
|
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
|
|
@@ -940,7 +985,8 @@ struct Wallet
|
|
const std::string &asset_type, const bool is_return,
|
|
PendingTransaction::Priority = PendingTransaction::Priority_Low,
|
|
uint32_t subaddr_account = 0,
|
|
- std::set<uint32_t> subaddr_indices = {}) = 0;
|
|
+ std::set<uint32_t> subaddr_indices = {},
|
|
+ const std::set<std::string> &preferred_inputs = {}) = 0;
|
|
|
|
/*!
|
|
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
|
|
@@ -875,7 +921,8 @@ struct Wallet
|
|
const std::string &asset_type, const bool is_return,
|
|
PendingTransaction::Priority = PendingTransaction::Priority_Low,
|
|
uint32_t subaddr_account = 0,
|
|
- std::set<uint32_t> subaddr_indices = {}) = 0;
|
|
+ std::set<uint32_t> subaddr_indices = {},
|
|
+ const std::set<std::string> &preferred_inputs = {}) = 0;
|
|
|
|
/*!
|
|
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
|
|
@@ -994,6 +1041,7 @@ struct Wallet
|
|
|
|
virtual TransactionHistory * history() = 0;
|
|
virtual AddressBook * addressBook() = 0;
|
|
+ virtual Coins * coins() = 0;
|
|
virtual Subaddress * subaddress() = 0;
|
|
virtual SubaddressAccount * subaddressAccount() = 0;
|
|
virtual void setListener(WalletListener *) = 0;
|
|
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
|
|
index 806de969a..8720e18b1 100644
|
|
--- a/src/wallet/wallet2.cpp
|
|
+++ b/src/wallet/wallet2.cpp
|
|
@@ -2100,12 +2100,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const
|
|
|
|
return false;
|
|
}
|
|
+void wallet2::freeze(const crypto::public_key &pk)
|
|
+{
|
|
+ freeze(get_transfer_details(pk));
|
|
+}
|
|
//----------------------------------------------------------------------------------------------------
|
|
void wallet2::freeze(const crypto::key_image &ki)
|
|
{
|
|
freeze(get_transfer_details(ki));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------
|
|
+void wallet2::thaw(const crypto::public_key &pk)
|
|
+{
|
|
+ thaw(get_transfer_details(pk));
|
|
+}
|
|
+//----------------------------------------------------------------------------------------------------
|
|
void wallet2::thaw(const crypto::key_image &ki)
|
|
{
|
|
thaw(get_transfer_details(ki));
|
|
@@ -2116,6 +2125,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const
|
|
return frozen(get_transfer_details(ki));
|
|
}
|
|
//----------------------------------------------------------------------------------------------------
|
|
+size_t wallet2::get_transfer_details(const crypto::public_key &pk) const
|
|
+{
|
|
+ for (size_t idx = 0; idx < m_transfers.size(); ++idx)
|
|
+ {
|
|
+ const transfer_details &td = m_transfers[idx];
|
|
+ if (td.get_public_key() == pk) {
|
|
+ return idx;
|
|
+ }
|
|
+ }
|
|
+ CHECK_AND_ASSERT_THROW_MES(false, "Public key not found");
|
|
+}
|
|
+//----------------------------------------------------------------------------------------------------
|
|
size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
|
|
{
|
|
for (size_t idx = 0; idx < m_transfers.size(); ++idx)
|
|
@@ -2621,6 +2643,7 @@ void wallet2::process_new_scanned_transaction(const crypto::hash &txid, const cryp
|
|
continue;
|
|
|
|
// update m_transfers view-incoming scan info, and default values
|
|
+ boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
|
|
transfer_details& td = m_transfers.emplace_back();
|
|
td.m_block_height = height;
|
|
td.m_internal_output_index = local_output_index;
|
|
@@ -11031,7 +11054,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
|
LOG_PRINT_L2("transfer_selected_rct done");
|
|
}
|
|
|
|
-std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::string& asset_type)
|
|
+std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::string& asset_type, const std::vector<crypto::key_image>& preferred_input_list)
|
|
{
|
|
std::vector<size_t> picks;
|
|
float current_output_relatdness = 1.0f;
|
|
@@ -11048,6 +11071,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
|
|
for (size_t i: m_transfers_indices[asset_type])
|
|
{
|
|
const transfer_details& td = m_transfers[i];
|
|
+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
|
+ continue;
|
|
+ }
|
|
if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
|
|
{
|
|
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
|
|
@@ -11069,6 +11095,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
|
|
{
|
|
size_t idx = *i;
|
|
const transfer_details& td = m_transfers[idx];
|
|
+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
|
+ continue;
|
|
+ }
|
|
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
|
|
{
|
|
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
|
|
@@ -11084,6 +11113,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
|
|
{
|
|
size_t idx2 = *j;
|
|
const transfer_details& td2 = m_transfers[idx2];
|
|
+ if (!is_preferred_input(preferred_input_list, td2.m_key_image)) {
|
|
+ continue;
|
|
+ }
|
|
if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below)
|
|
{
|
|
MDEBUG("Ignoring output " << idx2 << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
|
|
@@ -11125,7 +11157,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image,
|
|
// This system allows for sending (almost) the entire balance, since it does
|
|
// not generate spurious change in all txes, thus decreasing the instantaneous
|
|
// usable balance.
|
|
-std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs)
|
|
+std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list, const unique_index_container& subtract_fee_from_outputs)
|
|
{
|
|
//ensure device is let in NONE mode in any case
|
|
hw::device &hwdev = m_account.get_device();
|
|
@@ -11333,6 +11365,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|
for (size_t i: m_transfers_indices[source_asset])
|
|
{
|
|
const transfer_details& td = m_transfers[i];
|
|
+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
|
+ continue;
|
|
+ }
|
|
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
|
|
{
|
|
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold));
|
|
@@ -11528,7 +11563,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|
// will get us a known fee.
|
|
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
|
|
total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee);
|
|
- preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices, source_asset);
|
|
+ preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices, source_asset, preferred_input_list);
|
|
if (!preferred_inputs.empty())
|
|
{
|
|
string s;
|
|
@@ -12022,7 +12057,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
|
|
return true;
|
|
}
|
|
|
|
-std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string &asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
|
+std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string &asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list)
|
|
{
|
|
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
|
|
if (do_carrot_tx_construction)
|
|
@@ -12056,7 +12091,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
|
|
for (const auto& i: m_transfers_indices[asset_type])
|
|
{
|
|
//size_t i = m_transfers_indices[asset_type][idx];
|
|
const transfer_details& td = m_transfers[i];
|
|
+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
|
+ continue;
|
|
+ }
|
|
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
|
|
{
|
|
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
|
|
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
|
|
index a619bdd15..4f324c238 100644
|
|
--- a/src/wallet/wallet2.h
|
|
+++ b/src/wallet/wallet2.h
|
|
@@ -1286,8 +1286,8 @@ private:
|
|
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
|
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
|
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
|
- std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
|
- std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
|
+ std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
|
+ std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {});
|
|
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
|
std::vector<wallet2::pending_tx> create_transactions_return(std::vector<size_t> transfers_indices);
|
|
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, const std::string& asset_type, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
|
@@ -1694,6 +1694,7 @@ private:
|
|
uint64_t get_num_rct_outputs();
|
|
size_t get_num_transfer_details() const { return m_transfers.size(); }
|
|
const transfer_details &get_transfer_details(size_t idx) const;
|
|
+ size_t get_transfer_details(const crypto::public_key &pk) const;
|
|
|
|
uint8_t estimate_current_hard_fork(const uint64_t height = 0) const;
|
|
uint8_t get_current_hard_fork();
|
|
@@ -1835,7 +1836,9 @@ private:
|
|
void freeze(size_t idx);
|
|
void thaw(size_t idx);
|
|
bool frozen(size_t idx) const;
|
|
+ void freeze(const crypto::public_key &pk);
|
|
void freeze(const crypto::key_image &ki);
|
|
+ void thaw(const crypto::public_key &pk);
|
|
void thaw(const crypto::key_image &ki);
|
|
bool frozen(const crypto::key_image &ki) const;
|
|
bool frozen(const transfer_details &td) const;
|
|
@@ -1876,6 +1879,8 @@ private:
|
|
|
|
static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
|
|
|
|
+ boost::shared_mutex m_transfers_mutex;
|
|
+
|
|
bool get_pricing_record(oracle::pricing_record& pr, const uint64_t height);
|
|
bool get_circulating_supply(std::vector<std::pair<std::string, std::string>> &amounts);
|
|
bool get_yield_info(std::vector<cryptonote::yield_block_info>& ybi_data);
|
|
@@ -1962,7 +1967,7 @@ private:
|
|
std::vector<uint64_t> get_unspent_amounts_vector(bool strict);
|
|
uint64_t get_dynamic_base_fee_estimate();
|
|
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
|
|
- std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::string& asset_type);
|
|
+ std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::string& asset_type, const std::vector<crypto::key_image>& preferred_input_list);
|
|
void set_spent(size_t idx, uint64_t height);
|
|
void set_spent(const crypto::key_image &ki, const uint64_t height);
|
|
void set_unspent(size_t idx);
|
|
--
|
|
2.48.1
|
|
|