mirror of
https://github.com/wownero/lwsf-wow.git
synced 2026-10-17 15:31:32 +02:00
Fixed several bugs
This commit is contained in:
+33
-12
@@ -189,7 +189,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
read_bytes(source, *dest.second);
|
||||
dest.first = dest.second->id;
|
||||
}
|
||||
static void write_bytes(wire::writer& dest, const std::pair<crypto::hash, std::shared_ptr<transaction>>& source)
|
||||
static void write_bytes(wire::writer& dest, const std::pair<const crypto::hash, std::shared_ptr<transaction>>& source)
|
||||
{
|
||||
if (!source.second)
|
||||
WIRE_DLOG_THROW(wire::error::schema::object, "Unexpected nullptr");
|
||||
@@ -364,11 +364,13 @@ namespace lwsf { namespace internal { namespace backend
|
||||
`refresh()` method; the wallet is never in a partial-state. Swapping
|
||||
the transactions at the end helps with this guarantee. */
|
||||
|
||||
boost::container::flat_map<crypto::hash, std::shared_ptr<transaction>, memory> updated_txes;
|
||||
std::unordered_map<crypto::hash, std::shared_ptr<transaction>> updated_txes;
|
||||
updated_txes.reserve(
|
||||
std::max(self.primary.txes.size(), source.transactions.size())
|
||||
);
|
||||
|
||||
std::unordered_multimap<crypto::key_image, std::shared_ptr<transaction>> images;
|
||||
|
||||
/* The frontend will know about the spend first, iff the frontend was
|
||||
used to perform the spend. We copy _all_ transactions that have a
|
||||
spend secret, even if the backend doesn't acknowledge it, otherwise the
|
||||
@@ -382,10 +384,17 @@ namespace lwsf { namespace internal { namespace backend
|
||||
{
|
||||
if (spend.second.secret)
|
||||
{
|
||||
updated_txes.emplace_hint(
|
||||
updated_txes.end(), tx.first, std::make_shared<transaction>(*tx.second)
|
||||
const auto iter = updated_txes.emplace_hint(
|
||||
updated_txes.end(), tx.first, nullptr
|
||||
);
|
||||
break; // inner loop
|
||||
if (!iter->second)
|
||||
{
|
||||
iter->second = std::make_shared<transaction>(*tx.second);
|
||||
iter->second->height = std::nullopt;
|
||||
iter->second->timestamp = std::nullopt;
|
||||
iter->second->failed = false;
|
||||
}
|
||||
images.emplace(spend.first, iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,7 +414,15 @@ namespace lwsf { namespace internal { namespace backend
|
||||
if (existing == self.primary.txes.end())
|
||||
out.push_back(inserted.first->second);
|
||||
}
|
||||
if (!update_tx(self.primary, *inserted.first->second, tx))
|
||||
if (update_tx(self.primary, *inserted.first->second, tx))
|
||||
{
|
||||
for (const auto& spend : inserted.first->second->spends)
|
||||
{
|
||||
for (auto range = images.equal_range(spend.first); range.first != range.second; ++range.first)
|
||||
range.first->second->failed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
updated_txes.erase(tx.hash);
|
||||
}
|
||||
|
||||
@@ -425,7 +442,6 @@ namespace lwsf { namespace internal { namespace backend
|
||||
|
||||
self.per_byte_fee = unspents.per_byte_fee;
|
||||
self.fee_mask = unspents.fee_mask;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -492,8 +508,11 @@ namespace lwsf { namespace internal { namespace backend
|
||||
primary{},
|
||||
last_sync(0),
|
||||
blockchain_height(0),
|
||||
per_byte_fee(0),
|
||||
fee_mask(0),
|
||||
sync()
|
||||
sync(),
|
||||
sync_listener(),
|
||||
sync_refresh()
|
||||
{
|
||||
primary.subaccounts[0].minor[0];
|
||||
}
|
||||
@@ -592,12 +611,13 @@ namespace lwsf { namespace internal { namespace backend
|
||||
const std::chrono::steady_clock::time_point start{
|
||||
std::chrono::steady_clock::time_point::duration{last_sync.load()}
|
||||
};
|
||||
if (now - start < config::refresh_interval)
|
||||
if (now - start < config::refresh_interval_min)
|
||||
return {};
|
||||
}
|
||||
|
||||
expect<rpc::get_unspent_outs_response> outs_response{common_error::kInvalidArgument};
|
||||
|
||||
boost::unique_lock<boost::mutex> lock_refresh{sync_refresh};
|
||||
boost::unique_lock<boost::mutex> lock{sync};
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (!mandatory) // double-check
|
||||
@@ -605,10 +625,9 @@ namespace lwsf { namespace internal { namespace backend
|
||||
const std::chrono::steady_clock::time_point start{
|
||||
std::chrono::steady_clock::time_point::duration{last_sync.load()}
|
||||
};
|
||||
if (now - start < config::refresh_interval)
|
||||
if (now - start < config::refresh_interval_min)
|
||||
return {};
|
||||
}
|
||||
last_sync = now.time_since_epoch().count();
|
||||
|
||||
const rpc::login login{primary.address, primary.view.sec};
|
||||
lock.unlock();
|
||||
@@ -625,10 +644,11 @@ namespace lwsf { namespace internal { namespace backend
|
||||
return outs_response.error();
|
||||
|
||||
lock.lock();
|
||||
last_sync = now.time_since_epoch().count();
|
||||
const std::uint64_t orig_scan_height = primary.scan_height;
|
||||
const auto new_txes = merge_response(*this, *txs_response, *outs_response);
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock2{sync_listener};
|
||||
const boost::lock_guard<boost::mutex> lock_listener{sync_listener};
|
||||
if (!listener)
|
||||
return {};
|
||||
|
||||
@@ -637,6 +657,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
const std::uint64_t new_scan_height =
|
||||
std::max(orig_scan_height, primary.scan_height);
|
||||
lock.unlock();
|
||||
lock_refresh.unlock();
|
||||
|
||||
listener->refreshed();
|
||||
if (!new_txes.empty() || new_scan_height - orig_scan_height)
|
||||
|
||||
+7
-3
@@ -38,6 +38,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
@@ -135,7 +136,8 @@ namespace lwsf { namespace internal { namespace backend
|
||||
payment_id(),
|
||||
id{},
|
||||
prefix{},
|
||||
coinbase(false)
|
||||
coinbase(false),
|
||||
failed(false)
|
||||
{}
|
||||
|
||||
transaction(transaction&&) = default;
|
||||
@@ -161,6 +163,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
crypto::hash id;
|
||||
crypto::hash prefix;
|
||||
bool coinbase;
|
||||
bool failed;
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(transaction);
|
||||
|
||||
@@ -180,8 +183,8 @@ namespace lwsf { namespace internal { namespace backend
|
||||
std::string language;
|
||||
std::vector<address_book_entry> addressbook;
|
||||
std::map<std::uint32_t, sub_account> subaccounts;
|
||||
boost::container::flat_map<crypto::hash, std::shared_ptr<transaction>, memory> txes;
|
||||
std::map<std::string, std::string> attributes;
|
||||
std::unordered_map<crypto::hash, std::shared_ptr<transaction>> txes;
|
||||
std::unordered_map<std::string, std::string> attributes;
|
||||
std::uint64_t scan_height;
|
||||
std::uint64_t restore_height;
|
||||
Monero::NetworkType type;
|
||||
@@ -202,6 +205,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
std::uint64_t fee_mask;
|
||||
mutable boost::mutex sync;
|
||||
boost::mutex sync_listener;
|
||||
boost::mutex sync_refresh;
|
||||
|
||||
wallet();
|
||||
|
||||
|
||||
+5
-1
@@ -190,6 +190,7 @@ namespace
|
||||
|
||||
if (!prog.lws_url.empty())
|
||||
{
|
||||
wallet->setAutoRefreshInterval(0); // do not start background thread
|
||||
if (!wallet->init(prog.lws_url, 0, "", "", (prog.lws_url.substr(0, 8) == "https://"), true) || !wallet->refresh())
|
||||
throw std::runtime_error{"Failed to refresh wallet: " + wallet->errorString()};
|
||||
}
|
||||
@@ -204,7 +205,7 @@ namespace
|
||||
std::cout << "; Blockchain Height: " << wallet->daemonBlockChainHeight() << std::endl;
|
||||
std::cout << "; Start Height: " << wallet->getRefreshFromBlockHeight() << std::endl;
|
||||
std::cout << "; Scan Height: " << wallet->blockChainHeight() << std::endl;
|
||||
std::cout << "; Accounts: " << wallet->numSubaddressAccounts() << std::endl << std::endl;
|
||||
std::cout << "; Accounts: " << wallet->numSubaddressAccounts() << std::endl;
|
||||
|
||||
Monero::TransactionHistory* history = wallet->history();
|
||||
if (!history)
|
||||
@@ -212,6 +213,9 @@ namespace
|
||||
|
||||
history->refresh();
|
||||
const std::vector<Monero::TransactionInfo*>& info_list = history->getAll();
|
||||
|
||||
std::cout << "; Transactions: " << info_list.size() << std::endl << std::endl;
|
||||
|
||||
for (const Monero::TransactionInfo* info : info_list)
|
||||
{
|
||||
if (!info)
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace lwsf { namespace config
|
||||
constexpr const std::size_t max_txes_in_rpc = 2048;
|
||||
constexpr const std::uint32_t mixin_default = 16;
|
||||
constexpr const std::chrono::seconds refresh_interval{30};
|
||||
constexpr const std::chrono::seconds refresh_interval_min{5};
|
||||
constexpr const std::chrono::seconds rpc_timeout{5};
|
||||
constexpr const unsigned subaddr_retry{10};
|
||||
}} // lwsf // config
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace lwsf { namespace internal
|
||||
|
||||
virtual int direction() const override { return data_->direction; }
|
||||
virtual bool isPending() const override { return !data_->height; }
|
||||
virtual bool isFailed() const override { return false; }
|
||||
virtual bool isFailed() const override { return data_->failed; }
|
||||
virtual bool isCoinbase() const override { return data_->coinbase; }
|
||||
virtual std::uint64_t amount() const override { return data_->amount; }
|
||||
virtual std::uint64_t fee() const override { return data_->fee; }
|
||||
|
||||
+51
-26
@@ -359,8 +359,9 @@ namespace lwsf { namespace internal
|
||||
|
||||
void wallet::stop_refresh_loop()
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{thread_sync_};
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
||||
const boost::lock_guard<boost::mutex> lock2{refresh_sync_};
|
||||
thread_state_ = state::stop;
|
||||
}
|
||||
refresh_notify_.notify_all();
|
||||
@@ -370,10 +371,19 @@ namespace lwsf { namespace internal
|
||||
|
||||
void wallet::refresh_loop()
|
||||
{
|
||||
struct set_stop_
|
||||
{
|
||||
void operator()(state* val) const noexcept
|
||||
{
|
||||
if (val)
|
||||
*val = state::stop;
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock{refresh_sync_};
|
||||
while (thread_state_ != state::stop)
|
||||
const std::unique_ptr<state, set_stop_> set_stop{std::addressof(thread_state_)};
|
||||
while (mandatory_refresh_ || thread_state_ != state::stop)
|
||||
{
|
||||
const bool mandatory_refresh = mandatory_refresh_;
|
||||
mandatory_refresh_ = false;
|
||||
@@ -464,6 +474,7 @@ namespace lwsf { namespace internal
|
||||
refresh_notify_(),
|
||||
error_sync_(),
|
||||
refresh_sync_(),
|
||||
thread_sync_(),
|
||||
thread_state_(state::stop),
|
||||
mandatory_refresh_(false)
|
||||
{
|
||||
@@ -494,6 +505,7 @@ namespace lwsf { namespace internal
|
||||
refresh_notify_(),
|
||||
error_sync_(),
|
||||
refresh_sync_(),
|
||||
thread_sync_(),
|
||||
thread_state_(state::stop),
|
||||
mandatory_refresh_(false)
|
||||
{
|
||||
@@ -552,6 +564,7 @@ namespace lwsf { namespace internal
|
||||
refresh_notify_(),
|
||||
error_sync_(),
|
||||
refresh_sync_(),
|
||||
thread_sync_(),
|
||||
thread_state_(state::stop),
|
||||
mandatory_refresh_(false)
|
||||
{
|
||||
@@ -711,16 +724,12 @@ namespace lwsf { namespace internal
|
||||
const std::filesystem::path directory =
|
||||
std::filesystem::path{real_path}.remove_filename();
|
||||
|
||||
if (std::filesystem::exists(new_file, status))
|
||||
std::filesystem::remove(file, status);
|
||||
std::filesystem::rename(new_file, file, status);
|
||||
|
||||
if (std::filesystem::exists(new_file))
|
||||
std::filesystem::rename(new_file, file, status);
|
||||
|
||||
// blocks until file and directory contents are synced
|
||||
if (atomic_file_write(new_file, directory, epee::byte_slice{std::move(buffer)}))
|
||||
{
|
||||
std::filesystem::remove(file, status);
|
||||
std::filesystem::rename(new_file, file, status);
|
||||
}
|
||||
else
|
||||
status = error::write_failure;
|
||||
}
|
||||
@@ -786,8 +795,7 @@ namespace lwsf { namespace internal
|
||||
if (!connectToDaemon())
|
||||
return false;
|
||||
|
||||
thread_state_ = state::run;
|
||||
thread_ = boost::thread{[this] () { this->refresh_loop(); }};
|
||||
startRefresh();
|
||||
rollback.release();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -930,9 +938,26 @@ namespace lwsf { namespace internal
|
||||
|
||||
void wallet::startRefresh()
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{thread_sync_};
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
||||
thread_state_ = state::run;
|
||||
const boost::lock_guard<boost::mutex> lock2{refresh_sync_};
|
||||
const bool no_refresh =
|
||||
refresh_interval_ <= std::chrono::milliseconds{0};
|
||||
if (no_refresh)
|
||||
{
|
||||
thread_state_ = state::stop;
|
||||
if (!mandatory_refresh_)
|
||||
return;
|
||||
}
|
||||
|
||||
const state old_state = thread_state_;
|
||||
thread_state_ = no_refresh ? state::stop : state::run;
|
||||
if (old_state == state::stop)
|
||||
{
|
||||
if (thread_.joinable())
|
||||
thread_.join();
|
||||
thread_ = boost::thread{[this] () { this->refresh_loop(); }};
|
||||
}
|
||||
}
|
||||
refresh_notify_.notify_all();
|
||||
}
|
||||
@@ -940,12 +965,15 @@ namespace lwsf { namespace internal
|
||||
void wallet::pauseRefresh()
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
||||
thread_state_ = state::paused;
|
||||
if (thread_state_ != state::stop)
|
||||
thread_state_ = state::paused;
|
||||
}
|
||||
|
||||
bool wallet::refresh()
|
||||
{
|
||||
return set_error(data_->refresh(false));
|
||||
try { return set_error(data_->refresh(true)); }
|
||||
catch (const std::exception& e) { set_critical(e); }
|
||||
return false;
|
||||
}
|
||||
|
||||
void wallet::refreshAsync()
|
||||
@@ -954,25 +982,22 @@ namespace lwsf { namespace internal
|
||||
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
||||
mandatory_refresh_ = true;
|
||||
}
|
||||
refresh_notify_.notify_all();
|
||||
startRefresh();
|
||||
}
|
||||
|
||||
void wallet::setAutoRefreshInterval(int millis)
|
||||
{
|
||||
using rep_type = std::chrono::milliseconds::rep;
|
||||
static_assert(std::numeric_limits<int>::max() <= std::numeric_limits<rep_type>::max());
|
||||
if (0 < millis)
|
||||
|
||||
boost::unique_lock<boost::mutex> lock{refresh_sync_};
|
||||
refresh_interval_ = std::chrono::milliseconds{millis};
|
||||
if (millis <= 0)
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
||||
refresh_interval_ = std::chrono::milliseconds{millis};
|
||||
if (thread_state_ == state::stop)
|
||||
{
|
||||
thread_state_ = state::run;
|
||||
thread_ = boost::thread{[this] () { this->refresh_loop(); }};
|
||||
}
|
||||
thread_state_ = state::stop;
|
||||
lock.unlock();
|
||||
stop_refresh_loop();
|
||||
}
|
||||
else
|
||||
return stop_refresh_loop();
|
||||
}
|
||||
|
||||
int wallet::autoRefreshInterval() const
|
||||
|
||||
+2
-1
@@ -66,7 +66,8 @@ namespace internal
|
||||
std::uint32_t mixin_;
|
||||
boost::condition_variable refresh_notify_;
|
||||
mutable boost::mutex error_sync_;
|
||||
boost::mutex refresh_sync_;
|
||||
boost::mutex refresh_sync_; //!< Synchronizes with requests from other threads
|
||||
boost::mutex thread_sync_; //!< Syncrhonizes thread destruction+creation
|
||||
state thread_state_;
|
||||
bool mandatory_refresh_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user