aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt3
-rw-r--r--src/wallet/api/address_book.cpp2
-rw-r--r--src/wallet/api/wallet.cpp20
-rw-r--r--src/wallet/api/wallet.h3
-rw-r--r--src/wallet/api/wallet_manager.cpp6
-rw-r--r--src/wallet/api/wallet_manager.h2
-rw-r--r--src/wallet/wallet2.cpp253
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--src/wallet/wallet2_api.h2
-rw-r--r--src/wallet/wallet_args.cpp32
-rw-r--r--src/wallet/wallet_rpc_server.cpp72
-rw-r--r--src/wallet/wallet_rpc_server.h3
12 files changed, 271 insertions, 129 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 639080051..fe87d0de1 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -84,7 +84,6 @@ target_link_libraries(wallet
${Boost_REGEX_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
-add_dependencies(wallet version)
if (NOT BUILD_GUI_DEPS)
set(wallet_rpc_sources
@@ -110,13 +109,13 @@ if (NOT BUILD_GUI_DEPS)
cryptonote_core
cncrypto
common
+ version
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
- add_dependencies(wallet_rpc_server version)
set_property(TARGET wallet_rpc_server
PROPERTY
OUTPUT_NAME "monero-wallet-rpc")
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index 28f835ebd..a955cb166 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -42,7 +42,7 @@ namespace Monero {
AddressBook::~AddressBook() {}
AddressBookImpl::AddressBookImpl(WalletImpl *wallet)
- : m_wallet(wallet) {}
+ : m_wallet(wallet), m_errorCode(Status_Ok) {}
bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &payment_id_str, const std::string &description)
{
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 7afc1f449..9cd72b543 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -303,7 +303,7 @@ WalletImpl::~WalletImpl()
// Pause refresh thread - prevents refresh from starting again
pauseRefresh();
// Close wallet - stores cache and stops ongoing refresh operation
- close();
+ close(false); // do not store wallet as part of the closing activities
// Stop refresh thread
stopRefresh();
delete m_wallet2Callback;
@@ -566,19 +566,21 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed)
return m_status == Status_Ok;
}
-bool WalletImpl::close()
+bool WalletImpl::close(bool store)
{
bool result = false;
LOG_PRINT_L1("closing wallet...");
try {
- // Do not store wallet with invalid status
- // Status Critical refers to errors on opening or creating wallets.
- if (status() != Status_Critical)
- m_wallet->store();
- else
- LOG_ERROR("Status_Critical - not storing wallet");
- LOG_PRINT_L1("wallet::store done");
+ if (store) {
+ // Do not store wallet with invalid status
+ // Status Critical refers to errors on opening or creating wallets.
+ if (status() != Status_Critical)
+ m_wallet->store();
+ else
+ LOG_ERROR("Status_Critical - not storing wallet");
+ LOG_PRINT_L1("wallet::store done");
+ }
LOG_PRINT_L1("Calling wallet::stop...");
m_wallet->stop();
LOG_PRINT_L1("wallet::stop done");
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 8190c7873..1e3d1e600 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -63,7 +63,7 @@ public:
const std::string &address_string,
const std::string &viewkey_string,
const std::string &spendkey_string = "");
- bool close();
+ bool close(bool store = true);
std::string seed() const;
std::string getSeedLanguage() const;
void setSeedLanguage(const std::string &arg);
@@ -153,7 +153,6 @@ private:
std::string m_password;
TransactionHistoryImpl * m_history;
bool m_trustedDaemon;
- WalletListener * m_walletListener;
Wallet2CallbackImpl * m_wallet2Callback;
AddressBookImpl * m_addressBook;
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index a23533530..897137d35 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -102,10 +102,12 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
return wallet;
}
-bool WalletManagerImpl::closeWallet(Wallet *wallet)
+bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
{
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
- bool result = wallet_->close();
+ if (!wallet_)
+ return false;
+ bool result = wallet_->close(store);
if (!result) {
m_errorString = wallet_->errorString();
} else {
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index aa6ea439e..8455f0f16 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -48,7 +48,7 @@ public:
const std::string &addressString,
const std::string &viewKeyString,
const std::string &spendKeyString = "");
- virtual bool closeWallet(Wallet *wallet);
+ virtual bool closeWallet(Wallet *wallet, bool store = true);
bool walletExists(const std::string &path);
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const;
std::vector<std::string> findWallets(const std::string &path);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 323a3a7fe..986f33073 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -45,6 +45,7 @@ using namespace epee;
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "common/boost_serialization_helper.h"
#include "common/command_line.h"
+#include "common/threadpool.h"
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
@@ -89,14 +90,6 @@ using namespace cryptonote;
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
-#define KILL_IOSERVICE() \
- do { \
- work.reset(); \
- while (!ioservice.stopped()) ioservice.poll(); \
- threadpool.join_all(); \
- ioservice.stop(); \
- } while(0)
-
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
namespace
@@ -296,6 +289,13 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
return false;
}
restore_deterministic_wallet = true;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string());
+ if (field_seed_passphrase_found)
+ {
+ if (!field_seed_passphrase.empty())
+ recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase);
+ }
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string());
@@ -383,7 +383,11 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
cryptonote::account_public_address address2;
bool has_payment_id;
crypto::hash8 new_payment_id;
- get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address);
+ if (!get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address))
+ {
+ tools::fail_msg_writer() << tools::wallet2::tr("failed to parse address: ") << field_address;
+ return false;
+ }
address.m_spend_public_key = address2.m_spend_public_key;
}
wallet->generate(field_filename, field_password, address, viewkey);
@@ -527,7 +531,7 @@ bool wallet2::is_deterministic() const
return keys_deterministic;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_seed(std::string& electrum_words) const
+bool wallet2::get_seed(std::string& electrum_words, const std::string &passphrase) const
{
bool keys_deterministic = is_deterministic();
if (!keys_deterministic)
@@ -541,7 +545,10 @@ bool wallet2::get_seed(std::string& electrum_words) const
return false;
}
- crypto::ElectrumWords::bytes_to_words(get_account().get_keys().m_spend_secret_key, electrum_words, seed_language);
+ crypto::secret_key key = get_account().get_keys().m_spend_secret_key;
+ if (!passphrase.empty())
+ key = cryptonote::encrypt_key(key, passphrase);
+ crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language);
return true;
}
@@ -684,7 +691,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
std::deque<crypto::key_image> ki(tx.vout.size());
std::deque<uint64_t> amount(tx.vout.size());
std::deque<rct::key> mask(tx.vout.size());
- int threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ int threads = tpool.get_max_concurrency();
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
@@ -720,13 +729,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
++num_vouts_received;
// process the other outs from that tx
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
@@ -734,10 +736,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
- KILL_IOSERVICE();
+ waiter.wait();
for (size_t i = 1; i < tx.vout.size(); ++i)
{
if (error[i])
@@ -766,23 +768,18 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (tx.vout.size() > 1 && threads > 1)
{
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
std::deque<bool> received(tx.vout.size());
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
- KILL_IOSERVICE();
+ waiter.wait();
tx_money_got_in_outs = 0;
for (size_t i = 0; i < tx.vout.size(); ++i)
{
@@ -1251,7 +1248,8 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
- int threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ int threads = tpool.get_max_concurrency();
if (threads > 1)
{
std::vector<crypto::hash> round_block_hashes(threads);
@@ -1262,23 +1260,16 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
for (size_t b = 0; b < blocks_size; b += threads)
{
size_t round_size = std::min((size_t)threads, blocks_size - b);
-
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (size_t i = 0; i < round_size; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool::waiter waiter;
std::list<block_complete_entry>::const_iterator tmpblocki = blocki;
for (size_t i = 0; i < round_size; ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
+ tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i])));
++tmpblocki;
}
- KILL_IOSERVICE();
+ waiter.wait();
tmpblocki = blocki;
for (size_t i = 0; i < round_size; ++i)
{
@@ -1552,23 +1543,22 @@ void wallet2::update_pool_state(bool refreshed)
{
if (res.txs.size() == txids.size())
{
- size_t n = 0;
- for (const auto &txid: txids)
+ for (const auto &tx_entry: res.txs)
{
- // might have just been put in a block
- if (res.txs[n].in_pool)
+ if (tx_entry.in_pool)
{
cryptonote::transaction tx;
cryptonote::blobdata bd;
crypto::hash tx_hash, tx_prefix_hash;
- if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd))
+ if (epee::string_tools::parse_hexstr_to_binbuff(tx_entry.as_hex, bd))
{
if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
{
- if (tx_hash == txid)
+ const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash);
+ if (i != txids.end())
{
- process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
- m_scanned_pool_txs[0].insert(txid);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
@@ -1577,7 +1567,7 @@ void wallet2::update_pool_state(bool refreshed)
}
else
{
- LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool");
+ MERROR("Got txid " << tx_hash << " which we did not ask for");
}
}
else
@@ -1587,14 +1577,13 @@ void wallet2::update_pool_state(bool refreshed)
}
else
{
- LOG_PRINT_L0("Failed to parse tx " << txid);
+ LOG_PRINT_L0("Failed to parse transaction from daemon");
}
}
else
{
- LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more");
+ LOG_PRINT_L1("Transaction from daemon was in pool, but is no more");
}
- ++n;
}
}
else
@@ -1698,7 +1687,8 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
size_t try_count = 0;
crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
std::list<crypto::hash> short_chain_history;
- boost::thread pull_thread;
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
@@ -1736,11 +1726,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
std::list<cryptonote::block_complete_entry> next_blocks;
std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> next_o_indices;
bool error = false;
- pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
+ tpool.submit(&waiter, [&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
blocks_fetched += added_blocks;
- pull_thread.join();
+ waiter.wait();
if(blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
@@ -1762,8 +1752,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- if (pull_thread.joinable())
- pull_thread.join();
+ waiter.wait();
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
@@ -2668,10 +2657,11 @@ void wallet2::store_to(const std::string &path, const std::string &password)
// if we here, main wallet file is saved and we only need to save keys and address files
if (!same_file) {
prepare_file_names(path);
- store_keys(m_keys_file, password, false);
+ bool r = store_keys(m_keys_file, password, false);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
// save address to the new file
const std::string address_file = m_wallet_file + ".address.txt";
- bool r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet));
+ r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet));
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file);
// remove old wallet file
r = boost::filesystem::remove(old_file);
@@ -5404,6 +5394,9 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
}
spent = 0;
unspent = 0;
+ std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
+ std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
+ // was created by sweep_all, so we can't know the spent height and other detailed info.
for(size_t i = 0; i < m_transfers.size(); ++i)
{
transfer_details &td = m_transfers[i];
@@ -5414,8 +5407,146 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
unspent += amount;
LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
<< (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
+
+ if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
+ {
+ bool is_spent_tx_found = false;
+ for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
+ {
+ bool is_spent_tx = false;
+ for(const cryptonote::txin_v& in : it->m_tx.vin)
+ {
+ if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
+ {
+ is_spent_tx = true;
+ break;
+ }
+ }
+ if (is_spent_tx)
+ {
+ is_spent_tx_found = true;
+ spent_txids.insert(it->m_txid);
+ break;
+ }
+ }
+
+ if (!is_spent_tx_found)
+ swept_transfers.push_back(i);
+ }
}
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
+
+ if (check_spent)
+ {
+ // query outgoing txes
+ COMMAND_RPC_GET_TRANSACTIONS::request gettxs_req;
+ COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res;
+ gettxs_req.decode_as_json = false;
+ for (const crypto::hash& spent_txid : spent_txids)
+ gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
+
+ // process each outgoing tx
+ auto spent_txid = spent_txids.begin();
+ for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
+ {
+ THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
+
+ // parse tx
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(e.as_hex, bd), error::wallet_internal_error, "parse_hexstr_to_binbuff failed");
+ cryptonote::transaction spent_tx;
+ crypto::hash spnet_txid_parsed, spent_txid_prefix;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, spent_tx, spnet_txid_parsed, spent_txid_prefix), error::wallet_internal_error, "parse_and_validate_tx_from_blob failed");
+ THROW_WALLET_EXCEPTION_IF(*spent_txid != spnet_txid_parsed, error::wallet_internal_error, "parsed txid mismatch");
+
+ // get received (change) amount
+ uint64_t tx_money_got_in_outs = 0;
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
+ crypto::key_derivation derivation;
+ generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ size_t output_index = 0;
+ for (const cryptonote::tx_out& out : spent_tx.vout)
+ {
+ uint64_t money_transfered = 0;
+ bool error = false, received = false;
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, out, derivation, output_index, received, money_transfered, error);
+ THROW_WALLET_EXCEPTION_IF(error, error::wallet_internal_error, "check_acc_out_precomp failed");
+ if (received)
+ {
+ if (money_transfered == 0)
+ {
+ rct::key mask;
+ money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, output_index, mask);
+ }
+ tx_money_got_in_outs += money_transfered;
+ }
+ ++output_index;
+ }
+
+ // get spent amount
+ uint64_t tx_money_spent_in_ins = 0;
+ for (const cryptonote::txin_v& in : spent_tx.vin)
+ {
+ if (in.type() != typeid(cryptonote::txin_to_key))
+ continue;
+ auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
+ if (it != m_key_images.end())
+ {
+ const transfer_details& td = m_transfers[it->second];
+ uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
+ if (amount > 0)
+ {
+ THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error,
+ std::string("Inconsistent amount in tx input: got ") + print_money(amount) +
+ std::string(", expected ") + print_money(td.amount()));
+ }
+ amount = td.amount();
+ tx_money_spent_in_ins += amount;
+
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << *spent_txid);
+ set_spent(it->second, e.block_height);
+ if (m_callback)
+ m_callback->on_money_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx);
+ }
+ }
+
+ // create outgoing payment
+ process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_money_spent_in_ins, tx_money_got_in_outs);
+
+ // erase corresponding incoming payment
+ for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
+ {
+ if (j->second.m_tx_hash == *spent_txid)
+ {
+ m_payments.erase(j);
+ break;
+ }
+ }
+
+ ++spent_txid;
+ }
+
+ for (size_t n : swept_transfers)
+ {
+ const transfer_details& td = m_transfers[n];
+ confirmed_transfer_details pd;
+ pd.m_change = (uint64_t)-1; // cahnge is unknown
+ pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
+ std::string err;
+ pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
+ crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
+ m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
+ }
+ }
+
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
wallet2::payment_container wallet2::export_payments() const
@@ -5559,7 +5690,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
crypto::secret_key_to_public_key(skey, pkey);
const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
- error::wallet_internal_error, "Failed to authenticate criphertext");
+ error::wallet_internal_error, "Failed to authenticate ciphertext");
}
crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
return plaintext;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index adf03abcc..971e98351 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -363,7 +363,7 @@ namespace tools
* \brief Checks if deterministic wallet
*/
bool is_deterministic() const;
- bool get_seed(std::string& electrum_words) const;
+ bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const;
/*!
* \brief Gets the seed language
*/
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 8da8c62eb..7a5e01af7 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -663,7 +663,7 @@ struct WalletManager
* \param wallet previously opened / created wallet instance
* \return None
*/
- virtual bool closeWallet(Wallet *wallet) = 0;
+ virtual bool closeWallet(Wallet *wallet, bool store = true) = 0;
/*
* ! checks if wallet with the given name already exists
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 68f56e06f..22cfcc269 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -100,7 +100,7 @@ namespace wallet_args
command_line::add_arg(desc_general, command_line::arg_help);
command_line::add_arg(desc_general, command_line::arg_version);
- command_line::add_arg(desc_params, arg_log_file, "");
+ command_line::add_arg(desc_params, arg_log_file);
command_line::add_arg(desc_params, arg_log_level);
command_line::add_arg(desc_params, arg_max_log_file_size);
command_line::add_arg(desc_params, arg_max_concurrency);
@@ -116,6 +116,21 @@ namespace wallet_args
auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options);
po::store(parser.run(), vm);
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
+ tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
+ "daemon to work correctly.") << ENDL;
+ tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
+ tools::msg_writer() << desc_all;
+ return false;
+ }
+ else if (command_line::get_arg(vm, command_line::arg_version))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ return false;
+ }
+
if(command_line::has_arg(vm, arg_config_file))
{
std::string config = command_line::get_arg(vm, arg_config_file);
@@ -149,21 +164,6 @@ namespace wallet_args
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
}
- if (command_line::get_arg(vm, command_line::arg_help))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
- tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
- "daemon to work correctly.") << ENDL;
- tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
- tools::msg_writer() << desc_all;
- return boost::none;
- }
- else if (command_line::get_arg(vm, command_line::arg_version))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- return boost::none;
- }
-
if(command_line::has_arg(vm, arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 773d12775..cb35482bd 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <boost/format.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
#include <cstdint>
@@ -37,7 +38,6 @@ using namespace epee;
#include "wallet/wallet_args.h"
#include "common/command_line.h"
#include "common/i18n.h"
-#include "common/util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "wallet_rpc_server_commands_defs.h"
@@ -70,18 +70,12 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
- wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false)
+ wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_vm(NULL)
{
}
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
- try
- {
- boost::system::error_code ec{};
- boost::filesystem::remove(rpc_login_filename, ec);
- }
- catch (...) {}
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
@@ -160,7 +154,15 @@ namespace tools
#else
#define MKDIR(path, mode) mkdir(path, mode)
#endif
- MKDIR(m_wallet_dir.c_str(), 0700);
+ if (MKDIR(m_wallet_dir.c_str(), 0700) < 0)
+ {
+#ifdef _WIN32
+ LOG_ERROR(tr("Failed to create directory ") + m_wallet_dir);
+#else
+ LOG_ERROR((boost::format(tr("Failed to create directory %s: %s")) % m_wallet_dir % strerror(errno)).str());
+#endif
+ return false;
+ }
}
if (disable_auth)
@@ -182,34 +184,32 @@ namespace tools
default_rpc_username,
string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size())
);
+
+ std::string temp = "monero-wallet-rpc." + bind_port + ".login";
+ rpc_login_file = tools::private_file::create(temp);
+ if (!rpc_login_file.handle())
+ {
+ LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
+ return false;
+ }
+ std::fputs(http_login->username.c_str(), rpc_login_file.handle());
+ std::fputc(':', rpc_login_file.handle());
+ std::fputs(http_login->password.c_str(), rpc_login_file.handle());
+ std::fflush(rpc_login_file.handle());
+ if (std::ferror(rpc_login_file.handle()))
+ {
+ LOG_ERROR(tr("Error writing to file ") << temp);
+ return false;
+ }
+ LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
}
- else
+ else // chosen user/pass
{
http_login.emplace(
std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()
);
}
assert(bool(http_login));
-
- std::string temp = "monero-wallet-rpc." + bind_port + ".login";
- const auto cookie = tools::create_private_file(temp);
- if (!cookie)
- {
- LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
- return false;
- }
- rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup
- temp = rpc_login_filename;
- std::fputs(http_login->username.c_str(), cookie.get());
- std::fputc(':', cookie.get());
- std::fputs(http_login->password.c_str(), cookie.get());
- std::fflush(cookie.get());
- if (std::ferror(cookie.get()))
- {
- LOG_ERROR(tr("Error writing to file ") << temp);
- return false;
- }
- LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
} // end auth enabled
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
@@ -1545,7 +1545,7 @@ namespace tools
er.message = "Failed to add address book entry";
return false;
}
- res.index = m_wallet->get_address_book().size();
+ res.index = m_wallet->get_address_book().size() - 1;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1896,7 +1896,15 @@ just_dir:
wrpc.send_stop_signal();
});
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet rpc server"));
- wrpc.run();
+ 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
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index dd54222b0..e5ed0a846 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -33,6 +33,7 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <string>
+#include "common/util.h"
#include "net/http_server_impl_base.h"
#include "wallet_rpc_server_commands_defs.h"
#include "wallet2.h"
@@ -154,7 +155,7 @@ namespace tools
wallet2 *m_wallet;
std::string m_wallet_dir;
- std::string rpc_login_filename;
+ tools::private_file rpc_login_file;
std::atomic<bool> m_stop;
bool m_trusted_daemon;
epee::net_utils::http::http_simple_client m_http_client;