aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/pending_transaction.cpp6
-rw-r--r--src/wallet/api/wallet.cpp61
-rw-r--r--src/wallet/api/wallet.h15
-rw-r--r--src/wallet/api/wallet2_api.h5
-rw-r--r--src/wallet/ringdb.cpp15
-rw-r--r--src/wallet/wallet2.cpp96
-rw-r--r--src/wallet/wallet2.h9
-rw-r--r--src/wallet/wallet_rpc_server.cpp30
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h6
9 files changed, 177 insertions, 66 deletions
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index 8d200220d..913e3156f 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -200,7 +200,11 @@ std::string PendingTransactionImpl::multisigSignData() {
throw std::runtime_error("wallet is not multisig");
}
- auto cipher = m_wallet.m_wallet->save_multisig_tx(m_pending_tx);
+ tools::wallet2::multisig_tx_set txSet;
+ txSet.m_ptx = m_pending_tx;
+ txSet.m_signers = m_signers;
+ auto cipher = m_wallet.m_wallet->save_multisig_tx(txSet);
+
return epee::string_tools::buff_to_hex_nodelimer(cipher);
} catch (const std::exception& e) {
m_status = Status_Error;
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index a7fc3dcd7..de1bfdae1 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -96,6 +96,9 @@ namespace {
throw runtime_error("Multisig wallet is not finalized yet");
}
}
+ void checkMultisigWalletReady(const std::unique_ptr<tools::wallet2> &wallet) {
+ return checkMultisigWalletReady(wallet.get());
+ }
void checkMultisigWalletNotReady(const tools::wallet2* wallet) {
if (!wallet) {
@@ -111,6 +114,9 @@ namespace {
throw runtime_error("Multisig wallet is already finalized");
}
}
+ void checkMultisigWalletNotReady(const std::unique_ptr<tools::wallet2> &wallet) {
+ return checkMultisigWalletNotReady(wallet.get());
+ }
}
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
@@ -376,15 +382,15 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
, m_rebuildWalletCache(false)
, m_is_connected(false)
{
- m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true);
- m_history = new TransactionHistoryImpl(this);
- m_wallet2Callback = new Wallet2CallbackImpl(this);
- m_wallet->callback(m_wallet2Callback);
+ m_wallet.reset(new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true));
+ m_history.reset(new TransactionHistoryImpl(this));
+ m_wallet2Callback.reset(new Wallet2CallbackImpl(this));
+ m_wallet->callback(m_wallet2Callback.get());
m_refreshThreadDone = false;
m_refreshEnabled = false;
- m_addressBook = new AddressBookImpl(this);
- m_subaddress = new SubaddressImpl(this);
- m_subaddressAccount = new SubaddressAccountImpl(this);
+ m_addressBook.reset(new AddressBookImpl(this));
+ m_subaddress.reset(new SubaddressImpl(this));
+ m_subaddressAccount.reset(new SubaddressAccountImpl(this));
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
@@ -399,18 +405,13 @@ WalletImpl::~WalletImpl()
{
LOG_PRINT_L1(__FUNCTION__);
+ m_wallet->callback(NULL);
// Pause refresh thread - prevents refresh from starting again
pauseRefresh();
// Close wallet - stores cache and stops ongoing refresh operation
close(false); // do not store wallet as part of the closing activities
// Stop refresh thread
stopRefresh();
- delete m_wallet2Callback;
- delete m_history;
- delete m_addressBook;
- delete m_subaddress;
- delete m_subaddressAccount;
- delete m_wallet;
LOG_PRINT_L1(__FUNCTION__ << " finished");
}
@@ -609,7 +610,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
LOG_PRINT_L1("Generated new view only wallet from keys");
}
if(has_spendkey && !has_viewkey) {
- m_wallet->generate(path, password, spendkey, true, false, false);
+ m_wallet->generate(path, password, spendkey, true, false);
setSeedLanguage(language);
LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language);
}
@@ -629,7 +630,7 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
m_recoveringFromDevice = true;
try
{
- m_wallet->restore(path, password, device_name, false);
+ m_wallet->restore(path, password, device_name);
LOG_PRINT_L1("Generated new wallet from device: " + device_name);
}
catch (const std::exception& e) {
@@ -1565,22 +1566,22 @@ void WalletImpl::disposeTransaction(PendingTransaction *t)
TransactionHistory *WalletImpl::history()
{
- return m_history;
+ return m_history.get();
}
AddressBook *WalletImpl::addressBook()
{
- return m_addressBook;
+ return m_addressBook.get();
}
Subaddress *WalletImpl::subaddress()
{
- return m_subaddress;
+ return m_subaddress.get();
}
SubaddressAccount *WalletImpl::subaddressAccount()
{
- return m_subaddressAccount;
+ return m_subaddressAccount.get();
}
void WalletImpl::setListener(WalletListener *l)
@@ -2166,6 +2167,28 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool
return true;
}
+bool WalletImpl::blackballOutput(const std::string &amount, const std::string &offset)
+{
+ uint64_t raw_amount, raw_offset;
+ if (!epee::string_tools::get_xtype_from_string(raw_amount, amount))
+ {
+ setStatusError(tr("Failed to parse output amount"));
+ return false;
+ }
+ if (!epee::string_tools::get_xtype_from_string(raw_offset, offset))
+ {
+ setStatusError(tr("Failed to parse output offset"));
+ return false;
+ }
+ bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset));
+ if (!ret)
+ {
+ setStatusError(tr("Failed to blackball output"));
+ return false;
+ }
+ return true;
+}
+
bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset)
{
uint64_t raw_amount, raw_offset;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 8c20af347..6d343888b 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -183,7 +183,8 @@ public:
virtual std::string getDefaultDataDir() const override;
virtual bool lightWalletLogin(bool &isNewWallet) const override;
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override;
- virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) override;
+ virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) override;
+ virtual bool blackballOutput(const std::string &amount, const std::string &offset) override;
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override;
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const override;
virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const override;
@@ -216,16 +217,16 @@ private:
friend class SubaddressImpl;
friend class SubaddressAccountImpl;
- tools::wallet2 * m_wallet;
+ std::unique_ptr<tools::wallet2> m_wallet;
mutable boost::mutex m_statusMutex;
mutable int m_status;
mutable std::string m_errorString;
std::string m_password;
- TransactionHistoryImpl * m_history;
- Wallet2CallbackImpl * m_wallet2Callback;
- AddressBookImpl * m_addressBook;
- SubaddressImpl * m_subaddress;
- SubaddressAccountImpl * m_subaddressAccount;
+ std::unique_ptr<TransactionHistoryImpl> m_history;
+ std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback;
+ std::unique_ptr<AddressBookImpl> m_addressBook;
+ std::unique_ptr<SubaddressImpl> m_subaddress;
+ std::unique_ptr<SubaddressAccountImpl> m_subaddressAccount;
// multi-threaded refresh stuff
std::atomic<bool> m_refreshEnabled;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 184c0aa73..ec1a84877 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -887,7 +887,10 @@ struct Wallet
virtual bool rescanSpent() = 0;
//! blackballs a set of outputs
- virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) = 0;
+ virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) = 0;
+
+ //! blackballs an output
+ virtual bool blackballOutput(const std::string &amount, const std::string &offset) = 0;
//! unblackballs an output
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0;
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index e9fc6866d..e5995e7fb 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -398,6 +398,8 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
+ dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);
+ THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
MDB_val key, data;
for (const std::pair<uint64_t, uint64_t> &output: outputs)
@@ -411,25 +413,22 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
{
case BLACKBALL_BLACKBALL:
MDEBUG("Blackballing output " << output.first << "/" << output.second);
- dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP);
+ dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
MDEBUG("Unblackballing output " << output.first << "/" << output.second);
- dbr = mdb_del(txn, dbi_blackballs, &key, &data);
- if (dbr == MDB_NOTFOUND)
- dbr = 0;
+ dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
+ if (dbr == 0)
+ dbr = mdb_cursor_del(cursor, 0);
break;
case BLACKBALL_QUERY:
- dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);
- THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr)));
ret = dbr != MDB_NOTFOUND;
if (dbr == MDB_NOTFOUND)
dbr = 0;
- mdb_cursor_close(cursor);
break;
case BLACKBALL_CLEAR:
break;
@@ -439,6 +438,8 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
}
+ mdb_cursor_close(cursor);
+
if (op == BLACKBALL_CLEAR)
{
dbr = mdb_drop(txn, dbi_blackballs, 0);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 841c5c2ab..7871fe99c 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -66,6 +66,7 @@ using namespace epee;
#include "memwipe.h"
#include "common/base58.h"
#include "common/dns_utils.h"
+#include "common/notify.h"
#include "ringct/rctSigs.h"
#include "ringdb.h"
@@ -113,9 +114,9 @@ using namespace cryptonote;
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
-#define SEGREGATION_FORK_HEIGHT 1546000
-#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
-#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
+#define SEGREGATION_FORK_HEIGHT 99999999
+#define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
+#define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
#define FIRST_REFRESH_GRANULARITY 1024
@@ -199,6 +200,7 @@ struct options {
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
+ const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
@@ -293,7 +295,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
trusted_daemon = false;
if (tools::is_local_address(daemon_address))
{
- MINFO(tr("Daemon is local, assuming trusted"));
+ MINFO(tools::wallet2::tr("Daemon is local, assuming trusted"));
trusted_daemon = true;
}
}
@@ -305,6 +307,17 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->device_name(device_name);
+
+ try
+ {
+ if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
+ wallet->set_tx_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, opts.tx_notify).c_str())));
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to parse tx notify spec");
+ }
+
return wallet;
}
@@ -334,10 +347,10 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
- return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
+ return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify);
}
-std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
@@ -347,6 +360,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly
fails. This large wrapper is for the use of that macro */
std::unique_ptr<tools::wallet2> wallet;
+ epee::wipeable_string password;
const auto do_generate = [&]() -> bool {
std::string buf;
if (!epee::file_io_utils::load_file_to_string(json_file, buf)) {
@@ -484,10 +498,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
if (!field_seed.empty())
{
wallet->generate(field_filename, field_password, recovery_key, recover, false, create_address_file);
+ password = field_password;
}
else if (field_viewkey.empty() && !field_spendkey.empty())
{
wallet->generate(field_filename, field_password, spendkey, recover, false, create_address_file);
+ password = field_password;
}
else
{
@@ -514,6 +530,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet"));
}
wallet->generate(field_filename, field_password, address, viewkey, create_address_file);
+ password = field_password;
}
else
{
@@ -521,6 +538,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
wallet->generate(field_filename, field_password, address, spendkey, viewkey, create_address_file);
+ password = field_password;
}
}
}
@@ -533,9 +551,9 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
if (do_generate())
{
- return wallet;
+ return {std::move(wallet), tools::password_container(password)};
}
- return nullptr;
+ return {nullptr, tools::password_container{}};
}
static void throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method)
@@ -876,9 +894,10 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
command_line::add_arg(desc_params, opts.hw_device);
+ command_line::add_arg(desc_params, opts.tx_notify);
}
-std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::pair<std::unique_ptr<wallet2>, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
return generate_from_json(json_file, vm, unattended, opts, password_prompter);
@@ -1363,9 +1382,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
- std::vector<size_t> outs;
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
+ bool notify = false;
std::vector<tx_extra_field> local_tx_extra_fields;
if (tx_cache_data.tx_extra_fields.empty())
@@ -1385,6 +1404,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t total_received_1 = 0;
while (!tx.vout.empty())
{
+ std::vector<size_t> outs;
// if tx.vout is not empty, we loop through all tx pubkeys
tx_extra_pub_key pub_key_field;
@@ -1605,6 +1625,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
}
total_received_1 += amount;
+ notify = true;
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount)
{
@@ -1672,6 +1693,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
}
total_received_1 += extra_amount;
+ notify = true;
}
}
}
@@ -1745,10 +1767,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
// remove change sent to the spending subaddress account from the list of received funds
+ uint64_t sub_change = 0;
for (auto i = tx_money_got_in_outs.begin(); i != tx_money_got_in_outs.end();)
{
if (subaddr_account && i->first.major == *subaddr_account)
+ {
+ sub_change += i->second;
i = tx_money_got_in_outs.erase(i);
+ }
else
++i;
}
@@ -1793,7 +1819,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
}
- uint64_t total_received_2 = 0;
+ uint64_t total_received_2 = sub_change;
for (const auto& i : tx_money_got_in_outs)
total_received_2 += i.second;
if (total_received_1 != total_received_2)
@@ -1828,6 +1854,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
}
}
+
+ if (notify)
+ {
+ std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
+ if (tx_notify)
+ tx_notify->notify(epee::string_tools::pod_to_hex(txid).c_str());
+ }
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
@@ -3352,9 +3385,16 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
encrypted_secret_keys = field_encrypted_secret_keys;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
- if (m_device_name.empty() && field_device_name_found)
+ if (m_device_name.empty())
{
- m_device_name = field_device_name;
+ if (field_device_name_found)
+ {
+ m_device_name = field_device_name;
+ }
+ else
+ {
+ m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
+ }
}
}
else
@@ -6929,11 +6969,23 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
// while we still need more mixins
+ uint64_t num_usable_outs = num_outs;
+ bool allow_blackballed = false;
while (num_found < requested_outputs_count)
{
// if we've gone through every possible output, we've gotten all we can
- if (seen_indices.size() == num_outs)
- break;
+ if (seen_indices.size() == num_usable_outs)
+ {
+ // there is a first pass which rejects blackballed outputs, then a second pass
+ // which allows them if we don't have enough non blackballed outputs to reach
+ // the required amount of outputs (since consensus does not care about blackballed
+ // outputs, we still need to reach the minimum ring size)
+ if (allow_blackballed)
+ break;
+ MINFO("Not enough non blackballed outputs, we'll allow blackballed ones");
+ allow_blackballed = true;
+ num_usable_outs = num_outs;
+ }
// get a random output index from the DB. If we've already seen it,
// return to the top of the loop and try again, otherwise add it to the
@@ -7007,14 +7059,26 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (seen_indices.count(i))
continue;
- if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
+ if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
+ {
+ --num_usable_outs;
continue;
+ }
seen_indices.emplace(i);
LOG_PRINT_L2("picking " << i << " as " << type);
req.outputs.push_back({amount, i});
++num_found;
}
+
+ // if we had enough unusable outputs, we might fall off here and still
+ // have too few outputs, so we stuff with one to keep counts good, and
+ // we'll error out later
+ while (num_found < requested_outputs_count)
+ {
+ req.outputs.push_back({amount, 0});
+ ++num_found;
+ }
}
// sort the subsection, to ensure the daemon doesn't know which output is ours
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 440ac709b..680196f01 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -68,6 +68,7 @@ namespace tools
{
class ringdb;
class wallet2;
+ class Notify;
class wallet_keys_unlocker
{
@@ -176,7 +177,7 @@ namespace tools
static void init_options(boost::program_options::options_description& desc_params);
//! Uses stdin and stdout. Returns a wallet2 if no errors.
- static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::pair<std::unique_ptr<wallet2>, password_container> make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container>
@@ -554,7 +555,7 @@ namespace tools
* \param device_name name of HW to use
* \param create_address_file Whether to create an address file
*/
- void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file);
+ void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = false);
/*!
* \brief Creates a multisig wallet
@@ -1184,6 +1185,8 @@ namespace tools
void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password);
+ void set_tx_notify(const std::shared_ptr<tools::Notify> &notify) { m_tx_notify = notify; }
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1370,6 +1373,8 @@ namespace tools
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
bool m_unattended;
+
+ std::shared_ptr<tools::Notify> m_tx_notify;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 25)
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index b8379448d..e0b631aaf 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1554,7 +1554,7 @@ namespace tools
rpc_transfers.global_index = td.m_global_output_index;
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor};
- rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
+ rpc_transfers.key_image = td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
res.transfers.push_back(rpc_transfers);
}
}
@@ -2173,8 +2173,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
- fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
- return true;
+ res.transfers.resize(res.transfers.size() + 1);
+ fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
}
}
@@ -2183,8 +2183,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
if (i->first == txid)
{
- fill_transfer_entry(res.transfer, i->first, i->second);
- return true;
+ res.transfers.resize(res.transfers.size() + 1);
+ fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@@ -2193,8 +2193,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
- fill_transfer_entry(res.transfer, i->first, i->second);
- return true;
+ res.transfers.resize(res.transfers.size() + 1);
+ fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@@ -2205,11 +2205,17 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
if (i->second.m_pd.m_tx_hash == txid)
{
- fill_transfer_entry(res.transfer, i->first, i->second);
- return true;
+ res.transfers.resize(res.transfers.size() + 1);
+ fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
+ if (!res.transfers.empty())
+ {
+ res.transfer = res.transfers.front(); // backward compat
+ return true;
+ }
+
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction not found.";
return false;
@@ -3401,7 +3407,8 @@ public:
{
try
{
- wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
+ auto rc = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
+ wal = std::move(rc.first);
}
catch (const std::exception &e)
{
@@ -3506,6 +3513,8 @@ public:
std::string const t_executor::NAME = "Wallet RPC Daemon";
int main(int argc, char** argv) {
+ TRY_ENTRY();
+
namespace po = boost::program_options;
const auto arg_wallet_file = wallet_args::arg_wallet_file();
@@ -3549,4 +3558,5 @@ int main(int argc, char** argv) {
}
return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
+ CATCH_ENTRY_L0("main", 1);
}
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index c769929f5..2377b69e3 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 3
+#define WALLET_RPC_VERSION_MINOR 4
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -883,13 +883,11 @@ namespace wallet_rpc
std::string transfer_type;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
- bool verbose;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer_type)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
- KV_SERIALIZE(verbose)
END_KV_SERIALIZE_MAP()
};
@@ -1401,9 +1399,11 @@ namespace wallet_rpc
struct response
{
transfer_entry transfer;
+ std::list<transfer_entry> transfers;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer);
+ KV_SERIALIZE(transfers);
END_KV_SERIALIZE_MAP()
};
};