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.cpp6
-rw-r--r--src/wallet/api/unsigned_transaction.cpp8
-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.cpp468
-rw-r--r--src/wallet/wallet2.h78
-rw-r--r--src/wallet/wallet2_api.h2
-rw-r--r--src/wallet/wallet_args.cpp6
-rw-r--r--src/wallet/wallet_rpc_server.cpp78
-rw-r--r--src/wallet/wallet_rpc_server.h3
13 files changed, 469 insertions, 214 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..9605047b7 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)
{
@@ -57,7 +57,7 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa
return false;
}
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
bool has_long_pid = (payment_id_str.empty())? false : tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
// Short payment id provided
@@ -106,7 +106,7 @@ void AddressBookImpl::refresh()
for (size_t i = 0; i < rows.size(); ++i) {
tools::wallet2::address_book_row * row = &rows.at(i);
- std::string payment_id = (row->m_payment_id == cryptonote::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id);
+ std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id);
std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->testnet(),row->m_address);
// convert the zero padded short payment id to integrated address
if (payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) {
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 961bd772a..5105278e4 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -230,13 +230,13 @@ std::vector<std::string> UnsignedTransactionImpl::paymentId() const
{
std::vector<string> result;
for (const auto &utx: m_unsigned_tx_set.txes) {
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
cryptonote::tx_extra_nonce extra_nonce;
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
cryptonote::parse_tx_extra(utx.extra, tx_extra_fields);
if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
- crypto::hash8 payment_id8 = cryptonote::null_hash8;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
// We can't decrypt short pid without recipient key.
@@ -244,10 +244,10 @@ std::vector<std::string> UnsignedTransactionImpl::paymentId() const
}
else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
- payment_id = cryptonote::null_hash;
+ payment_id = crypto::null_hash;
}
}
- if(payment_id != cryptonote::null_hash)
+ if(payment_id != crypto::null_hash)
result.push_back(epee::string_tools::pod_to_hex(payment_id));
else
result.push_back("");
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 805d5c737..08e1d2a09 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -33,6 +33,7 @@
#include <boost/format.hpp>
#include <boost/optional/optional.hpp>
#include <boost/utility/value_init.hpp>
+#include <boost/algorithm/string/join.hpp>
#include "include_base_utils.h"
using namespace epee;
@@ -289,6 +290,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());
@@ -376,7 +384,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);
@@ -419,6 +431,20 @@ static void throw_on_rpc_response_error(const boost::optional<std::string> &stat
THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, *status);
}
+std::string strjoin(const std::vector<size_t> &V, const char *sep)
+{
+ std::stringstream ss;
+ bool first = true;
+ for (const auto &v: V)
+ {
+ if (!first)
+ ss << sep;
+ ss << std::to_string(v);
+ first = false;
+ }
+ return ss.str();
+}
+
} //namespace
namespace tools
@@ -502,6 +528,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
//----------------------------------------------------------------------------------------------------
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit)
{
+ m_checkpoints.init_default_checkpoints(m_testnet);
if(m_http_client.is_connected())
m_http_client.disconnect();
m_is_initialized = true;
@@ -520,7 +547,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)
@@ -534,7 +561,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;
}
@@ -577,24 +607,24 @@ void wallet2::set_unspent(size_t idx)
td.m_spent_height = 0;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const
+void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, tx_scan_info_t &tx_scan_info) const
{
if (o.target.type() != typeid(txout_to_key))
{
- error = true;
+ tx_scan_info.error = true;
LOG_ERROR("wrong type id in transaction out");
return;
}
- received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i);
- if(received)
+ tx_scan_info.received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i);
+ if(tx_scan_info.received)
{
- money_transfered = o.amount; // may be 0 for ringct outputs
+ tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs
}
else
{
- money_transfered = 0;
+ tx_scan_info.money_transfered = 0;
}
- error = false;
+ tx_scan_info.error = false;
}
//----------------------------------------------------------------------------------------------------
static uint64_t decodeRct(const rct::rctSig & rv, const crypto::public_key &pub, const crypto::secret_key &sec, unsigned int i, rct::key & mask)
@@ -635,6 +665,22 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a
return true;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs)
+{
+ wallet_generate_key_image_helper(keys, tx_pub_key, i, tx_scan_info.in_ephemeral, tx_scan_info.ki);
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
+
+ outs.push_back(i);
+ if (tx_scan_info.money_transfered == 0)
+ {
+ tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, i, tx_scan_info.mask);
+ }
+ tx_money_got_in_outs += tx_scan_info.money_transfered;
+ tx_scan_info.amount = tx_scan_info.money_transfered;
+ ++num_vouts_received;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
{
// In this function, tx (probably) only contains the base information
@@ -673,13 +719,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
bool r = true;
- std::deque<cryptonote::keypair> in_ephemeral(tx.vout.size());
- 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());
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
- int threads = tpool.get_max_concurrency();
+ std::unique_ptr<tx_scan_info_t[]> tx_scan_info{new tx_scan_info_t[tx.vout.size()]};
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);
@@ -689,105 +731,64 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
- uint64_t money_transfered = 0;
- bool error = false, received = false;
- check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, received, money_transfered, error);
- if (error)
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, tx_scan_info[0]);
+ if (tx_scan_info[0].error)
{
r = false;
}
else
{
// this assumes that the miner tx pays a single address
- if (received)
+ if (tx_scan_info[0].received)
{
- wallet_generate_key_image_helper(keys, tx_pub_key, 0, in_ephemeral[0], ki[0]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(0);
- if (money_transfered == 0)
- {
- money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, 0, mask[0]);
- }
- amount[0] = money_transfered;
- tx_money_got_in_outs = money_transfered;
- ++num_vouts_received;
+ scan_output(keys, tx, tx_pub_key, 0, tx_scan_info[0], num_vouts_received, tx_money_got_in_outs, outs);
// process the other outs from that tx
-
- std::vector<uint64_t> money_transfered(tx.vout.size());
- std::deque<bool> error(tx.vout.size());
- std::deque<bool> received(tx.vout.size());
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++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])));
+ std::ref(tx_scan_info[i])));
}
waiter.wait();
+
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- if (error[i])
+ if (tx_scan_info[i].error)
{
r = false;
break;
}
- if (received[i])
+ if (tx_scan_info[i].received)
{
- wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(i);
- if (money_transfered[i] == 0)
- {
- money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
- }
- tx_money_got_in_outs += money_transfered[i];
- amount[i] = money_transfered[i];
- ++num_vouts_received;
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
}
}
- else if (tx.vout.size() > 1 && threads > 1)
+ else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1)
{
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)
{
- 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])));
+ 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(tx_scan_info[i])));
}
waiter.wait();
- tx_money_got_in_outs = 0;
+
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- if (error[i])
+ if (tx_scan_info[i].error)
{
r = false;
break;
}
- if (received[i])
+ if (tx_scan_info[i].received)
{
- wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(i);
- if (money_transfered[i] == 0)
- {
- money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
- }
- tx_money_got_in_outs += money_transfered[i];
- amount[i] = money_transfered[i];
- ++num_vouts_received;
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -795,31 +796,15 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- uint64_t money_transfered = 0;
- bool error = false, received = false;
- check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, received, money_transfered, error);
- if (error)
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, tx_scan_info[i]);
+ if (tx_scan_info[i].error)
{
r = false;
break;
}
- else
+ if (tx_scan_info[i].received)
{
- if (received)
- {
- wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(i);
- if (money_transfered == 0)
- {
- money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
- }
- amount[i] = money_transfered;
- tx_money_got_in_outs += money_transfered;
- ++num_vouts_received;
- }
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -841,7 +826,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
- auto kit = m_pub_keys.find(in_ephemeral[o].pub);
+ auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
error::wallet_internal_error, std::string("Unexpected transfer index from public key: ")
+ "got " + (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second))
@@ -857,14 +842,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
- td.m_key_image = ki[o];
+ td.m_key_image = tx_scan_info[o].ki;
td.m_key_image_known = !m_watch_only;
td.m_amount = tx.vout[o].amount;
td.m_pk_index = pk_index - 1;
if (td.m_amount == 0)
{
- td.m_mask = mask[o];
- td.m_amount = amount[o];
+ td.m_mask = tx_scan_info[o].mask;
+ td.m_amount = tx_scan_info[o].amount;
td.m_rct = true;
}
else if (miner_tx && tx.version == 2)
@@ -879,7 +864,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
set_unspent(m_transfers.size()-1);
m_key_images[td.m_key_image] = m_transfers.size()-1;
- m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1;
+ m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount);
@@ -912,8 +897,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_pk_index = pk_index - 1;
if (td.m_amount == 0)
{
- td.m_mask = mask[o];
- td.m_amount = amount[o];
+ td.m_mask = tx_scan_info[o].mask;
+ td.m_amount = tx_scan_info[o].amount;
td.m_rct = true;
}
else if (miner_tx && tx.version == 2)
@@ -926,7 +911,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_mask = rct::identity();
td.m_rct = false;
}
- THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys");
+ THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub, error::wallet_internal_error, "Inconsistent public keys");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
@@ -1128,16 +1113,19 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
{
size_t i = 0;
size_t current_multiplier = 1;
- size_t sz = m_blockchain.size();
+ size_t sz = m_blockchain.size() - m_blockchain.offset();
if(!sz)
+ {
+ ids.push_back(m_blockchain.genesis());
return;
+ }
size_t current_back_offset = 1;
- bool genesis_included = false;
+ bool base_included = false;
while(current_back_offset < sz)
{
- ids.push_back(m_blockchain[sz-current_back_offset]);
+ ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
if(sz-current_back_offset == 0)
- genesis_included = true;
+ base_included = true;
if(i < 10)
{
++current_back_offset;
@@ -1147,8 +1135,10 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
}
++i;
}
- if(!genesis_included)
- ids.push_back(m_blockchain[0]);
+ if(!base_included)
+ ids.push_back(m_blockchain[m_blockchain.offset()]);
+ if(m_blockchain.offset())
+ ids.push_back(m_blockchain.genesis());
}
//----------------------------------------------------------------------------------------------------
void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const
@@ -1233,6 +1223,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
size_t tx_o_indices_idx = 0;
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
+ THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain");
tools::threadpool& tpool = tools::threadpool::getInstance();
int threads = tpool.get_max_concurrency();
@@ -1529,23 +1520,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]);
@@ -1554,7 +1544,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
@@ -1564,14 +1554,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
@@ -1787,6 +1776,13 @@ bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
void wallet2::detach_blockchain(uint64_t height)
{
LOG_PRINT_L0("Detaching blockchain on height " << height);
+
+ // size 1 2 3 4 5 6 7 8 9
+ // block 0 1 2 3 4 5 6 7 8
+ // C
+ THROW_WALLET_EXCEPTION_IF(height <= m_checkpoints.get_max_height() && m_blockchain.size() > m_checkpoints.get_max_height(),
+ error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
+
size_t transfers_detached = 0;
for (size_t i = 0; i < m_transfers.size(); ++i)
@@ -1817,8 +1813,8 @@ void wallet2::detach_blockchain(uint64_t height)
}
m_transfers.erase(it, m_transfers.end());
- size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height);
- m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end());
+ size_t blocks_detached = m_blockchain.size() - height;
+ m_blockchain.crop(height);
m_local_bc_height -= blocks_detached;
for (auto it = m_payments.begin(); it != m_payments.end(); )
@@ -2562,13 +2558,49 @@ void wallet2::load(const std::string& wallet_, const std::string& password)
check_genesis(genesis_hash);
}
+ trim_hashchain();
+
m_local_bc_height = m_blockchain.size();
}
//----------------------------------------------------------------------------------------------------
+void wallet2::trim_hashchain()
+{
+ uint64_t height = m_checkpoints.get_max_height();
+ if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
+ {
+ MINFO("Fixing empty hashchain");
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request> req = AUTO_VAL_INIT(req);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response, std::string> res = AUTO_VAL_INIT(res);
+ m_daemon_rpc_mutex.lock();
+ req.jsonrpc = "2.0";
+ req.id = epee::serialization::storage_entry(0);
+ req.method = "getblockheaderbyheight";
+ req.params.height = m_blockchain.size() - 1;
+ bool r = net_utils::invoke_http_json("/json_rpc", req, res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ if (r && res.result.status == CORE_RPC_STATUS_OK)
+ {
+ crypto::hash hash;
+ epee::string_tools::hex_to_pod(res.result.block_header.hash, hash);
+ m_blockchain.refill(hash);
+ }
+ else
+ {
+ MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon");
+ }
+ }
+ if (height > 0 && m_blockchain.size() > height)
+ {
+ --height;
+ MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset());
+ m_blockchain.trim(height);
+ }
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
std::string what("Genesis block mismatch. You probably use wallet without testnet flag with blockchain from test network or vice versa");
- THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain[0], error::wallet_internal_error, what);
+ THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::path() const
@@ -2583,6 +2615,8 @@ void wallet2::store()
//----------------------------------------------------------------------------------------------------
void wallet2::store_to(const std::string &path, const std::string &password)
{
+ trim_hashchain();
+
// if file is the same, we do:
// 1. save wallet to the *.new file
// 2. remove old wallet file
@@ -2645,10 +2679,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);
@@ -3090,7 +3125,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
if(!parse_tx_extra(ptx.tx.extra, tx_extra_fields))
- return cryptonote::null_hash;
+ return crypto::null_hash;
tx_extra_nonce extra_nonce;
crypto::hash payment_id = null_hash;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
@@ -3105,7 +3140,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
}
else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
- payment_id = cryptonote::null_hash;
+ payment_id = crypto::null_hash;
}
}
return payment_id;
@@ -3154,7 +3189,7 @@ void wallet2::commit_tx(pending_tx& ptx)
}
txid = get_transaction_hash(ptx.tx);
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
std::vector<cryptonote::tx_destination_entry> dests;
uint64_t amount_in = 0;
if (store_tx_info())
@@ -4338,7 +4373,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error, "original_output_index too large");
+ THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error,
+ std::string("original_output_index too large: ") + std::to_string(original_output_index) + " > " + std::to_string(dsts.size()));
if (original_output_index == dsts.size())
dsts.push_back(tx_destination_entry(0,addr));
THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &addr, sizeof(addr)), error::wallet_internal_error, "Mismatched destination address");
@@ -4437,12 +4473,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
TX &tx = txes.back();
LOG_PRINT_L2("Start of loop with " << unused_transfers_indices.size() << " " << unused_dust_indices.size());
- LOG_PRINT_L2("unused_transfers_indices:");
- for (auto t: unused_transfers_indices)
- LOG_PRINT_L2(" " << t);
- LOG_PRINT_L2("unused_dust_indices:");
- for (auto t: unused_dust_indices)
- LOG_PRINT_L2(" " << t);
+ LOG_PRINT_L2("unused_transfers_indices: " << strjoin(unused_transfers_indices, " "));
+ LOG_PRINT_L2("unused_dust_indices:" << strjoin(unused_dust_indices, " "));
LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? -1 : dsts[0].amount));
LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct);
@@ -4616,6 +4648,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
LOG_PRINT_L2("We have more to pay, starting another tx");
txes.push_back(TX());
+ original_output_index = 0;
}
}
}
@@ -5164,10 +5197,9 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
{
- uint64_t money_transfered = 0;
- bool error = false, received = false;
- check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, received, money_transfered, error);
- if (!error && received)
+ tx_scan_info_t tx_scan_info;
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, tx_scan_info);
+ if (!tx_scan_info.error && tx_scan_info.received)
return tx_pub_key;
}
}
@@ -5175,7 +5207,7 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
// we found no key yielding an output
THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error,
"Public key yielding at least one output wasn't found in the transaction extra");
- return cryptonote::null_pkey;
+ return crypto::null_pkey;
}
bool wallet2::export_key_images(const std::string filename)
@@ -5381,6 +5413,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];
@@ -5391,8 +5426,145 @@ 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)
+ {
+ tx_scan_info_t tx_scan_info;
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, out, derivation, output_index, tx_scan_info);
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed");
+ if (tx_scan_info.received)
+ {
+ if (tx_scan_info.money_transfered == 0)
+ {
+ rct::key mask;
+ tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, output_index, mask);
+ }
+ tx_money_got_in_outs += tx_scan_info.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
@@ -5421,20 +5593,28 @@ void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2
}
}
-std::vector<crypto::hash> wallet2::export_blockchain() const
+std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const
{
- std::vector<crypto::hash> bc;
- for (auto const &b : m_blockchain)
+ std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc;
+ std::get<0>(bc) = m_blockchain.offset();
+ std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
+ for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
{
- bc.push_back(b);
+ std::get<2>(bc).push_back(m_blockchain[n]);
}
return bc;
}
-void wallet2::import_blockchain(const std::vector<crypto::hash> &bc)
+void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc)
{
m_blockchain.clear();
- for (auto const &b : bc)
+ if (std::get<0>(bc))
+ {
+ for (size_t n = std::get<0>(bc); n > 0; ++n)
+ m_blockchain.push_back(std::get<1>(bc));
+ m_blockchain.trim(std::get<0>(bc));
+ }
+ for (auto const &b : std::get<2>(bc))
{
m_blockchain.push_back(b);
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index adf03abcc..1dff14a95 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -36,6 +36,7 @@
#include <boost/program_options/variables_map.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
+#include <boost/serialization/deque.hpp>
#include <atomic>
#include "include_base_utils.h"
@@ -52,6 +53,7 @@
#include "crypto/hash.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
+#include "checkpoints/checkpoints.h"
#include "wallet_errors.h"
#include "common/password.h"
@@ -91,6 +93,38 @@ namespace tools
}
};
+ class hashchain
+ {
+ public:
+ hashchain(): m_genesis(crypto::null_hash), m_offset(0) {}
+
+ size_t size() const { return m_blockchain.size() + m_offset; }
+ size_t offset() const { return m_offset; }
+ const crypto::hash &genesis() const { return m_genesis; }
+ void push_back(const crypto::hash &hash) { if (m_offset == 0 && m_blockchain.empty()) m_genesis = hash; m_blockchain.push_back(hash); }
+ bool is_in_bounds(size_t idx) const { return idx >= m_offset && idx < size(); }
+ const crypto::hash &operator[](size_t idx) const { return m_blockchain[idx - m_offset]; }
+ crypto::hash &operator[](size_t idx) { return m_blockchain[idx - m_offset]; }
+ void crop(size_t height) { m_blockchain.resize(height - m_offset); }
+ void clear() { m_offset = 0; m_blockchain.clear(); }
+ bool empty() const { return m_blockchain.empty() && m_offset == 0; }
+ void trim(size_t height) { while (height > m_offset+1 && m_blockchain.size() > 1) { m_blockchain.pop_front(); ++m_offset; } m_blockchain.shrink_to_fit(); }
+ void refill(const crypto::hash &hash) { m_blockchain.push_back(hash); --m_offset; }
+
+ template <class t_archive>
+ inline void serialize(t_archive &a, const unsigned int ver)
+ {
+ a & m_offset;
+ a & m_genesis;
+ a & m_blockchain;
+ }
+
+ private:
+ size_t m_offset;
+ crypto::hash m_genesis;
+ std::deque<crypto::hash> m_blockchain;
+ };
+
class wallet2
{
friend class ::Serialization_portability_wallet_Test;
@@ -133,6 +167,19 @@ namespace tools
wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ struct tx_scan_info_t
+ {
+ cryptonote::keypair in_ephemeral;
+ crypto::key_image ki;
+ rct::key mask;
+ uint64_t amount;
+ uint64_t money_transfered;
+ bool error;
+ bool received;
+
+ tx_scan_info_t(): money_transfered(0), error(true), received(false) {}
+ };
+
struct transfer_details
{
uint64_t m_block_height;
@@ -203,7 +250,7 @@ namespace tools
uint64_t m_timestamp;
uint64_t m_unlock_time;
- confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash), m_timestamp(0), m_unlock_time(0) {}
+ confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0) {}
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time) {}
};
@@ -363,7 +410,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
*/
@@ -439,7 +486,19 @@ namespace tools
uint64_t dummy_refresh_height = 0; // moved to keys file
if(ver < 5)
return;
- a & m_blockchain;
+ if (ver < 19)
+ {
+ std::vector<crypto::hash> blockchain;
+ a & blockchain;
+ for (const auto &b: blockchain)
+ {
+ m_blockchain.push_back(b);
+ }
+ }
+ else
+ {
+ a & m_blockchain;
+ }
a & m_transfers;
a & m_account_public_address;
a & m_key_images;
@@ -588,8 +647,8 @@ namespace tools
payment_container export_payments() const;
void import_payments(const payment_container &payments);
void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
- std::vector<crypto::hash> export_blockchain() const;
- void import_blockchain(const std::vector<crypto::hash> &bc);
+ std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
+ void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
bool export_key_images(const std::string filename);
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
@@ -650,7 +709,7 @@ namespace tools
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
crypto::hash8 get_short_payment_id(const pending_tx &ptx) const;
- void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const;
+ void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, tx_scan_info_t &tx_scan_info) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit();
std::vector<uint64_t> get_unspent_amounts_vector();
@@ -664,6 +723,8 @@ namespace tools
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
+ void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs);
+ void trim_hashchain();
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
@@ -671,12 +732,13 @@ namespace tools
std::string m_wallet_file;
std::string m_keys_file;
epee::net_utils::http::http_simple_client m_http_client;
- std::vector<crypto::hash> m_blockchain;
+ hashchain m_blockchain;
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
+ cryptonote::checkpoints m_checkpoints;
transfer_container m_transfers;
payment_container m_payments;
@@ -716,7 +778,7 @@ namespace tools
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 18)
+BOOST_CLASS_VERSION(tools::wallet2, 19)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6)
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 81dc7e549..22cfcc269 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -84,6 +84,7 @@ namespace wallet_args
#endif
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
+ const command_line::arg_descriptor<std::size_t> arg_max_log_file_size = {"max-log-file-size", "Specify maximum log file size [B]", MAX_LOG_FILE_SIZE};
const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY};
const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""};
const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true};
@@ -99,8 +100,9 @@ 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);
command_line::add_arg(desc_params, arg_config_file);
@@ -156,7 +158,7 @@ namespace wallet_args
log_path = command_line::get_arg(vm, arg_log_file);
else
log_path = mlog_get_default_log_path(default_log_name);
- mlog_configure(log_path, log_to_console);
+ mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size));
if (!vm["log-level"].defaulted())
{
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 773d12775..46b092376 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());
@@ -362,7 +362,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
{
- crypto::hash8 integrated_payment_id = cryptonote::null_hash8;
+ crypto::hash8 integrated_payment_id = crypto::null_hash8;
std::string extra_nonce;
for (auto it = destinations.begin(); it != destinations.end(); it++)
{
@@ -395,7 +395,7 @@ namespace tools
if (has_payment_id)
{
- if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash8)
+ if (!payment_id.empty() || integrated_payment_id != crypto::null_hash8)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "A single payment id is allowed per transaction";
@@ -1485,7 +1485,7 @@ namespace tools
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id8;
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
er.message = "";
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
@@ -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;