diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/pending_transaction.cpp | 2 | ||||
-rw-r--r-- | src/wallet/api/transaction_history.cpp | 1 | ||||
-rw-r--r-- | src/wallet/api/transaction_info.cpp | 6 | ||||
-rw-r--r-- | src/wallet/api/transaction_info.h | 2 | ||||
-rw-r--r-- | src/wallet/api/unsigned_transaction.cpp | 10 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 16 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 1 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.cpp | 4 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.h | 1 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 106 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 18 | ||||
-rw-r--r-- | src/wallet/wallet2_api.h | 18 | ||||
-rw-r--r-- | src/wallet/wallet_args.cpp | 6 | ||||
-rw-r--r-- | src/wallet/wallet_errors.h | 4 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 108 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 26 |
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() }; |