mirror of https://github.com/oxen-io/oxen-core.git
Add non-blocking isRefreshing(); make height retrieval non-blocking
This commit is contained in:
parent
e09b23a143
commit
d8662ace7b
|
@ -163,7 +163,7 @@ uint64_t PendingTransactionImpl::amount() const
|
|||
result += dest.amount;
|
||||
}
|
||||
service_nodes::staking_components sc;
|
||||
uint64_t height = m_wallet.blockChainHeight(w);
|
||||
uint64_t height = m_wallet.blockChainHeight();
|
||||
std::optional<uint8_t> hf_version = m_wallet.hardForkVersion();
|
||||
if (hf_version)
|
||||
{
|
||||
|
|
|
@ -1085,19 +1085,16 @@ std::vector<std::pair<std::string, uint64_t>>* WalletImpl::listCurrentStakes() c
|
|||
return stakes;
|
||||
}
|
||||
|
||||
uint64_t WalletImpl::blockChainHeight(LockedWallet& w) {
|
||||
EXPORT
|
||||
uint64_t WalletImpl::blockChainHeight() const
|
||||
{
|
||||
// This call is thread-safe
|
||||
auto& w = m_wallet_ptr;
|
||||
if(w->light_wallet()) {
|
||||
return w->get_light_wallet_scanned_block_height();
|
||||
}
|
||||
return w->get_blockchain_current_height();
|
||||
}
|
||||
|
||||
EXPORT
|
||||
uint64_t WalletImpl::blockChainHeight() const
|
||||
{
|
||||
auto w = wallet();
|
||||
return blockChainHeight(w);
|
||||
}
|
||||
EXPORT
|
||||
uint64_t WalletImpl::approximateBlockChainHeight() const
|
||||
{
|
||||
|
@ -1113,7 +1110,10 @@ uint64_t WalletImpl::estimateBlockChainHeight() const
|
|||
EXPORT
|
||||
uint64_t WalletImpl::daemonBlockChainHeight() const
|
||||
{
|
||||
auto w = wallet();
|
||||
// I *think* the calls here are thread-safe, so we can do this without locking
|
||||
//auto w = wallet();
|
||||
auto& w = m_wallet_ptr;
|
||||
|
||||
if(w->light_wallet()) {
|
||||
return w->get_light_wallet_scanned_block_height();
|
||||
}
|
||||
|
@ -1134,7 +1134,10 @@ uint64_t WalletImpl::daemonBlockChainHeight() const
|
|||
EXPORT
|
||||
uint64_t WalletImpl::daemonBlockChainTargetHeight() const
|
||||
{
|
||||
auto w = wallet();
|
||||
// As above
|
||||
//auto w = wallet();
|
||||
auto& w = m_wallet_ptr;
|
||||
|
||||
if(w->light_wallet()) {
|
||||
return w->get_light_wallet_blockchain_height();
|
||||
}
|
||||
|
@ -1188,6 +1191,12 @@ void WalletImpl::refreshAsync()
|
|||
m_refreshCV.notify_one();
|
||||
}
|
||||
|
||||
EXPORT
|
||||
bool WalletImpl::isRefreshing(std::chrono::milliseconds max_wait) {
|
||||
std::unique_lock lock{m_refreshMutex2, std::defer_lock};
|
||||
return !lock.try_lock_for(max_wait);
|
||||
}
|
||||
|
||||
EXPORT
|
||||
bool WalletImpl::rescanBlockchain()
|
||||
{
|
||||
|
|
|
@ -49,13 +49,23 @@ class SubaddressImpl;
|
|||
class SubaddressAccountImpl;
|
||||
struct Wallet2CallbackImpl;
|
||||
|
||||
// Wrapper that holds a lock to prevent background refreshes, which kill things; provides ->
|
||||
// Wrapper that holds a lock to prevent background refreshes, which kill things; provides `->`
|
||||
// indirection into the tools::wallet2 instance.
|
||||
struct LockedWallet {
|
||||
std::unique_lock<std::mutex> refresh_lock;
|
||||
std::unique_lock<std::recursive_timed_mutex> refresh_lock;
|
||||
tools::wallet2* const wallet;
|
||||
LockedWallet(const std::unique_ptr<tools::wallet2>& w, std::mutex& refresh_mutex)
|
||||
// Constructs a wallet wrapper from a moved existing unique_lock which may be initially locked
|
||||
// or unlocked (if unlocked, it will be immediately locked).
|
||||
LockedWallet(const std::unique_ptr<tools::wallet2>& w, std::unique_lock<std::recursive_timed_mutex>&& lock)
|
||||
: refresh_lock{std::move(lock)}, wallet{w.get()} {
|
||||
if (!refresh_lock) refresh_lock.lock();
|
||||
}
|
||||
// Constructs a wallet wrapper from a wallet and the refresh mutex; locks the mutex immediately.
|
||||
LockedWallet(const std::unique_ptr<tools::wallet2>& w, std::recursive_timed_mutex& refresh_mutex)
|
||||
: refresh_lock{refresh_mutex}, wallet{w.get()} {}
|
||||
|
||||
// Returns the wallet2 pointer, to allow `w->whatever()` to call into wallet functions through
|
||||
// the locking wrapper.
|
||||
tools::wallet2* operator->() { return wallet; }
|
||||
};
|
||||
|
||||
|
@ -109,15 +119,17 @@ public:
|
|||
uint64_t balance(uint32_t accountIndex = 0) const override;
|
||||
uint64_t unlockedBalance(uint32_t accountIndex = 0) const override;
|
||||
std::vector<std::pair<std::string, uint64_t>>* listCurrentStakes() const override;
|
||||
static uint64_t blockChainHeight(LockedWallet& w);
|
||||
uint64_t blockChainHeight() const override;
|
||||
uint64_t approximateBlockChainHeight() const override;
|
||||
uint64_t estimateBlockChainHeight() const override;
|
||||
// Returns the current daemon height, either from the wallet's current cached value or (if the
|
||||
// cache is too old) via a request to the daemon.
|
||||
uint64_t daemonBlockChainHeight() const override;
|
||||
uint64_t daemonBlockChainTargetHeight() const override;
|
||||
bool synchronized() const override;
|
||||
bool refresh() override;
|
||||
void refreshAsync() override;
|
||||
bool isRefreshing(std::chrono::milliseconds max_wait = std::chrono::milliseconds{50}) override;
|
||||
bool rescanBlockchain() override;
|
||||
void rescanBlockchainAsync() override;
|
||||
void setAutoRefreshInterval(int millis) override;
|
||||
|
@ -259,7 +271,7 @@ private:
|
|||
std::mutex m_refreshMutex;
|
||||
|
||||
// synchronizing sync and async refresh
|
||||
mutable std::mutex m_refreshMutex2;
|
||||
mutable std::recursive_timed_mutex m_refreshMutex2;
|
||||
std::condition_variable m_refreshCV;
|
||||
std::thread m_refreshThread;
|
||||
std::thread m_longPollThread;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
@ -614,8 +615,8 @@ struct Wallet
|
|||
virtual bool watchOnly() const = 0;
|
||||
|
||||
/**
|
||||
* @brief blockChainHeight - returns current blockchain height
|
||||
* @return
|
||||
* @brief blockChainHeight - returns current blockchain height. This is thread-safe and will
|
||||
* not block if called from different threads.
|
||||
*/
|
||||
virtual uint64_t blockChainHeight() const = 0;
|
||||
|
||||
|
@ -632,7 +633,7 @@ struct Wallet
|
|||
**/
|
||||
virtual uint64_t estimateBlockChainHeight() const = 0;
|
||||
/**
|
||||
* @brief daemonBlockChainHeight - returns daemon blockchain height
|
||||
* @brief daemonBlockChainHeight - returns daemon blockchain height; thread-safe.
|
||||
* @return 0 - in case error communicating with the daemon.
|
||||
* status() will return Status_Error and a return verbose error description
|
||||
*/
|
||||
|
@ -690,6 +691,21 @@ struct Wallet
|
|||
*/
|
||||
virtual void refreshAsync() = 0;
|
||||
|
||||
/**
|
||||
* @brief refreshing - returns true if a refresh is currently underway; this is done *without*
|
||||
* requiring a lock, unlike most other wallet-interacting functions.
|
||||
*
|
||||
* @param max_wait - the maximum time to try to obtain the refresh thread lock. If this time
|
||||
* expires without acquiring a lock, we return true, otherwise we return false. Defaults to
|
||||
* 50ms; can be set to 0ms to always return immediately.
|
||||
*
|
||||
* @return - true if the refresh thread is currently active, false otherwise. If true, very few
|
||||
* other methods here will work (i.e. they will block until the refresh finishes). The most
|
||||
* notably non-blocking, thread-safe methods that can be used when this returns true are
|
||||
* blockChainHeight and daemonBlockChainHeight.
|
||||
*/
|
||||
virtual bool isRefreshing(std::chrono::milliseconds max_wait = std::chrono::milliseconds{50}) = 0;
|
||||
|
||||
/**
|
||||
* @brief rescanBlockchain - rescans the wallet, updating transactions from daemon
|
||||
* @return - true if refreshed successfully;
|
||||
|
|
|
@ -2645,6 +2645,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
|
|||
LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
|
||||
}
|
||||
m_blockchain.push_back(bl_id);
|
||||
m_cached_height++;
|
||||
|
||||
if (0 != m_callback)
|
||||
m_callback->on_new_block(height, b);
|
||||
|
@ -3311,6 +3312,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
|
|||
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
|
||||
m_blockchain.push_back(checkpoint_hash);
|
||||
m_blockchain.trim(checkpoint_height);
|
||||
m_cached_height = m_blockchain.size();
|
||||
short_chain_history.clear();
|
||||
get_short_chain_history(short_chain_history);
|
||||
}
|
||||
|
@ -3343,6 +3345,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
|
|||
if (!(current_index % 1024))
|
||||
LOG_PRINT_L2( "Skipped block by height: " << current_index);
|
||||
m_blockchain.push_back(bl_id);
|
||||
m_cached_height++;
|
||||
|
||||
if (0 != m_callback)
|
||||
{ // FIXME: this isn't right, but simplewallet just logs that we got a block.
|
||||
|
@ -3550,6 +3553,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
|||
generate_genesis(b);
|
||||
m_blockchain.clear();
|
||||
m_blockchain.push_back(get_block_hash(b));
|
||||
m_cached_height++;
|
||||
short_chain_history.clear();
|
||||
get_short_chain_history(short_chain_history);
|
||||
fast_refresh(stop_height, blocks_start_height, short_chain_history, true);
|
||||
|
@ -3557,6 +3561,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
|||
THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error, "Unexpected hashchain offset");
|
||||
for (const auto &h: tip)
|
||||
m_blockchain.push_back(h);
|
||||
m_cached_height = m_blockchain.size();
|
||||
short_chain_history.clear();
|
||||
get_short_chain_history(short_chain_history);
|
||||
start_height = stop_height;
|
||||
|
@ -3794,6 +3799,7 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui
|
|||
|
||||
size_t blocks_detached = m_blockchain.size() - height;
|
||||
m_blockchain.crop(height);
|
||||
m_cached_height = m_blockchain.size();
|
||||
|
||||
for (auto it = m_payments.begin(); it != m_payments.end(); )
|
||||
{
|
||||
|
@ -3825,6 +3831,7 @@ bool wallet2::deinit()
|
|||
bool wallet2::clear()
|
||||
{
|
||||
m_blockchain.clear();
|
||||
m_cached_height = m_blockchain.size();
|
||||
m_transfers.clear();
|
||||
m_key_images.clear();
|
||||
m_pub_keys.clear();
|
||||
|
@ -3861,6 +3868,7 @@ void wallet2::clear_soft(bool keep_key_images)
|
|||
cryptonote::block b;
|
||||
generate_genesis(b);
|
||||
m_blockchain.push_back(get_block_hash(b));
|
||||
m_cached_height = m_blockchain.size();
|
||||
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
|
||||
}
|
||||
|
||||
|
@ -4607,6 +4615,7 @@ void wallet2::setup_new_blockchain()
|
|||
cryptonote::block b;
|
||||
generate_genesis(b);
|
||||
m_blockchain.push_back(get_block_hash(b));
|
||||
m_cached_height = m_blockchain.size();
|
||||
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
|
||||
add_subaddress_account(tr("Primary account"));
|
||||
}
|
||||
|
@ -5792,6 +5801,7 @@ void wallet2::load(const fs::path& wallet_, const epee::wipeable_string& passwor
|
|||
{
|
||||
m_blockchain.push_back(genesis_hash);
|
||||
m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
|
||||
m_cached_height = m_blockchain.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5856,6 +5866,7 @@ void wallet2::trim_hashchain()
|
|||
MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset());
|
||||
m_blockchain.trim(height);
|
||||
}
|
||||
m_cached_height = m_blockchain.size();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
|
||||
|
@ -13771,36 +13782,36 @@ void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2
|
|||
}
|
||||
}
|
||||
|
||||
std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const
|
||||
std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> wallet2::export_blockchain() const
|
||||
{
|
||||
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();
|
||||
auto& [offset, genesis_hash, hashes] = bc;
|
||||
offset = m_blockchain.offset();
|
||||
genesis_hash = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
|
||||
for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
|
||||
{
|
||||
std::get<2>(bc).push_back(m_blockchain[n]);
|
||||
}
|
||||
hashes.push_back(m_blockchain[n]);
|
||||
return bc;
|
||||
}
|
||||
|
||||
void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc)
|
||||
{
|
||||
const auto& [offset, genesis_h, hashes] = bc;
|
||||
m_blockchain.clear();
|
||||
if (std::get<0>(bc))
|
||||
if (offset)
|
||||
{
|
||||
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 (size_t n = offset; n > 0; --n)
|
||||
m_blockchain.push_back(genesis_h);
|
||||
m_blockchain.trim(offset);
|
||||
}
|
||||
for (auto const &b : std::get<2>(bc))
|
||||
{
|
||||
for (auto const &b : hashes)
|
||||
m_blockchain.push_back(b);
|
||||
}
|
||||
|
||||
cryptonote::block genesis;
|
||||
generate_genesis(genesis);
|
||||
crypto::hash genesis_hash = get_block_hash(genesis);
|
||||
check_genesis(genesis_hash);
|
||||
m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
|
||||
m_cached_height = m_blockchain.size();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const
|
||||
|
|
|
@ -835,7 +835,9 @@ private:
|
|||
|
||||
std::unordered_map<std::string, ons_detail> get_ons_cache();
|
||||
|
||||
uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); }
|
||||
// Returns the current height up to which the wallet has synchronized the blockchain. Thread
|
||||
// safe (though the value may be behind if another thread is in the middle of adding blocks).
|
||||
uint64_t get_blockchain_current_height() const { return m_cached_height; }
|
||||
void rescan_spent();
|
||||
void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false);
|
||||
bool is_transfer_unlocked(const transfer_details &td) const;
|
||||
|
@ -864,6 +866,7 @@ private:
|
|||
{
|
||||
a & m_blockchain;
|
||||
}
|
||||
m_cached_height = m_blockchain.size();
|
||||
a & m_transfers;
|
||||
a & m_account_public_address;
|
||||
a & m_key_images;
|
||||
|
@ -1575,6 +1578,7 @@ private:
|
|||
fs::path m_keys_file;
|
||||
fs::path m_mms_file;
|
||||
hashchain m_blockchain;
|
||||
std::atomic<uint64_t> m_cached_height; // Tracks m_blockchain.size(), but thread-safe.
|
||||
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, pool_payment_details> m_unconfirmed_payments;
|
||||
|
|
Loading…
Reference in New Issue