aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt2
-rw-r--r--src/wallet/api/wallet.cpp83
-rw-r--r--src/wallet/api/wallet.h4
-rw-r--r--src/wallet/api/wallet2_api.h29
-rw-r--r--src/wallet/api/wallet_manager.cpp8
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/ringdb.cpp117
-rw-r--r--src/wallet/ringdb.h9
-rw-r--r--src/wallet/wallet2.cpp564
-rw-r--r--src/wallet/wallet2.h254
-rw-r--r--src/wallet/wallet_errors.h6
-rw-r--r--src/wallet/wallet_rpc_server.cpp321
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h6
13 files changed, 630 insertions, 774 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index a16f4fe19..be10b9f62 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -90,6 +90,8 @@ target_link_libraries(wallet_rpc_server
cncrypto
common
version
+ daemonizer
+ ${EPEE_READLINE}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 3780d7271..8b25096a2 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -376,7 +376,7 @@ 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);
+ 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);
@@ -629,7 +629,7 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
m_recoveringFromDevice = true;
try
{
- m_wallet->restore(path, password, device_name);
+ m_wallet->restore(path, password, device_name, false);
LOG_PRINT_L1("Generated new wallet from device: " + device_name);
}
catch (const std::exception& e) {
@@ -639,6 +639,11 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
return true;
}
+Wallet::Device WalletImpl::getDeviceType() const
+{
+ return static_cast<Wallet::Device>(m_wallet->get_device_type());
+}
+
bool WalletImpl::open(const std::string &path, const std::string &password)
{
clearStatus();
@@ -1244,6 +1249,20 @@ size_t WalletImpl::importMultisigImages(const vector<string>& images) {
return 0;
}
+bool WalletImpl::hasMultisigPartialKeyImages() const {
+ try {
+ clearStatus();
+ checkMultisigWalletReady(m_wallet);
+
+ return m_wallet->has_multisig_partial_key_images();
+ } catch (const exception& e) {
+ LOG_ERROR("Error on checking for partial multisig key images: ") << e.what();
+ setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what());
+ }
+
+ return false;
+}
+
PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) {
try {
clearStatus();
@@ -1381,8 +1400,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
- } catch (const tools::error::get_random_outs_error &e) {
- setStatusError((boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str());
+ } catch (const tools::error::get_outs_error &e) {
+ setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::not_enough_unlocked_money& e) {
std::ostringstream writer;
@@ -1463,8 +1482,8 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
- } catch (const tools::error::get_random_outs_error&) {
- setStatusError(tr("failed to get random outputs to mix"));
+ } catch (const tools::error::get_outs_error&) {
+ setStatusError(tr("failed to get outputs to mix"));
} catch (const tools::error::not_enough_unlocked_money& e) {
setStatusError("");
std::ostringstream writer;
@@ -2033,7 +2052,7 @@ bool WalletImpl::isNewWallet() const
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
{
// claim RPC so there's no in-memory encryption for now
- if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
+ if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
@@ -2095,21 +2114,36 @@ bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const
return m_wallet->use_fork_rules(version,early_blocks);
}
-bool WalletImpl::blackballOutputs(const std::vector<std::string> &pubkeys, bool add)
+bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool add)
{
- std::vector<crypto::public_key> raw_pubkeys;
- raw_pubkeys.reserve(pubkeys.size());
- for (const std::string &str: pubkeys)
+ std::vector<std::pair<uint64_t, uint64_t>> raw_outputs;
+ raw_outputs.reserve(outputs.size());
+ uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
+ for (const std::string &str: outputs)
{
- crypto::public_key pkey;
- if (!epee::string_tools::hex_to_pod(str, pkey))
+ if (sscanf(str.c_str(), "@%" PRIu64, &amount) == 1)
+ continue;
+ if (amount == std::numeric_limits<uint64_t>::max())
{
- setStatusError(tr("Failed to parse output public key"));
- return false;
+ setStatusError("First line is not an amount");
+ return true;
+ }
+ if (sscanf(str.c_str(), "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits<uint64_t>::max() - offset)
+ {
+ while (num_offsets--)
+ raw_outputs.push_back(std::make_pair(amount, offset++));
+ }
+ else if (sscanf(str.c_str(), "%" PRIu64, &offset) == 1)
+ {
+ raw_outputs.push_back(std::make_pair(amount, offset));
+ }
+ else
+ {
+ setStatusError(tr("Invalid output: ") + str);
+ return false;
}
- raw_pubkeys.push_back(pkey);
}
- bool ret = m_wallet->set_blackballed_outputs(raw_pubkeys, add);
+ bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add);
if (!ret)
{
setStatusError(tr("Failed to set blackballed outputs"));
@@ -2118,15 +2152,20 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &pubkeys, bool
return true;
}
-bool WalletImpl::unblackballOutput(const std::string &pubkey)
+bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset)
{
- crypto::public_key raw_pubkey;
- if (!epee::string_tools::hex_to_pod(pubkey, raw_pubkey))
+ 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 public key"));
+ setStatusError(tr("Failed to parse output offset"));
return false;
}
- bool ret = m_wallet->unblackball_output(raw_pubkey);
+ bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret)
{
setStatusError(tr("Failed to unblackball output"));
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 0f3b1ce04..e3a300317 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -79,6 +79,7 @@ public:
bool recoverFromDevice(const std::string &path,
const std::string &password,
const std::string &device_name);
+ Device getDeviceType() const;
bool close(bool store = true);
std::string seed() const override;
std::string getSeedLanguage() const override;
@@ -139,6 +140,7 @@ public:
bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
bool exportMultisigImages(std::string& images) override;
size_t importMultisigImages(const std::vector<std::string>& images) override;
+ bool hasMultisigPartialKeyImages() const override;
PendingTransaction* restoreMultisigTransaction(const std::string& signData) override;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
@@ -181,7 +183,7 @@ public:
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 unblackballOutput(const std::string &pubkey) 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;
virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative) override;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 5a52c6b17..e0d491705 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -373,6 +373,10 @@ struct WalletListener
*/
struct Wallet
{
+ enum Device {
+ Device_Software = 0,
+ Device_Ledger = 1
+ };
enum Status {
Status_Ok,
@@ -720,6 +724,11 @@ struct Wallet
* @return number of imported images
*/
virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0;
+ /**
+ * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants
+ * @return true if there are partial key images
+ */
+ virtual bool hasMultisigPartialKeyImages() const = 0;
/**
* @brief restoreMultisigTransaction creates PendingTransaction from signData
@@ -875,7 +884,7 @@ struct Wallet
virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) = 0;
//! unblackballs an output
- virtual bool unblackballOutput(const std::string &pubkey) = 0;
+ virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0;
//! gets the ring used for a key image, if any
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const = 0;
@@ -906,6 +915,12 @@ struct Wallet
virtual bool unlockKeysFile() = 0;
//! returns true if the keys file is locked
virtual bool isKeysFileLocked() = 0;
+
+ /*!
+ * \brief Queries backing device for wallet keys
+ * \return Device they are on
+ */
+ virtual Device getDeviceType() const = 0;
};
/**
@@ -1092,6 +1107,18 @@ struct WalletManager
virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0;
/*!
+ * \brief determine the key storage for the specified wallet file
+ * \param device_type (OUT) wallet backend as enumerated in Wallet::Device
+ * \param keys_file_name Keys file to verify password for
+ * \param password Password to verify
+ * \return true if password correct, else false
+ *
+ * for verification only - determines key storage hardware
+ *
+ */
+ virtual bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const = 0;
+
+ /*!
* \brief findWallets - searches for the wallet files by given path name recursively
* \param path - starting point to search
* \return - list of strings with found wallets (absolute paths);
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 3851ca9cc..5b262f1b7 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -167,6 +167,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name,
return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default"), kdf_rounds);
}
+bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const
+{
+ hw::device::device_type type;
+ bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds);
+ device_type = static_cast<Wallet::Device>(type);
+ return r;
+}
+
std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path)
{
std::vector<std::string> result;
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 8b1c8be7f..573e80d1a 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -76,6 +76,7 @@ public:
virtual bool closeWallet(Wallet *wallet, bool store = true) override;
bool walletExists(const std::string &path) override;
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
+ bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const;
std::vector<std::string> findWallets(const std::string &path) override;
std::string errorString() const override;
void setDaemonAddress(const std::string &address) override;
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index 3f2634c8b..e9fc6866d 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -55,6 +55,13 @@ static int compare_hash32(const MDB_val *a, const MDB_val *b)
return 0;
}
+static int compare_uint64(const MDB_val *a, const MDB_val *b)
+{
+ const uint64_t va = *(const uint64_t*) a->mv_data;
+ const uint64_t vb = *(const uint64_t*) b->mv_data;
+ return va < vb ? -1 : va > vb;
+}
+
static std::string compress_ring(const std::vector<uint64_t> &ring)
{
std::string s;
@@ -146,7 +153,7 @@ static int resize_env(MDB_env *env, const char *db_path, size_t needed)
MDB_stat mst;
int ret;
- needed = std::max(needed, (size_t)(2ul * 1024 * 1024)); // at least 2 MB
+ needed = std::max(needed, (size_t)(100ul * 1024 * 1024)); // at least 100 MB
ret = mdb_env_info(env, &mei);
if (ret)
@@ -217,9 +224,9 @@ ringdb::ringdb(std::string filename, const std::string &genesis):
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
mdb_set_compare(txn, dbi_rings, compare_hash32);
- dbr = mdb_dbi_open(txn, ("blackballs-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
+ dbr = mdb_dbi_open(txn, ("blackballs2-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
- mdb_set_dupsort(txn, dbi_blackballs, compare_hash32);
+ mdb_set_dupsort(txn, dbi_blackballs, compare_uint64);
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
@@ -374,7 +381,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return true;
}
-bool ringdb::blackball_worker(const crypto::public_key &output, int op)
+bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op)
{
MDB_txn *txn;
MDB_cursor *cursor;
@@ -382,49 +389,61 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op)
bool tx_active = false;
bool ret = true;
- dbr = resize_env(env, filename.c_str(), 32 * 2); // a pubkey, and some slack
+ THROW_WALLET_EXCEPTION_IF(outputs.size() > 1 && op == BLACKBALL_QUERY, tools::error::wallet_internal_error, "Blackball query only makes sense for a single output");
+
+ dbr = resize_env(env, filename.c_str(), 32 * 2 * outputs.size()); // a pubkey, and some slack
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
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;
- MDB_val key = zerokeyval;
- MDB_val data;
- data.mv_data = (void*)&output;
- data.mv_size = sizeof(output);
- switch (op)
+ MDB_val key, data;
+ for (const std::pair<uint64_t, uint64_t> &output: outputs)
+ {
+ key.mv_data = (void*)&output.first;
+ key.mv_size = sizeof(output.first);
+ data.mv_data = (void*)&output.second;
+ data.mv_size = sizeof(output.second);
+
+ switch (op)
+ {
+ case BLACKBALL_BLACKBALL:
+ MDEBUG("Blackballing output " << output.first << "/" << output.second);
+ dbr = mdb_put(txn, dbi_blackballs, &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;
+ 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;
+ default:
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op");
+ }
+ THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
+ }
+
+ if (op == BLACKBALL_CLEAR)
{
- case BLACKBALL_BLACKBALL:
- MDEBUG("Blackballing output " << output);
- dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA);
- if (dbr == MDB_KEYEXIST)
- dbr = 0;
- break;
- case BLACKBALL_UNBLACKBALL:
- MDEBUG("Unblackballing output " << output);
- dbr = mdb_del(txn, dbi_blackballs, &key, &data);
- if (dbr == MDB_NOTFOUND)
- dbr = 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:
- dbr = mdb_drop(txn, dbi_blackballs, 0);
- break;
- default:
- THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op");
+ dbr = mdb_drop(txn, dbi_blackballs, 0);
+ THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to clear blackballs table: " + std::string(mdb_strerror(dbr)));
}
- THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn blackballing output to database: " + std::string(mdb_strerror(dbr)));
@@ -432,24 +451,32 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op)
return ret;
}
-bool ringdb::blackball(const crypto::public_key &output)
+bool ringdb::blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs)
+{
+ return blackball_worker(outputs, BLACKBALL_BLACKBALL);
+}
+
+bool ringdb::blackball(const std::pair<uint64_t, uint64_t> &output)
{
- return blackball_worker(output, BLACKBALL_BLACKBALL);
+ std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
+ return blackball_worker(outputs, BLACKBALL_BLACKBALL);
}
-bool ringdb::unblackball(const crypto::public_key &output)
+bool ringdb::unblackball(const std::pair<uint64_t, uint64_t> &output)
{
- return blackball_worker(output, BLACKBALL_UNBLACKBALL);
+ std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
+ return blackball_worker(outputs, BLACKBALL_UNBLACKBALL);
}
-bool ringdb::blackballed(const crypto::public_key &output)
+bool ringdb::blackballed(const std::pair<uint64_t, uint64_t> &output)
{
- return blackball_worker(output, BLACKBALL_QUERY);
+ std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
+ return blackball_worker(outputs, BLACKBALL_QUERY);
}
bool ringdb::clear_blackballs()
{
- return blackball_worker(crypto::public_key(), BLACKBALL_CLEAR);
+ return blackball_worker(std::vector<std::pair<uint64_t, uint64_t>>(), BLACKBALL_CLEAR);
}
}
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index 6b4bce124..7b448b0d7 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -49,13 +49,14 @@ namespace tools
bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
- bool blackball(const crypto::public_key &output);
- bool unblackball(const crypto::public_key &output);
- bool blackballed(const crypto::public_key &output);
+ bool blackball(const std::pair<uint64_t, uint64_t> &output);
+ bool blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs);
+ bool unblackball(const std::pair<uint64_t, uint64_t> &output);
+ bool blackballed(const std::pair<uint64_t, uint64_t> &output);
bool clear_blackballs();
private:
- bool blackball_worker(const crypto::public_key &output, int op);
+ bool blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op);
private:
std::string filename;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index a50a88b59..4e93309ed 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -161,6 +161,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"), ""};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
@@ -200,7 +201,7 @@ std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_siz
return get_weight_string(get_transaction_weight(tx, blob_size));
}
-std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<tools::wallet2> make_basic(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);
@@ -211,6 +212,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
+ auto device_name = command_line::get_arg(vm, opts.hw_device);
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
@@ -261,10 +263,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
catch (const std::exception &e) { }
}
- std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds));
- wallet->init(rpc, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
+ std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
+ wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
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);
return wallet;
}
@@ -297,7 +300,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
return password_prompter(verify ? tr("Enter a new password for the wallet") : 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 rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+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)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
@@ -435,7 +438,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
- wallet.reset(make_basic(vm, rpc, opts, password_prompter).release());
+ wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
@@ -721,8 +724,11 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<too
w(w),
locked(password != boost::none)
{
- if (!locked || w.is_rpc())
+ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt)
+ {
+ locked = false;
return;
+ }
const epee::wipeable_string pass = password->password();
w.generate_chacha_key_from_password(pass, key);
w.decrypt_keys(key);
@@ -745,7 +751,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
w.encrypt_keys(key);
}
-wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
+wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
m_run(true),
@@ -764,7 +770,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_explicit_refresh_from_block_height(true),
m_confirm_missing_payment_id(true),
m_confirm_non_default_ring_size(true),
- m_ask_password(true),
+ m_ask_password(AskPasswordToDecrypt),
m_min_output_count(0),
m_min_output_value(0),
m_merge_destinations(false),
@@ -788,12 +794,12 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_light_wallet_connected(false),
m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0),
- m_key_on_device(false),
+ m_key_device_type(hw::device::device_type::SOFTWARE),
m_ring_history_saved(false),
m_ringdb(),
m_last_block_reward(0),
m_encrypt_keys_after_refresh(boost::none),
- m_rpc(false)
+ m_unattended(unattended)
{
}
@@ -811,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v
return command_line::get_arg(vm, options().stagenet);
}
+std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
+{
+ return command_line::get_arg(vm, options().hw_device);
+}
+
void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
@@ -826,16 +837,17 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
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);
}
-std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+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)
{
const options opts{};
- return generate_from_json(json_file, vm, rpc, opts, password_prompter);
+ return generate_from_json(json_file, vm, unattended, opts, password_prompter);
}
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
- const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+ const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
auto pwd = get_password(vm, opts, password_prompter, false);
@@ -843,7 +855,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
{
return {nullptr, password_container{}};
}
- auto wallet = make_basic(vm, rpc, opts, password_prompter);
+ auto wallet = make_basic(vm, unattended, opts, password_prompter);
if (wallet)
{
wallet->load(wallet_file, pwd->password());
@@ -851,7 +863,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
return {std::move(wallet), std::move(*pwd)};
}
-std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
+std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
auto pwd = get_password(vm, opts, password_prompter, true);
@@ -859,19 +871,18 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const
{
return {nullptr, password_container{}};
}
- return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)};
+ return {make_basic(vm, unattended, opts, password_prompter), std::move(*pwd)};
}
-std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return make_basic(vm, rpc, opts, password_prompter);
+ return make_basic(vm, unattended, opts, password_prompter);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
+bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
{
- m_rpc = rpc;
m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
@@ -981,6 +992,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
return true;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::reconnect_device()
+{
+ bool r = true;
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
+ r = hwdev.init();
+ if (!r){
+ LOG_PRINT_L2("Could not init device");
+ return false;
+ }
+
+ r = hwdev.connect();
+ if (!r){
+ LOG_PRINT_L2("Could not connect to the device");
+ return false;
+ }
+
+ m_account.set_device(hwdev);
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -1210,7 +1242,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
// if keys are encrypted, ask for password
- if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
{
static critical_section password_lock;
CRITICAL_REGION_LOCAL(password_lock);
@@ -2067,9 +2099,11 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
{
drop_from_short_history(short_chain_history, 3);
+ THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
+
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin();
- for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n)
+ for (size_t n = 0; n < std::min((size_t)3, prev_parsed_blocks.size()); ++n)
{
short_chain_history.push_front(i->hash);
++i;
@@ -2389,6 +2423,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
+ m_blockchain.trim(checkpoint_height);
short_chain_history.clear();
get_short_chain_history(short_chain_history);
}
@@ -2600,10 +2635,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
break;
}
- // switch to the new blocks from the daemon
- blocks_start_height = next_blocks_start_height;
- blocks = std::move(next_blocks);
- parsed_blocks = std::move(next_parsed_blocks);
first = false;
// handle error from async fetching thread
@@ -2611,6 +2642,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
{
throw std::runtime_error("proxy exception in refresh thread");
}
+
+ // switch to the new blocks from the daemon
+ blocks_start_height = next_blocks_start_height;
+ blocks = std::move(next_blocks);
+ parsed_blocks = std::move(next_parsed_blocks);
}
catch (const tools::error::password_needed&)
{
@@ -2843,7 +2879,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
crypto::chacha_key key;
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
account.encrypt_viewkey(key);
account.decrypt_keys(key);
@@ -2872,7 +2908,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
rapidjson::Value value2(rapidjson::kNumberType);
- value2.SetInt(m_key_on_device?1:0);
+ value2.SetInt(m_key_device_type);
json.AddMember("key_on_device", value2, json.GetAllocator());
value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ??
@@ -2922,7 +2958,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
- value2.SetInt(m_ask_password ? 1 :0);
+ value2.SetInt(m_ask_password);
json.AddMember("ask_password", value2, json.GetAllocator());
value2.SetUint(m_min_output_count);
@@ -2973,6 +3009,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(1);
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
+ value.SetString(m_device_name.c_str(), m_device_name.size());
+ json.AddMember("device_name", value, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -3003,7 +3042,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
// re-encrypt, but keep viewkey unencrypted
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
m_account.encrypt_keys(key);
m_account.decrypt_viewkey(key);
@@ -3019,7 +3058,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
//----------------------------------------------------------------------------------------------------
void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
{
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(original_password);
setup_keys(new_password);
rewrite(filename, new_password);
@@ -3067,7 +3106,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_refresh_type = RefreshType::RefreshDefault;
m_confirm_missing_payment_id = true;
m_confirm_non_default_ring_size = true;
- m_ask_password = true;
+ m_ask_password = AskPasswordToDecrypt;
m_min_output_count = 0;
m_min_output_value = 0;
m_merge_destinations = false;
@@ -3081,7 +3120,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = true;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
- m_key_on_device = false;
+ m_device_name = "";
+ m_key_device_type = hw::device::device_type::SOFTWARE;
encrypted_secret_keys = false;
}
else if(json.IsObject())
@@ -3101,8 +3141,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
if (json.HasMember("key_on_device"))
{
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, false);
- m_key_on_device = field_key_on_device;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE);
+ m_key_device_type = static_cast<hw::device::device_type>(field_key_on_device);
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string());
@@ -3176,7 +3216,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt);
m_ask_password = field_ask_password;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
cryptonote::set_default_decimal_point(field_default_decimal_point);
@@ -3212,8 +3252,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
+
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
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)
+ {
+ m_device_name = field_device_name;
+ }
}
else
{
@@ -3222,13 +3269,17 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
}
r = epee::serialization::load_t_from_binary(m_account, account_data);
- if (r && m_key_on_device) {
+ THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
+ if (m_key_device_type == hw::device::device_type::LEDGER) {
LOG_PRINT_L0("Account on device. Initing device...");
- hw::device &hwdev = hw::get_device("Ledger");
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
hwdev.init();
hwdev.connect();
m_account.set_device(hwdev);
LOG_PRINT_L0("Device inited...");
+ } else if (key_on_device()) {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
}
if (r)
@@ -3240,7 +3291,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
else
{
// rewrite with encrypted keys, ignore errors
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
encrypt_keys(key);
bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
if (!saved_ret)
@@ -3248,7 +3299,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
// just moan a bit, but not fatal
MERROR("Error saving keys file with encrypted keys, not fatal");
}
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(key);
m_keys_file_locker.reset();
}
@@ -3371,6 +3422,84 @@ void wallet2::decrypt_keys(const epee::wipeable_string &password)
decrypt_keys(key);
}
+void wallet2::setup_new_blockchain()
+{
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
+ add_subaddress_account(tr("Primary account"));
+}
+
+void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file)
+{
+ if (!wallet_.empty())
+ {
+ bool r = store_keys(m_keys_file, password, watch_only);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+ if (create_address_file)
+ {
+ r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
+ if(!r) MERROR("String with address text not saved");
+ }
+ }
+}
+
+
+/*!
+ * \brief determine the key storage for the specified wallet file
+ * \param device_type (OUT) wallet backend as enumerated in hw::device::device_type
+ * \param keys_file_name Keys file to verify password for
+ * \param password Password to verify
+ * \return true if password correct, else false
+ *
+ * for verification only - determines key storage hardware
+ *
+ */
+bool wallet2::query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds)
+{
+ rapidjson::Document json;
+ wallet2::keys_file_data keys_file_data;
+ std::string buf;
+ bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
+
+ // Decrypt the contents
+ r = ::serialization::parse_binary(buf, keys_file_data);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
+ std::string account_data;
+ account_data.resize(keys_file_data.account_data.size());
+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+ crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+
+ // The contents should be JSON if the wallet follows the new format.
+ if (json.Parse(account_data.c_str()).HasParseError())
+ {
+ // old format before JSON wallet key file format
+ }
+ else
+ {
+ account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
+ json["key_data"].GetStringLength());
+
+ if (json.HasMember("key_on_device"))
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE);
+ device_type = static_cast<hw::device::device_type>(field_key_on_device);
+ }
+ }
+
+ cryptonote::account_base account_data_check;
+
+ r = epee::serialization::load_t_from_binary(account_data_check, account_data);
+ if (!r) return false;
+ return true;
+}
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -3445,26 +3574,11 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = true;
m_multisig_threshold = threshold;
m_multisig_signers = multisig_signers;
- m_key_on_device = false;
+ m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3500,7 +3614,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
- m_key_on_device = false;
+ m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
// calculate a starting refresh height
@@ -3508,23 +3622,9 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_refresh_from_block_height = estimate_blockchain_height();
}
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3602,26 +3702,12 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
- m_key_on_device = false;
+ m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, true);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3656,26 +3742,12 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
- m_key_on_device = false;
+ m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+ create_keys_file(wallet_, false, password, create_address_file);
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3687,7 +3759,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
* \param password Password of wallet file
* \param device_name device string address
*/
-void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name)
+void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
@@ -3697,33 +3769,28 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
- m_key_on_device = true;
- m_account.create_from_device(device_name);
+
+ auto &hwdev = hw::get_device(device_name);
+ hwdev.set_name(device_name);
+
+ m_account.create_from_device(hwdev);
+ m_key_device_type = m_account.get_device().get_type();
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
setup_keys(password);
+ m_device_name = device_name;
- if (!wallet_.empty()) {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
{
// the default lookahead setting (50:200) is clearly too much for hardware wallet
m_subaddress_lookahead_major = 5;
m_subaddress_lookahead_minor = 20;
}
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty()) {
store();
}
@@ -3746,7 +3813,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
// decrypt keys
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
crypto::chacha_key chacha_key;
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
@@ -3804,7 +3871,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
m_watch_only = false;
m_multisig = true;
m_multisig_threshold = threshold;
- m_key_on_device = false;
+ m_key_device_type = hw::device::device_type::SOFTWARE;
if (threshold == spend_keys.size() + 1)
{
@@ -3819,23 +3886,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
// re-encrypt keys
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
- if (!m_wallet_file.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!m_wallet_file.empty())
store();
@@ -3903,7 +3956,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
// keys are decrypted
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
crypto::chacha_key chacha_key;
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
@@ -3937,17 +3990,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
// keys are encrypted again
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
- if (!m_wallet_file.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
m_subaddresses.clear();
m_subaddress_labels.clear();
@@ -4273,7 +4316,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
lock_keys_file();
- wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password);
+ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
@@ -4758,12 +4801,7 @@ void wallet2::rescan_blockchain(bool refresh)
{
clear();
- cryptonote::block genesis;
- generate_genesis(genesis);
- crypto::hash genesis_hash = get_block_hash(genesis);
- m_blockchain.push_back(genesis_hash);
- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (refresh)
this->refresh(false);
@@ -4800,7 +4838,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig
uint64_t current_time = static_cast<uint64_t>(time(NULL));
// XXX: this needs to be fast, so we'd need to get the starting heights
// from the daemon to be correct once voting kicks in
- uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827;
+ uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827;
uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
if(current_time + leeway >= unlock_time)
return true;
@@ -4993,69 +5031,6 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
}
//----------------------------------------------------------------------------------------------------
-void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx)
-{
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
-}
-//----------------------------------------------------------------------------------------------------
-void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra)
-{
- cryptonote::transaction tx;
- pending_tx ptx;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx);
-}
-
-namespace {
-// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
-//
-// split amount for each dst in dsts into num_splits parts
-// and make num_splits new vector<crypt...> instances to hold these new amounts
-std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
- std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
-{
- std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
-
- if (num_splits <= 1)
- {
- retVal.push_back(dsts);
- return retVal;
- }
-
- // for each split required
- for (size_t i=0; i < num_splits; i++)
- {
- std::vector<cryptonote::tx_destination_entry> new_dsts;
-
- // for each destination
- for (size_t j=0; j < dsts.size(); j++)
- {
- cryptonote::tx_destination_entry de;
- uint64_t amount;
-
- amount = dsts[j].amount;
- amount = amount / num_splits;
-
- // if last split, add remainder
- if (i + 1 == num_splits)
- {
- amount += dsts[j].amount % num_splits;
- }
-
- de.addr = dsts[j].addr;
- de.amount = amount;
-
- new_dsts.push_back(de);
- }
-
- retVal.push_back(new_dsts);
- }
-
- return retVal;
-}
-} // anonymous namespace
-//----------------------------------------------------------------------------------------------------
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
@@ -6055,116 +6030,6 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
return priority;
}
//----------------------------------------------------------------------------------------------------
-// separated the call(s) to wallet2::transfer into their own function
-//
-// this function will make multiple calls to wallet2::transfer if multiple
-// transactions will be required
-std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
-{
- const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
-
- const uint64_t base_fee = get_base_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
- const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
- const uint64_t fee_quantization_mask = get_fee_quantization_mask();
-
- // failsafe split attempt counter
- size_t attempt_count = 0;
-
- for(attempt_count = 1; ;attempt_count++)
- {
- size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
-
- auto split_values = split_amounts(dsts, num_tx);
-
- // Throw if split_amounts comes back with a vector of size different than it should
- if (split_values.size() != num_tx)
- {
- throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
- }
-
- std::vector<pending_tx> ptx_vector;
- try
- {
- // for each new destination vector (i.e. for each new tx)
- for (auto & dst_vector : split_values)
- {
- cryptonote::transaction tx;
- pending_tx ptx;
-
- // loop until fee is met without increasing tx size to next KB boundary.
- uint64_t needed_fee = estimate_fee(use_per_byte_fee, false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size()+1, extra.size(), false, base_fee, fee_multiplier, fee_quantization_mask);
- do
- {
- transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
- auto txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(use_per_byte_fee, ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
- } while (ptx.fee < needed_fee);
-
- ptx_vector.push_back(ptx);
-
- // mark transfers to be used as "spent"
- for(size_t idx: ptx.selected_transfers)
- {
- set_spent(idx, 0);
- }
- }
-
- // if we made it this far, we've selected our transactions. committing them will mark them spent,
- // so this is a failsafe in case they don't go through
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
-
- }
-
- // if we made it this far, we're OK to actually send the transactions
- return ptx_vector;
-
- }
- // only catch this here, other exceptions need to pass through to the calling function
- catch (const tools::error::tx_too_big& e)
- {
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- if (attempt_count >= MAX_SPLIT_ATTEMPTS)
- {
- throw;
- }
- }
- catch (...)
- {
- // in case of some other exception, make sure any tx in queue are marked unspent again
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- throw;
- }
- }
-}
-
bool wallet2::set_ring_database(const std::string &filename)
{
m_ring_database = filename;
@@ -6337,7 +6202,7 @@ bool wallet2::find_and_save_rings(bool force)
return true;
}
-bool wallet2::blackball_output(const crypto::public_key &output)
+bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@@ -6345,7 +6210,7 @@ bool wallet2::blackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
-bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
+bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
{
if (!m_ringdb)
return false;
@@ -6354,14 +6219,13 @@ bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &out
bool ret = true;
if (!add)
ret &= m_ringdb->clear_blackballs();
- for (const auto &output: outputs)
- ret &= m_ringdb->blackball(output);
+ ret &= m_ringdb->blackball(outputs);
return ret;
}
catch (const std::exception &e) { return false; }
}
-bool wallet2::unblackball_output(const crypto::public_key &output)
+bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@@ -6369,7 +6233,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
-bool wallet2::is_output_blackballed(const crypto::public_key &output) const
+bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
{
if (!m_ringdb)
return false;
@@ -6414,8 +6278,8 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
- if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
- return false;
+// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
+// return false;
outs.back().push_back(item);
return true;
}
@@ -6912,6 +6776,8 @@ 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
+ continue;
seen_indices.emplace(i);
LOG_PRINT_L2("picking " << i << " as " << type);
@@ -6934,7 +6800,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, daemon_resp.status);
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
@@ -7412,7 +7278,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(selected_transfers.size() != sources.size(), error::wallet_internal_error, "mismatched selected_transfers and sources sixes");
for(size_t idx: selected_transfers)
{
- cryptonote::tx_source_entry& src = sources[src_idx];
+ cryptonote::tx_source_entry& src = sources_copy[src_idx];
src.multisig_kLRki = get_multisig_composite_kLRki(idx, multisig_signers[signer_index], used_L, new_used_L);
++src_idx;
}
@@ -9711,6 +9577,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
if (account_minreserve)
{
+ THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
// minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
{ return m_transfers[a].amount() > m_transfers[b].amount(); });
@@ -11019,7 +10886,12 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
refresh(false);
}
- catch (...) {}
+ catch (...)
+ {
+ m_multisig_rescan_info = NULL;
+ m_multisig_rescan_k = NULL;
+ throw;
+ }
m_multisig_rescan_info = NULL;
m_multisig_rescan_k = NULL;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index f9b516bff..9eb9b04f2 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -162,28 +162,36 @@ namespace tools
RefreshDefault = RefreshOptimizeCoinbase,
};
+ enum AskPasswordType {
+ AskPasswordNever = 0,
+ AskPasswordOnAction = 1,
+ AskPasswordToDecrypt = 2,
+ };
+
static const char* tr(const char* str);
static bool has_testnet_option(const boost::program_options::variables_map& vm);
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
+ static std::string device_name_option(const boost::program_options::variables_map& vm);
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 rpc, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ 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);
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container>
- make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ make_from_file(const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
- static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Just parses variables.
- static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
+ static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1);
- wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1);
+ wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false);
~wallet2();
struct multisig_info
@@ -544,8 +552,9 @@ namespace tools
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \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);
+ void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file);
/*!
* \brief Creates a multisig wallet
@@ -631,7 +640,7 @@ namespace tools
bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;}
bool deinit();
- bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
+ bool init(std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false);
void stop() { m_run.store(false, std::memory_order_relaxed); }
@@ -701,7 +710,9 @@ namespace tools
bool has_multisig_partial_key_images() const;
bool has_unknown_key_images() const;
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
- bool key_on_device() const { return m_key_on_device; }
+ bool key_on_device() const { return get_device_type() != hw::device::device_type::SOFTWARE; }
+ hw::device::device_type get_device_type() const { return m_key_device_type; }
+ bool reconnect_device();
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
@@ -713,12 +724,6 @@ namespace tools
uint64_t balance_all() const;
uint64_t unlocked_balance_all() const;
template<typename T>
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy);
- template<typename T>
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx);
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra);
- void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx);
- template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
@@ -746,7 +751,6 @@ namespace tools
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<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, 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_2(std::vector<cryptonote::tx_destination_entry> dsts, 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); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, 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_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
@@ -912,8 +916,8 @@ namespace tools
void auto_refresh(bool r) { m_auto_refresh = r; }
bool confirm_missing_payment_id() const { return m_confirm_missing_payment_id; }
void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
- bool ask_password() const { return m_ask_password; }
- void ask_password(bool always) { m_ask_password = always; }
+ AskPasswordType ask_password() const { return m_ask_password; }
+ void ask_password(AskPasswordType ask) { m_ask_password = ask; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
uint32_t get_min_output_count() const { return m_min_output_count; }
void set_min_output_value(uint64_t value) { m_min_output_value = value; }
@@ -938,6 +942,8 @@ namespace tools
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
+ const std::string & device_name() const { return m_device_name; }
+ void device_name(const std::string & device_name) { m_device_name = device_name; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
@@ -1089,7 +1095,7 @@ namespace tools
uint64_t adjust_mixin(uint64_t mixin) const;
uint32_t adjust_priority(uint32_t priority);
- bool is_rpc() const { return m_rpc; }
+ bool is_unattended() const { return m_unattended; }
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
@@ -1159,10 +1165,10 @@ namespace tools
bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
bool find_and_save_rings(bool force = true);
- bool blackball_output(const crypto::public_key &output);
- bool set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add = false);
- bool unblackball_output(const crypto::public_key &output);
- bool is_output_blackballed(const crypto::public_key &output) const;
+ bool blackball_output(const std::pair<uint64_t, uint64_t> &output);
+ bool set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add = false);
+ bool unblackball_output(const std::pair<uint64_t, uint64_t> &output);
+ bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const;
bool lock_keys_file();
bool unlock_keys_file();
@@ -1242,6 +1248,9 @@ namespace tools
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
+ void setup_new_blockchain();
+ void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
+
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
std::string m_daemon_address;
@@ -1277,7 +1286,7 @@ namespace tools
bool m_trusted_daemon;
i_wallet2_callback* m_callback;
- bool m_key_on_device;
+ hw::device::device_type m_key_device_type;
cryptonote::network_type m_nettype;
uint64_t m_kdf_rounds;
std::string seed_language; /*!< Language of the mnemonics (seed). */
@@ -1300,7 +1309,7 @@ namespace tools
bool m_explicit_refresh_from_block_height;
bool m_confirm_missing_payment_id;
bool m_confirm_non_default_ring_size;
- bool m_ask_password;
+ AskPasswordType m_ask_password;
uint32_t m_min_output_count;
uint64_t m_min_output_value;
bool m_merge_destinations;
@@ -1316,6 +1325,7 @@ namespace tools
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
+ std::string m_device_name;
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
@@ -1342,7 +1352,7 @@ namespace tools
crypto::chacha_key m_cache_key;
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
- bool m_rpc;
+ bool m_unattended;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 25)
@@ -1817,198 +1827,4 @@ namespace tools
//----------------------------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------------------------
- template<typename T>
- void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy)
- {
- pending_tx ptx;
- cryptonote::transaction tx;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx);
- }
-
- template<typename T>
- void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
- {
- using namespace cryptonote;
- // throw if attempting a transaction with no destinations
- THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
-
- THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
-
- uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
- uint64_t needed_money = fee;
-
- // calculate total amount being sent to all destinations
- // throw if total amount overflows uint64_t
- for(auto& dt: dsts)
- {
- THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
- needed_money += dt.amount;
- THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
- }
-
- // randomly select inputs for transaction
- // throw if requested send amount is greater than (unlocked) amount available to send
- std::vector<size_t> selected_transfers;
- uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers);
- THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
-
- uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
- for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
- THROW_WALLET_EXCEPTION_IF(subaddr_account != *i, error::wallet_internal_error, "the tx uses funds from multiple accounts");
-
- typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
- typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
-
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
- if(fake_outputs_count)
- {
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
- req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
- for(size_t idx: selected_transfers)
- {
- const transfer_container::const_iterator it = m_transfers.begin() + idx;
- THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
- "m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
- " is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
- req.amounts.push_back(it->amount());
- }
-
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_bin("/getrandom_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
- THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
- "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
- std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));
-
- std::unordered_map<uint64_t, uint64_t> scanty_outs;
- for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs: daemon_resp.outs)
- {
- if (amount_outs.outs.size() < fake_outputs_count)
- {
- scanty_outs[amount_outs.amount] = amount_outs.outs.size();
- }
- }
- THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
- }
-
- //prepare inputs
- size_t i = 0;
- std::vector<cryptonote::tx_source_entry> sources;
- for(size_t idx: selected_transfers)
- {
- sources.resize(sources.size()+1);
- cryptonote::tx_source_entry& src = sources.back();
- const transfer_details& td = m_transfers[idx];
- src.amount = td.amount();
- src.rct = false;
- //paste mixin transaction
- if(daemon_resp.outs.size())
- {
- daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;});
- for(out_entry& daemon_oe: daemon_resp.outs[i].outs)
- {
- if(td.m_global_output_index == daemon_oe.global_amount_index)
- continue;
- tx_output_entry oe;
- oe.first = daemon_oe.global_amount_index;
- oe.second.dest = rct::pk2rct(daemon_oe.out_key);
- oe.second.mask = rct::identity();
- src.outputs.push_back(oe);
- if(src.outputs.size() >= fake_outputs_count)
- break;
- }
- }
-
- //paste real transaction to the random index
- auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
- {
- return a.first >= td.m_global_output_index;
- });
- //size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
- tx_output_entry real_oe;
- real_oe.first = td.m_global_output_index;
- real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
- real_oe.second.mask = rct::identity();
- auto interted_it = src.outputs.insert(it_to_insert, real_oe);
- src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
- src.real_output = interted_it - src.outputs.begin();
- src.real_output_in_tx_index = td.m_internal_output_index;
- src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()});
- detail::print_source_entry(src);
- ++i;
- }
-
- cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
- if (needed_money < found_money)
- {
- change_dts.addr = get_subaddress({subaddr_account, 0});
- change_dts.amount = found_money - needed_money;
- }
-
- std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
- uint64_t dust = 0;
- destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
- for(auto& d: dust_dsts) {
- THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
- std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
- }
- for(auto& d: dust_dsts) {
- if (!dust_policy.add_to_fee)
- splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
- dust += d.amount;
- }
-
- crypto::secret_key tx_key;
- std::vector<crypto::secret_key> additional_tx_keys;
- rct::multisig_out msout;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL);
- THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
- THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
-
- std::string key_images;
- bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
- {
- CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
- key_images += boost::to_string(in.k_image) + " ";
- return true;
- });
- THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
-
- bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
- || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
-
- if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
-
- ptx.key_images = key_images;
- ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
- ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
- ptx.dust_added_to_fee = dust_policy.add_to_fee;
- ptx.tx = tx;
- ptx.change_dts = change_dts;
- ptx.selected_transfers = selected_transfers;
- ptx.tx_key = tx_key;
- ptx.additional_tx_keys = additional_tx_keys;
- ptx.dests = dsts;
- ptx.construction_data.sources = sources;
- ptx.construction_data.change_dts = change_dts;
- ptx.construction_data.splitted_dsts = splitted_dsts;
- ptx.construction_data.selected_transfers = selected_transfers;
- ptx.construction_data.extra = tx.extra;
- ptx.construction_data.unlock_time = unlock_time;
- ptx.construction_data.use_rct = false;
- ptx.construction_data.use_bulletproofs = false;
- ptx.construction_data.dests = dsts;
- // record which subaddress indices are being used as inputs
- ptx.construction_data.subaddr_account = subaddr_account;
- ptx.construction_data.subaddr_indices.clear();
- for (size_t idx: selected_transfers)
- ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
- }
-
-
}
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index a30e807b1..bc518d04a 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -73,7 +73,7 @@ namespace tools
// get_tx_pool_error
// out_of_hashchain_bounds_error
// transfer_error *
- // get_random_outs_general_error
+ // get_outs_general_error
// not_enough_unlocked_money
// not_enough_money
// tx_not_possible
@@ -128,7 +128,7 @@ namespace tools
get_blocks_error_message_index,
get_hashes_error_message_index,
get_out_indices_error_message_index,
- get_random_outs_error_message_index
+ get_outs_error_message_index
};
template<typename Base, int msg_index>
@@ -427,7 +427,7 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
- typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error;
+ typedef failed_rpc_request<transfer_error, get_outs_error_message_index> get_outs_error;
//----------------------------------------------------------------------------------------------------
struct not_enough_unlocked_money : public transfer_error
{
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 0e0b2e4eb..86b46b173 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -51,6 +51,7 @@ using namespace epee;
#include "mnemonics/electrum-words.h"
#include "rpc/rpc_args.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "daemonizer/daemonizer.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
@@ -160,8 +161,13 @@ namespace tools
std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port);
const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login);
m_restricted = command_line::get_arg(*m_vm, arg_restricted);
- if (command_line::has_arg(*m_vm, arg_wallet_dir))
+ if (!command_line::is_arg_defaulted(*m_vm, arg_wallet_dir))
{
+ if (!command_line::is_arg_defaulted(*m_vm, wallet_args::arg_wallet_file()))
+ {
+ MERROR(arg_wallet_dir.name << " and " << wallet_args::arg_wallet_file().name << " are incompatible, use only one of them");
+ return false;
+ }
m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir);
#ifdef _WIN32
#define MKDIR(path, mode) mkdir(path)
@@ -754,10 +760,10 @@ namespace tools
{
if (get_tx_key)
{
- std::string s = epee::string_tools::pod_to_hex(ptx.tx_key);
+ epee::wipeable_string s = epee::to_hex::wipeable_string(ptx.tx_key);
for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
- s += epee::string_tools::pod_to_hex(additional_tx_key);
- fill(tx_key, s);
+ s += epee::to_hex::wipeable_string(additional_tx_key);
+ fill(tx_key, std::string(s.data(), s.size()));
}
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
fill(amount, total_amount(ptx));
@@ -1564,11 +1570,13 @@ namespace tools
}
else if(req.key_type.compare("view_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
+ epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key);
+ res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("spend_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key);
+ epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
+ res.key = std::string(key.data(), key.size());
}
else
{
@@ -1794,11 +1802,11 @@ namespace tools
return false;
}
- std::ostringstream oss;
- oss << epee::string_tools::pod_to_hex(tx_key);
+ epee::wipeable_string s;
+ s += epee::to_hex::wipeable_string(tx_key);
for (size_t i = 0; i < additional_tx_keys.size(); ++i)
- oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
- res.tx_key = oss.str();
+ s += epee::to_hex::wipeable_string(additional_tx_keys[i]);
+ res.tx_key = std::string(s.data(), s.size());
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1814,26 +1822,33 @@ namespace tools
return false;
}
- std::string tx_key_str = req.tx_key;
+ epee::wipeable_string tx_key_str = req.tx_key;
+ if (tx_key_str.size() < 64 || tx_key_str.size() % 64)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ const char *data = tx_key_str.data();
crypto::secret_key tx_key;
- if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key))))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
er.message = "Tx key has invalid format";
return false;
}
- tx_key_str = tx_key_str.substr(64);
+ size_t offset = 64;
std::vector<crypto::secret_key> additional_tx_keys;
- while (!tx_key_str.empty())
+ while (offset < tx_key_str.size())
{
additional_tx_keys.resize(additional_tx_keys.size() + 1);
- if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back()))))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
er.message = "Tx key has invalid format";
return false;
}
- tx_key_str = tx_key_str.substr(64);
+ offset += 64;
}
cryptonote::address_parse_info info;
@@ -3260,12 +3275,172 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
}
+class t_daemon
+{
+private:
+ const boost::program_options::variables_map& vm;
+
+public:
+ t_daemon(boost::program_options::variables_map const & _vm)
+ : vm(_vm)
+ {
+ }
+
+ bool run()
+ {
+ std::unique_ptr<tools::wallet2> wal;
+ try
+ {
+ const bool testnet = tools::wallet2::has_testnet_option(vm);
+ const bool stagenet = tools::wallet2::has_stagenet_option(vm);
+ if (testnet && stagenet)
+ {
+ MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
+ return false;
+ }
+
+ const auto arg_wallet_file = wallet_args::arg_wallet_file();
+ const auto arg_from_json = wallet_args::arg_generate_from_json();
+
+ const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
+ const auto from_json = command_line::get_arg(vm, arg_from_json);
+ const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir);
+ const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
+ const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
+
+ if(!wallet_file.empty() && !from_json.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
+ return false;
+ }
+
+ if (!wallet_dir.empty())
+ {
+ wal = NULL;
+ goto just_dir;
+ }
+
+ if (wallet_file.empty() && from_json.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
+ return false;
+ }
+
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
+ if(!wallet_file.empty())
+ {
+ wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first;
+ }
+ else
+ {
+ try
+ {
+ wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Error creating wallet: " << e.what());
+ return false;
+ }
+ }
+ if (!wal)
+ {
+ return false;
+ }
+
+ bool quit = false;
+ tools::signal_handler::install([&wal, &quit](int) {
+ assert(wal);
+ quit = true;
+ wal->stop();
+ });
+
+ wal->refresh(wal->is_trusted_daemon());
+ // if we ^C during potentially length load/refresh, there's no server loop yet
+ if (quit)
+ {
+ MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
+ wal->store();
+ MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
+ return false;
+ }
+ MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
+ return false;
+ }
+ just_dir:
+ tools::wallet_rpc_server wrpc;
+ if (wal) wrpc.set_wallet(wal.release());
+ bool r = wrpc.init(&vm);
+ CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
+ tools::signal_handler::install([&wrpc](int) {
+ wrpc.send_stop_signal();
+ });
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
+ try
+ {
+ wrpc.run();
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
+ return false;
+ }
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
+ try
+ {
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
+ wrpc.stop();
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
+ return false;
+ }
+ return true;
+ }
+};
+
+class t_executor final
+{
+public:
+ static std::string const NAME;
+
+ std::string const & name()
+ {
+ return NAME;
+ }
+
+ t_daemon create_daemon(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm);
+ }
+
+ bool run_non_interactive(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm).run();
+ }
+
+ bool run_interactive(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm).run();
+ }
+};
+
+std::string const t_executor::NAME = "Wallet RPC Daemon";
+
int main(int argc, char** argv) {
namespace po = boost::program_options;
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json();
+ po::options_description hidden_options("Hidden");
+
po::options_description desc_params(wallet_args::tr("Wallet options"));
tools::wallet2::init_options(desc_params);
command_line::add_arg(desc_params, arg_rpc_bind_port);
@@ -3277,6 +3452,8 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
+ daemonizer::init_options(hidden_options, desc_params);
+
boost::optional<po::variables_map> vm;
bool should_terminate = false;
std::tie(vm, should_terminate) = wallet_args::main(
@@ -3298,115 +3475,5 @@ int main(int argc, char** argv) {
return 0;
}
- std::unique_ptr<tools::wallet2> wal;
- try
- {
- const bool testnet = tools::wallet2::has_testnet_option(*vm);
- const bool stagenet = tools::wallet2::has_stagenet_option(*vm);
- if (testnet && stagenet)
- {
- MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
- return 1;
- }
-
- const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file);
- const auto from_json = command_line::get_arg(*vm, arg_from_json);
- const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
- const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password);
- const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
-
- if(!wallet_file.empty() && !from_json.empty())
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
- return 1;
- }
-
- if (!wallet_dir.empty())
- {
- wal = NULL;
- goto just_dir;
- }
-
- if (wallet_file.empty() && from_json.empty())
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
- return 1;
- }
-
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
- if(!wallet_file.empty())
- {
- wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first;
- }
- else
- {
- try
- {
- wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt);
- }
- catch (const std::exception &e)
- {
- MERROR("Error creating wallet: " << e.what());
- return 1;
- }
- }
- if (!wal)
- {
- return 1;
- }
-
- bool quit = false;
- tools::signal_handler::install([&wal, &quit](int) {
- assert(wal);
- quit = true;
- wal->stop();
- });
-
- wal->refresh(wal->is_trusted_daemon());
- // if we ^C during potentially length load/refresh, there's no server loop yet
- if (quit)
- {
- MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
- wal->store();
- MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
- return 1;
- }
- MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
- }
- catch (const std::exception& e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
- return 1;
- }
-just_dir:
- tools::wallet_rpc_server wrpc;
- if (wal) wrpc.set_wallet(wal.release());
- bool r = wrpc.init(&(vm.get()));
- CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
- tools::signal_handler::install([&wrpc](int) {
- wrpc.send_stop_signal();
- });
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
- try
- {
- wrpc.run();
- }
- catch (const std::exception &e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
- return 1;
- }
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
- try
- {
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
- wrpc.stop();
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
- }
- catch (const std::exception& e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
- return 1;
- }
- return 0;
+ return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
}
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index ce10e2917..4501cf575 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -766,15 +766,9 @@ namespace wallet_rpc
struct response
{
std::string tx_hash;
- std::string tx_key;
- uint64_t fee;
- std::string tx_blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
- KV_SERIALIZE(tx_key)
- KV_SERIALIZE(fee)
- KV_SERIALIZE(tx_blob)
END_KV_SERIALIZE_MAP()
};
};