aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/pending_transaction.cpp2
-rw-r--r--src/wallet/api/transaction_history.cpp1
-rw-r--r--src/wallet/api/transaction_info.cpp6
-rw-r--r--src/wallet/api/transaction_info.h2
-rw-r--r--src/wallet/api/unsigned_transaction.cpp10
-rw-r--r--src/wallet/api/wallet.cpp16
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/api/wallet_manager.cpp4
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/wallet2.cpp106
-rw-r--r--src/wallet/wallet2.h18
-rw-r--r--src/wallet/wallet2_api.h18
-rw-r--r--src/wallet/wallet_args.cpp6
-rw-r--r--src/wallet/wallet_errors.h4
-rw-r--r--src/wallet/wallet_rpc_server.cpp108
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h26
16 files changed, 261 insertions, 68 deletions
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index 9798d66c6..c98a599e7 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -102,6 +102,7 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
}
// Commit tx
else {
+ m_wallet.pauseRefresh();
while (!m_pending_tx.empty()) {
auto & ptx = m_pending_tx.back();
m_wallet.m_wallet->commit_tx(ptx);
@@ -133,6 +134,7 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
m_status = Status_Error;
}
+ m_wallet.startRefresh();
return m_status == Status_Ok;
}
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 85f2b05ce..23d3905b2 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -132,6 +132,7 @@ void TransactionHistoryImpl::refresh()
ti->m_blockheight = pd.m_block_height;
ti->m_timestamp = pd.m_timestamp;
ti->m_confirmations = wallet_height - pd.m_block_height;
+ ti->m_unlock_time = pd.m_unlock_time;
m_history.push_back(ti);
/* output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%20.20s %s %s %s")
diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp
index 79a8fe9b5..171272265 100644
--- a/src/wallet/api/transaction_info.cpp
+++ b/src/wallet/api/transaction_info.cpp
@@ -50,6 +50,7 @@ TransactionInfoImpl::TransactionInfoImpl()
, m_blockheight(0)
, m_timestamp(0)
, m_confirmations(0)
+ , m_unlock_time(0)
{
}
@@ -115,6 +116,11 @@ uint64_t TransactionInfoImpl::confirmations() const
return m_confirmations;
}
+uint64_t TransactionInfoImpl::unlockTime() const
+{
+ return m_unlock_time;
+}
+
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index 16fa5da7a..ee56b859f 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -56,6 +56,7 @@ public:
virtual std::string paymentId() const;
virtual const std::vector<Transfer> &transfers() const;
virtual uint64_t confirmations() const;
+ virtual uint64_t unlockTime() const;
private:
int m_direction;
@@ -69,6 +70,7 @@ private:
std::string m_paymentid;
std::vector<Transfer> m_transfers;
uint64_t m_confirmations;
+ uint64_t m_unlock_time;
friend class TransactionHistoryImpl;
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 1d9ef5d7c..961bd772a 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -101,7 +101,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
{
// gather info to ask the user
uint64_t amount = 0, amount_to_dests = 0, change = 0;
- size_t min_mixin = ~0;
+ size_t min_ring_size = ~0;
std::unordered_map<std::string, uint64_t> dests;
const std::string wallet_address = m_wallet.m_wallet->get_account().get_public_address_str(m_wallet.m_wallet->testnet());
int first_known_non_zero_change_index = -1;
@@ -111,9 +111,9 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
for (size_t s = 0; s < cd.sources.size(); ++s)
{
amount += cd.sources[s].amount;
- size_t mixin = cd.sources[s].outputs.size() - 1;
- if (mixin < min_mixin)
- min_mixin = mixin;
+ size_t ring_size = cd.sources[s].outputs.size();
+ if (ring_size < min_ring_size)
+ min_ring_size = ring_size;
}
for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
{
@@ -178,7 +178,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
else
change_string += tr("no change");
uint64_t fee = amount - amount_to_dests;
- m_confirmationMessage = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %s")) % (unsigned long)get_num_txes() % cryptonote::print_money(amount) % cryptonote::print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % extra_message).str();
+ m_confirmationMessage = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu. %s")) % (unsigned long)get_num_txes() % cryptonote::print_money(amount) % cryptonote::print_money(fee) % dest_string % change_string % (unsigned long)min_ring_size % extra_message).str();
return true;
}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 6a0e1727c..a2bb44cfe 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -99,7 +99,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount)
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount)
{
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
@@ -114,7 +114,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount)
+ virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount)
{
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
@@ -129,8 +129,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount,
- const cryptonote::transaction& spend_tx)
+ virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx,
+ uint64_t amount, const cryptonote::transaction& spend_tx)
{
// TODO;
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
@@ -1009,9 +1009,9 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
} catch (const tools::error::not_enough_outs_to_mix& e) {
std::ostringstream writer;
- writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":";
+ writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
for (const std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) {
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.second;
+ writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
}
m_errorString = writer.str();
m_status = Status_Error;
@@ -1103,9 +1103,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
} catch (const tools::error::not_enough_outs_to_mix& e) {
std::ostringstream writer;
- writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":";
+ writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
for (const std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) {
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.second;
+ writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
}
m_errorString = writer.str();
m_status = Status_Error;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index e9e2cc580..07b0f063b 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -98,6 +98,7 @@ public:
void setAutoRefreshInterval(int millis);
int autoRefreshInterval() const;
void setRefreshFromBlockHeight(uint64_t refresh_from_block_height);
+ uint64_t getRefreshFromBlockHeight() const { return m_wallet->get_refresh_from_block_height(); };
void setRecoveringFromSeed(bool recoveringFromSeed);
bool watchOnly() const;
bool rescanSpent();
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index b2f947972..a23533530 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -125,6 +125,10 @@ bool WalletManagerImpl::walletExists(const std::string &path)
return false;
}
+bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const
+{
+ return tools::wallet2::verify_password(keys_file_name, password, watch_only);
+}
std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path)
{
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 033e8108f..aa6ea439e 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -50,6 +50,7 @@ public:
const std::string &spendKeyString = "");
virtual bool closeWallet(Wallet *wallet);
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);
std::string errorString() const;
void setDaemonAddress(const std::string &address);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index bb8cdaf63..51c772d29 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1097,6 +1097,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
}
entry.first->second.m_block_height = height;
entry.first->second.m_timestamp = ts;
+ entry.first->second.m_unlock_time = tx.unlock_time;
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices)
@@ -1388,7 +1389,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::update_pool_state()
+void wallet2::update_pool_state(bool refreshed)
{
MDEBUG("update_pool_state start");
@@ -1424,13 +1425,14 @@ void wallet2::update_pool_state()
// a tx is removed from the pool due to being found in a new block, but
// just before the block is visible by refresh. So we keep a boolean, so
// that the first time we don't see the tx, we set that boolean, and only
- // delete it the second time it is checked
+ // delete it the second time it is checked (but only when refreshed, so
+ // we're sure we've seen the blockchain state first)
if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
{
LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool");
pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool;
}
- else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool)
+ else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
{
LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed");
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
@@ -1459,24 +1461,30 @@ void wallet2::update_pool_state()
MDEBUG("update_pool_state done first loop");
// remove pool txes to us that aren't in the pool anymore
- std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
- while (uit != m_unconfirmed_payments.end())
+ // but only if we just refreshed, so that the tx can go in
+ // the in transfers list instead (or nowhere if it just
+ // disappeared without being mined)
+ if (refreshed)
{
- const crypto::hash &txid = uit->second.m_tx_hash;
- bool found = false;
- for (const auto &it2: res.tx_hashes)
+ std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
+ while (uit != m_unconfirmed_payments.end())
{
- if (it2 == txid)
+ const crypto::hash &txid = uit->second.m_tx_hash;
+ bool found = false;
+ for (const auto &it2: res.tx_hashes)
{
- found = true;
- break;
+ if (it2 == txid)
+ {
+ found = true;
+ break;
+ }
+ }
+ auto pit = uit++;
+ if (!found)
+ {
+ MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
+ m_unconfirmed_payments.erase(pit);
}
- }
- auto pit = uit++;
- if (!found)
- {
- MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
- m_unconfirmed_payments.erase(pit);
}
}
MDEBUG("update_pool_state done second loop");
@@ -1490,7 +1498,16 @@ void wallet2::update_pool_state()
LOG_PRINT_L2("Already seen " << txid << ", skipped");
continue;
}
- if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end())
+ bool txid_found_in_up = false;
+ for (const auto &up: m_unconfirmed_payments)
+ {
+ if (up.second.m_tx_hash == txid)
+ {
+ txid_found_in_up = true;
+ break;
+ }
+ }
+ if (!txid_found_in_up)
{
LOG_PRINT_L1("Found new pool tx: " << txid);
bool found = false;
@@ -1685,6 +1702,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
+ bool refreshed = false;
// pull the first set of blocks
get_short_chain_history(short_chain_history);
@@ -1726,6 +1744,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
if(blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
+ refreshed = true;
break;
}
@@ -1764,7 +1783,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
{
// If stop() is called we don't need to check pending transactions
if(m_run.load(std::memory_order_relaxed))
- update_pool_state();
+ update_pool_state(refreshed);
}
catch (...)
{
@@ -1940,6 +1959,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p
value2.SetInt(m_merge_destinations ? 1 :0);
json.AddMember("merge_destinations", value2, json.GetAllocator());
+ value2.SetInt(m_testnet ? 1 :0);
+ json.AddMember("testnet", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -2081,6 +2103,11 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_min_output_value = field_min_output_value;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false);
m_merge_destinations = field_merge_destinations;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, testnet, int, Int, false, m_testnet);
+ // Wallet is being opened with testnet flag, but is saved as a mainnet wallet
+ THROW_WALLET_EXCEPTION_IF(m_testnet && !field_testnet, error::wallet_internal_error, "Mainnet wallet can not be opened as testnet wallet");
+ // Wallet is being opened without testnet flag but is saved as a testnet wallet.
+ THROW_WALLET_EXCEPTION_IF(!m_testnet && field_testnet, error::wallet_internal_error, "Testnet wallet can not be opened as mainnet wallet");
}
const cryptonote::account_keys& keys = m_account.get_keys();
@@ -2095,6 +2122,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
/*!
* \brief verify password for default wallet keys file.
* \param password Password to verify
+ * \return true if password is correct
*
* for verification only
* should not mutate state, unlike load_keys()
@@ -2103,7 +2131,23 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
*/
bool wallet2::verify_password(const std::string& password) const
{
- const std::string keys_file_name = m_keys_file;
+ return verify_password(m_keys_file, password, m_watch_only);
+}
+
+/*!
+ * \brief verify password for specified wallet keys file.
+ * \param keys_file_name Keys file to verify password for
+ * \param password Password to verify
+ * \param watch_only If set = only verify view keys, otherwise also spend keys
+ * \return true if password is correct
+ *
+ * for verification only
+ * should not mutate state, unlike load_keys()
+ * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
+ *
+ */
+bool wallet2::verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only)
+{
wallet2::keys_file_data keys_file_data;
std::string buf;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
@@ -2136,7 +2180,7 @@ bool wallet2::verify_password(const std::string& password) const
const cryptonote::account_keys& keys = account_data_check.get_keys();
r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
- if(!m_watch_only)
+ if(!watch_only)
r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
return r;
}
@@ -3252,7 +3296,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
const tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
- LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, mixin " << (sd.sources[0].outputs.size()-1));
+ LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
crypto::secret_key tx_key;
@@ -4119,7 +4163,7 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
// txnFee
size += 4;
- LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " at mixin " << mixin << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size;
}
@@ -4359,7 +4403,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// try to pick outputs not from the same block. We will get two outputs, one for
// the destination, and one for change.
LOG_PRINT_L2("checking preferred");
- std::vector<size_t> prefered_inputs;
+ std::vector<size_t> preferred_inputs;
uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
if (use_rct && get_num_rct_outputs() >= rct_outs_needed)
@@ -4367,12 +4411,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count + 1, 2), fee_multiplier);
- prefered_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee);
- if (!prefered_inputs.empty())
+ preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee);
+ if (!preferred_inputs.empty())
{
string s;
- for (auto i: prefered_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
- LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s);
+ for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
+ LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s);
}
}
LOG_PRINT_L2("done checking preferred");
@@ -4427,8 +4471,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
pop_if_present(unused_transfers_indices, idx);
pop_if_present(unused_dust_indices, idx);
- } else if (!prefered_inputs.empty()) {
- idx = pop_back(prefered_inputs);
+ } else if (!preferred_inputs.empty()) {
+ idx = pop_back(preferred_inputs);
pop_if_present(unused_transfers_indices, idx);
pop_if_present(unused_dust_indices, idx);
} else
@@ -4521,7 +4565,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_testnet, i->addr) << " from " <<
- print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accomodate " <<
+ print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accommodate " <<
print_money(needed_fee) << " fee");
dsts[0].amount += i->amount - new_paid_amount;
i->amount = new_paid_amount;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 96dea2e40..ba9abacf5 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -129,6 +129,8 @@ namespace tools
//! Just parses variables.
static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm);
+ static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only);
+
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_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
struct transfer_details
@@ -199,10 +201,11 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> m_dests;
crypto::hash m_payment_id;
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) {}
+ 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(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_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) {}
};
struct tx_construction_data
@@ -585,7 +588,7 @@ namespace tools
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
- void update_pool_state();
+ void update_pool_state(bool refreshed = false);
std::string encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const;
std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated = true) const;
@@ -706,7 +709,7 @@ BOOST_CLASS_VERSION(tools::wallet2, 18)
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)
-BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 3)
+BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 4)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 16)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
@@ -881,6 +884,13 @@ namespace boost
x.m_amount_out += x.m_change;
}
}
+ if (ver < 4)
+ {
+ if (!typename Archive::is_saving())
+ x.m_unlock_time = 0;
+ return;
+ }
+ a & x.m_unlock_time;
}
template <class Archive>
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index a136954f0..b7483ef8c 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -156,6 +156,7 @@ struct TransactionInfo
virtual uint64_t fee() const = 0;
virtual uint64_t blockHeight() const = 0;
virtual uint64_t confirmations() const = 0;
+ virtual uint64_t unlockTime() const = 0;
//! transaction_id
virtual std::string hash() const = 0;
virtual std::time_t timestamp() const = 0;
@@ -380,6 +381,12 @@ struct Wallet
virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
/*!
+ * \brief getRestoreHeight - get wallet creation height
+ *
+ */
+ virtual uint64_t getRefreshFromBlockHeight() const = 0;
+
+ /*!
* \brief setRecoveringFromSeed - set state recover form seed
*
* \param recoveringFromSeed - true/false
@@ -662,11 +669,20 @@ struct WalletManager
/*!
* @brief TODO: delme walletExists - check if the given filename is the wallet
* @param path - filename
- * @return
+ * @return - true if wallet exists
*/
virtual bool walletExists(const std::string &path) = 0;
/*!
+ * @brief verifyWalletPassword - check if the given filename is the wallet
+ * @param keys_file_name - location of keys file
+ * @param password - password to verify
+ * @param watch_only - verify only view keys?
+ * @return - true if password is correct
+ */
+ virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const = 0;
+
+ /*!
* \brief findWallets - searches for the wallet files by given path name recursively
* \param path - starting point to search
* \return - list of strings with found wallets (absolute paths);
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 1508f3791..34c5a2a5d 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -149,8 +149,10 @@ namespace wallet_args
if (command_line::get_arg(vm, command_line::arg_help))
{
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage;
+ 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;
}
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 16807e045..2e5acc8cb 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -417,7 +417,7 @@ namespace tools
typedef std::unordered_map<uint64_t, uint64_t> scanty_outs_t;
explicit not_enough_outs_to_mix(std::string&& loc, const scanty_outs_t& scanty_outs, size_t mixin_count)
- : transfer_error(std::move(loc), "not enough outputs to mix")
+ : transfer_error(std::move(loc), "not enough outputs to use")
, m_scanty_outs(scanty_outs)
, m_mixin_count(mixin_count)
{
@@ -429,7 +429,7 @@ namespace tools
std::string to_string() const
{
std::ostringstream ss;
- ss << transfer_error::to_string() << ", mixin_count = " << m_mixin_count << ", scanty_outs:";
+ ss << transfer_error::to_string() << ", ring size = " << (m_mixin_count + 1) << ", scanty_outs:";
for (const auto& out: m_scanty_outs)
{
ss << '\n' << cryptonote::print_money(out.first) << " - " << out.second;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index e7b9b5a71..da55c2141 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -236,6 +236,7 @@ namespace tools
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
+ entry.unlock_time = pd.m_unlock_time;
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "in";
@@ -249,6 +250,7 @@ namespace tools
entry.payment_id = entry.payment_id.substr(0,16);
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
+ entry.unlock_time = pd.m_unlock_time;
entry.fee = pd.m_amount_in - pd.m_amount_out;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
entry.amount = pd.m_amount_in - change - entry.fee;
@@ -276,6 +278,7 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.fee = pd.m_amount_in - pd.m_amount_out;
entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
+ entry.unlock_time = pd.m_tx.unlock_time;
entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
}
@@ -289,6 +292,7 @@ namespace tools
entry.height = 0;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
+ entry.unlock_time = pd.m_unlock_time;
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "pool";
@@ -352,10 +356,25 @@ namespace tools
cryptonote::tx_destination_entry de;
bool has_payment_id;
crypto::hash8 new_payment_id;
- if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false))
+ er.message = "";
+ if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address,
+ [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
+ if (!dnssec_valid)
+ {
+ er.message = std::string("Invalid DNSSEC for ") + url;
+ return {};
+ }
+ if (addresses.empty())
+ {
+ er.message = std::string("No Monero address found at ") + url;
+ return {};
+ }
+ return addresses[0];
+ }))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
- er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
+ if (er.message.empty())
+ er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
return false;
}
de.amount = it->amount;
@@ -441,7 +460,7 @@ namespace tools
{
uint64_t mixin = req.mixin;
if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) {
- LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
+ LOG_PRINT_L1("Requested ring size " << (req.mixin + 1) << " too low for hard fork 2, using 3");
mixin = 2;
}
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
@@ -454,7 +473,8 @@ namespace tools
return false;
}
- m_wallet->commit_tx(ptx_vector);
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hash
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
@@ -463,6 +483,13 @@ namespace tools
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
}
res.fee = ptx_vector.back().fee;
+
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx_vector.back().tx, blob);
+ res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
+ }
return true;
}
catch (const tools::error::daemon_busy& e)
@@ -511,7 +538,7 @@ namespace tools
uint64_t mixin = req.mixin;
uint64_t ptx_amount;
if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) {
- LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
+ LOG_PRINT_L1("Requested ring size " << (req.mixin + 1) << " too low for hard fork 2, using 3");
mixin = 2;
}
std::vector<wallet2::pending_tx> ptx_vector;
@@ -519,9 +546,12 @@ namespace tools
ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
- LOG_PRINT_L2("on_transfer_split calling commit_tx");
- m_wallet->commit_tx(ptx_vector);
- LOG_PRINT_L2("on_transfer_split called commit_tx");
+ if (!req.do_not_relay)
+ {
+ LOG_PRINT_L2("on_transfer_split calling commit_tx");
+ m_wallet->commit_tx(ptx_vector);
+ LOG_PRINT_L2("on_transfer_split called commit_tx");
+ }
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -538,6 +568,13 @@ namespace tools
res.amount_list.push_back(ptx_amount);
res.fee_list.push_back(ptx.fee);
+
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
+ }
}
return true;
@@ -577,7 +614,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- m_wallet->commit_tx(ptx_vector);
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -588,6 +626,12 @@ namespace tools
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
res.fee_list.push_back(ptx.fee);
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
+ }
}
return true;
@@ -640,7 +684,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
- m_wallet->commit_tx(ptx_vector);
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -650,7 +695,12 @@ namespace tools
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
- res.fee_list.push_back(ptx.fee);
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
+ }
}
return true;
@@ -1018,10 +1068,23 @@ namespace tools
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id;
- if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false))
+ er.message = "";
+ if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address,
+ [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
+ if (!dnssec_valid)
+ {
+ er.message = std::string("Invalid DNSSEC for ") + url;
+ return {};
+ }
+ if (addresses.empty())
+ {
+ er.message = std::string("No Monero address found at ") + url;
+ return {};
+ }
+ return addresses[0];
+ }))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
- er.message = "";
return false;
}
@@ -1412,10 +1475,25 @@ namespace tools
bool has_payment_id;
crypto::hash8 payment_id8;
crypto::hash payment_id = cryptonote::null_hash;
- if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false))
+ 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 {
+ if (!dnssec_valid)
+ {
+ er.message = std::string("Invalid DNSSEC for ") + url;
+ return {};
+ }
+ if (addresses.empty())
+ {
+ er.message = std::string("No Monero address found at ") + url;
+ return {};
+ }
+ return addresses[0];
+ }))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
- er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
+ if (er.message.empty())
+ er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
return false;
}
if (has_payment_id)
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 12ac281e4..fa5c154de 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -119,6 +119,8 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_key;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -127,6 +129,8 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_key)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -136,12 +140,14 @@ namespace wallet_rpc
std::string tx_key;
std::list<std::string> amount_keys;
uint64_t fee;
+ std::string tx_blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount_keys)
KV_SERIALIZE(fee)
+ KV_SERIALIZE(tx_blob)
END_KV_SERIALIZE_MAP()
};
};
@@ -156,6 +162,8 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_keys;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -164,6 +172,8 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -182,12 +192,14 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
+ std::list<std::string> tx_blob_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(tx_blob_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -197,9 +209,13 @@ namespace wallet_rpc
struct request
{
bool get_tx_keys;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(get_tx_keys)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -217,11 +233,13 @@ namespace wallet_rpc
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
+ std::list<std::string> tx_blob_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(tx_blob_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -237,6 +255,8 @@ namespace wallet_rpc
std::string payment_id;
bool get_tx_keys;
uint64_t below_amount;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
@@ -246,6 +266,8 @@ namespace wallet_rpc
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE(below_amount)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -263,11 +285,13 @@ namespace wallet_rpc
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
+ std::list<std::string> tx_blob_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(tx_blob_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -536,6 +560,7 @@ namespace wallet_rpc
std::string note;
std::list<transfer_destination> destinations;
std::string type;
+ uint64_t unlock_time;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid);
@@ -547,6 +572,7 @@ namespace wallet_rpc
KV_SERIALIZE(note);
KV_SERIALIZE(destinations);
KV_SERIALIZE(type);
+ KV_SERIALIZE(unlock_time)
END_KV_SERIALIZE_MAP()
};