mirror of
https://codeberg.org/wownero/lwsf-wow.git
synced 2026-10-17 05:41:30 +02:00
Complete subaddresses and restore height. Fix error reporting
This commit is contained in:
+235
-104
@@ -31,8 +31,10 @@
|
|||||||
#include <boost/thread/lock_guard.hpp>
|
#include <boost/thread/lock_guard.hpp>
|
||||||
#include "crypto/crypto.h" // monero/src
|
#include "crypto/crypto.h" // monero/src
|
||||||
#include "crypto/crypto-ops.h" // monero/src
|
#include "crypto/crypto-ops.h" // monero/src
|
||||||
#include "cryptonote_basic/cryptonote_basic_impl.h" // monero/src
|
#include "cryptonote_basic/cryptonote_basic_impl.h" // monero/src
|
||||||
|
#include "cryptonote_basic/cryptonote_format_utils.h" // monero/src
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "hex.h" // monero/src
|
||||||
#include "lwsf_config.h"
|
#include "lwsf_config.h"
|
||||||
#include "ringct/rctOps.h"
|
#include "ringct/rctOps.h"
|
||||||
#include "wire.h"
|
#include "wire.h"
|
||||||
@@ -49,6 +51,26 @@ namespace Monero
|
|||||||
WIRE_AS_INTEGER(Monero::NetworkType);
|
WIRE_AS_INTEGER(Monero::NetworkType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace lwsf { namespace config
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template<typename F, typename T>
|
||||||
|
void map_lookahead(F& format, T& self)
|
||||||
|
{
|
||||||
|
wire::object(format, WIRE_FIELD(major), WIRE_FIELD(minor));
|
||||||
|
}
|
||||||
|
} // anonymous
|
||||||
|
WIRE_DEFINE_OBJECT(lookahead, map_lookahead);
|
||||||
|
|
||||||
|
constexpr bool operator==(const lookahead& lhs, const lookahead& rhs) noexcept
|
||||||
|
{ return lhs.major == rhs.major && lhs.minor == rhs.minor; }
|
||||||
|
|
||||||
|
constexpr bool operator!=(const lookahead& lhs, const lookahead& rhs) noexcept
|
||||||
|
{ return lhs.major != rhs.major || lhs.minor != rhs.minor; }
|
||||||
|
|
||||||
|
}} // lwsf // config
|
||||||
|
|
||||||
namespace lwsf { namespace internal { namespace backend
|
namespace lwsf { namespace internal { namespace backend
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -125,6 +147,12 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \return If user requested {0,0} lookahead with no known subaddresses
|
||||||
|
bool no_subaddresses(const epee::span<const sub_account> subaccounts, const config::lookahead lookahead) noexcept
|
||||||
|
{
|
||||||
|
return !lookahead.major && !lookahead.minor && subaccounts.size() == 1 && !subaccounts[0].last;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename F, typename T>
|
template<typename F, typename T>
|
||||||
void map_address_book_entry(F& format, T& self)
|
void map_address_book_entry(F& format, T& self)
|
||||||
{
|
{
|
||||||
@@ -240,12 +268,11 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
wire::optional_field("attributes", wire::trusted_array(std::ref(self.attributes))),
|
wire::optional_field("attributes", wire::trusted_array(std::ref(self.attributes))),
|
||||||
WIRE_FIELD(scan_height),
|
WIRE_FIELD(scan_height),
|
||||||
WIRE_FIELD(restore_height),
|
WIRE_FIELD(restore_height),
|
||||||
WIRE_FIELD_DEFAULTED(maj_lookahead, config::subaddr_major_lookahead),
|
WIRE_FIELD_DEFAULTED(lookahead, config::default_lookahead),
|
||||||
WIRE_FIELD_DEFAULTED(min_lookahead, config::subaddr_minor_lookahead),
|
WIRE_FIELD_DEFAULTED(type, Monero::MAINNET),
|
||||||
WIRE_FIELD(type),
|
|
||||||
WIRE_FIELD(view),
|
WIRE_FIELD(view),
|
||||||
WIRE_FIELD(spend),
|
WIRE_FIELD(spend),
|
||||||
WIRE_FIELD(generated_locally)
|
WIRE_FIELD_DEFAULTED(generated_locally, true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,20 +467,27 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
{
|
{
|
||||||
if (tx.second)
|
if (tx.second)
|
||||||
{
|
{
|
||||||
if (!tx.second->transfers.empty())
|
const bool rescanning =
|
||||||
|
source.scanned_block_height < tx.second->height.value_or(0);
|
||||||
|
if (rescanning || !tx.second->description.empty() || !tx.second->transfers.empty())
|
||||||
{
|
{
|
||||||
const auto iter = updated_txes.emplace_hint(
|
const auto iter = updated_txes.emplace_hint(
|
||||||
updated_txes.end(), tx.first, nullptr
|
updated_txes.end(), tx.first, nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!iter->second)
|
if (!iter->second)
|
||||||
{
|
{
|
||||||
iter->second = std::make_shared<transaction>(*tx.second);
|
iter->second = std::make_shared<transaction>(*tx.second);
|
||||||
iter->second->height = std::nullopt;
|
for (const auto& spend : tx.second->spends)
|
||||||
iter->second->timestamp = std::nullopt;
|
images.emplace(spend.first, iter->second);
|
||||||
iter->second->failed = false;
|
|
||||||
|
if (!rescanning)
|
||||||
|
{
|
||||||
|
iter->second->height = std::nullopt;
|
||||||
|
iter->second->timestamp = std::nullopt;
|
||||||
|
iter->second->failed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const auto& spend : tx.second->spends)
|
|
||||||
images.emplace(spend.first, iter->second);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,7 +514,10 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
for (const auto& spend : inserted.first->second->spends)
|
for (const auto& spend : inserted.first->second->spends)
|
||||||
{
|
{
|
||||||
for (auto range = images.equal_range(spend.first); range.first != range.second; ++range.first)
|
for (auto range = images.equal_range(spend.first); range.first != range.second; ++range.first)
|
||||||
range.first->second->failed = true;
|
{
|
||||||
|
if (inserted.first->second->id != range.first->second->id)
|
||||||
|
range.first->second->failed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -529,39 +566,54 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
major lookahead is set to error state if `false` is returned, and set to
|
major lookahead is set to error state if `false` is returned, and set to
|
||||||
the server lookahead iff true is returned.
|
the server lookahead iff true is returned.
|
||||||
\return True iff the server has enough lookahead for all fields. */
|
\return True iff the server has enough lookahead for all fields. */
|
||||||
bool update_lookaheads(wallet& self, const boost::container::flat_set<rpc::subaddrs, std::less<>>& all)
|
bool update_lookaheads(wallet& self, const boost::container::flat_set<rpc::subaddrs, std::less<>>& all) noexcept
|
||||||
{
|
{
|
||||||
const auto maj_lookahead = self.primary.maj_lookahead;
|
self.server_lookahead = 0;
|
||||||
const auto min_lookahead = self.primary.min_lookahead;
|
self.lookahead_error = {};
|
||||||
if (!maj_lookahead && !min_lookahead)
|
|
||||||
throw std::logic_error{"update_lookaheads does not work with lookahead {0, 0}"};
|
const auto lookahead = self.primary.lookahead;
|
||||||
|
if (no_subaddresses(epee::to_span(self.primary.subaccounts), lookahead))
|
||||||
|
return true;
|
||||||
|
|
||||||
self.server_lookahead = {error::subaddr_server};
|
|
||||||
static_assert(std::is_same<std::uint32_t, decltype(all.rbegin()->key)>());
|
static_assert(std::is_same<std::uint32_t, decltype(all.rbegin()->key)>());
|
||||||
const std::uint32_t server_lookahead = all.empty() ?
|
const std::uint32_t server_lookahead = all.empty() ?
|
||||||
std::uint32_t(0) : all.rbegin()->key;
|
std::uint32_t(0) : all.rbegin()->key;
|
||||||
|
|
||||||
// Ensure major lookahead starts at zero, has no gaps, and "far" enough.
|
// Ensure major lookahead starts at zero, has no gaps, and "far" enough.
|
||||||
const std::size_t last_account = std::max(self.primary.subaccounts.size(), std::size_t(1)) - 1;
|
const std::size_t last_account = std::max(self.primary.subaccounts.size(), std::size_t(1)) - 1;
|
||||||
bool min_met = add_uint32_clamp(last_account, maj_lookahead) <= server_lookahead;
|
bool min_met = add_uint32_clamp(last_account, lookahead.major) <= server_lookahead;
|
||||||
min_met &= all.size() == std::uint64_t(server_lookahead) + 1;
|
min_met &= all.size() == std::uint64_t(server_lookahead) + 1;
|
||||||
|
|
||||||
|
// track the min lookahead we have
|
||||||
|
if (!min_met)
|
||||||
|
{
|
||||||
|
std::uint32_t last = -1;
|
||||||
|
for (const rpc::subaddrs& e : all)
|
||||||
|
{
|
||||||
|
if (last + 1 == e.key)
|
||||||
|
self.server_lookahead = ++last;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self.server_lookahead = server_lookahead;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < self.primary.subaccounts.size(); ++i)
|
for (std::size_t i = 0; i < self.primary.subaccounts.size(); ++i)
|
||||||
{
|
{
|
||||||
auto& acct = self.primary.subaccounts[i];
|
auto& acct = self.primary.subaccounts[i];
|
||||||
const auto elem = all.find(i);
|
const auto elem = all.find(i);
|
||||||
const bool has_range = elem != all.end();
|
const bool has_range = elem != all.end();
|
||||||
|
|
||||||
const std::uint32_t lookahead = has_range ? std::get<1>(elem->head) : 0;
|
const std::uint32_t this_lookahead = has_range ? std::get<1>(elem->head) : 0;
|
||||||
min_met &= add_uint32_clamp(acct.last, min_lookahead) <= lookahead;
|
min_met &= add_uint32_clamp(acct.last, lookahead.minor) <= this_lookahead;
|
||||||
min_met &= has_range && std::get<0>(elem->head) == 0;
|
min_met &= !lookahead.minor || (has_range && std::get<0>(elem->head) == 0);
|
||||||
|
|
||||||
// Only increase lookahead, server has no delete subaddr command
|
acct.server_lookahead = this_lookahead;
|
||||||
acct.server_lookahead = std::max(acct.server_lookahead, lookahead);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that unused minor lookahead is far enough
|
// Ensure that unused minor lookahead is far enough
|
||||||
const std::uint32_t last_minor = std::max(min_lookahead, std::uint32_t(1)) - 1;
|
const std::uint32_t last_minor = std::max(lookahead.minor, std::uint32_t(1)) - 1;
|
||||||
for (std::size_t i = self.primary.subaccounts.size(); i < all.size(); ++i)
|
for (std::size_t i = self.primary.subaccounts.size(); i < all.size(); ++i)
|
||||||
{
|
{
|
||||||
if (!min_met)
|
if (!min_met)
|
||||||
@@ -572,8 +624,8 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
min_met &= std::get<0>(range) == 0;
|
min_met &= std::get<0>(range) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_met)
|
if (!min_met)
|
||||||
self.server_lookahead = server_lookahead;
|
self.lookahead_error = {error::subaddr_ahead};
|
||||||
return min_met;
|
return min_met;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,14 +637,16 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
// remember upsert_subaddrs is inclusive in the minor ranges
|
// remember upsert_subaddrs is inclusive in the minor ranges
|
||||||
|
|
||||||
const auto& accts = self.primary.subaccounts;
|
const auto& accts = self.primary.subaccounts;
|
||||||
|
const std::size_t end = accts.size() + self.primary.lookahead.major;
|
||||||
|
out.reserve(end);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < accts.size() && i <= max_index; ++i)
|
for (std::size_t i = 0; i < accts.size() && i <= max_index; ++i)
|
||||||
out.insert(out.end(), rpc::subaddrs{std::uint32_t(i)})->head = {0, add_uint32_clamp(accts[i].last, self.primary.min_lookahead)};
|
out.insert(out.end(), rpc::subaddrs{std::uint32_t(i)})->head = {0, add_uint32_clamp(accts[i].last, self.primary.lookahead.major)};
|
||||||
|
|
||||||
/* `0 < maj_lookahead & min_lookahead == 0` is a strange edge case.
|
/* `0 < lookahead.major & lookahead.minor == 0` is a strange edge case.
|
||||||
Assume `min_lookahead == 1` in that scenario. */
|
Assume `lookahead.minor == 1` in that scenario. */
|
||||||
|
|
||||||
const std::uint32_t minor = std::max(std::uint32_t(1), self.primary.min_lookahead) - 1;
|
const std::uint32_t minor = std::max(std::uint32_t(1), self.primary.lookahead.minor) - 1;
|
||||||
const std::size_t end = accts.size() + self.primary.maj_lookahead;
|
|
||||||
for (std::size_t i = accts.size(); i < end && i <= max_index; ++i)
|
for (std::size_t i = accts.size(); i < end && i <= max_index; ++i)
|
||||||
out.insert(out.end(), rpc::subaddrs{std::uint32_t(i)})->head = {0, minor};
|
out.insert(out.end(), rpc::subaddrs{std::uint32_t(i)})->head = {0, minor};
|
||||||
}
|
}
|
||||||
@@ -600,7 +654,7 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
std::error_code handle_lookahead_error(std::error_code error) noexcept
|
std::error_code handle_lookahead_error(std::error_code error) noexcept
|
||||||
{
|
{
|
||||||
if (error == rpc_max_subaddresses)
|
if (error == rpc_max_subaddresses)
|
||||||
error = {error::subaddr_server};
|
error = {error::subaddr_ahead};
|
||||||
else if (error == rpc_not_implemented)
|
else if (error == rpc_not_implemented)
|
||||||
error = {error::subaddr_disabled};
|
error = {error::subaddr_disabled};
|
||||||
else if (error == wire::error::schema::array_max_element)
|
else if (error == wire::error::schema::array_max_element)
|
||||||
@@ -644,6 +698,9 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
|
|
||||||
std::string_view sub_account::sub_label(const std::uint32_t minor) const noexcept
|
std::string_view sub_account::sub_label(const std::uint32_t minor) const noexcept
|
||||||
{
|
{
|
||||||
|
if (!minor)
|
||||||
|
return primary_label();
|
||||||
|
|
||||||
const auto elem = detail.find(minor);
|
const auto elem = detail.find(minor);
|
||||||
if (elem != detail.end())
|
if (elem != detail.end())
|
||||||
return elem->second.label;
|
return elem->second.label;
|
||||||
@@ -692,12 +749,13 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
: listener(nullptr),
|
: listener(nullptr),
|
||||||
client(),
|
client(),
|
||||||
primary{},
|
primary{},
|
||||||
refresh_status(),
|
refresh_error(),
|
||||||
server_lookahead(default_subaddr_state),
|
lookahead_error(default_subaddr_state),
|
||||||
last_sync(),
|
last_sync(),
|
||||||
blockchain_height(0),
|
blockchain_height(0),
|
||||||
per_byte_fee(0),
|
per_byte_fee(0),
|
||||||
fee_mask(0),
|
fee_mask(0),
|
||||||
|
server_lookahead(0),
|
||||||
sync(),
|
sync(),
|
||||||
sync_listener(),
|
sync_listener(),
|
||||||
sync_refresh(),
|
sync_refresh(),
|
||||||
@@ -771,7 +829,9 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
// Remember that this function provides the strong exception guarantee.
|
// Remember that this function provides the strong exception guarantee.
|
||||||
boost::unique_lock<boost::mutex> lock{sync};
|
boost::unique_lock<boost::mutex> lock{sync};
|
||||||
{
|
{
|
||||||
server_lookahead = {default_subaddr_state};
|
passed_login = false;
|
||||||
|
server_lookahead = 0;
|
||||||
|
lookahead_error = {error::subaddr_disabled};
|
||||||
const rpc::login_request login{
|
const rpc::login_request login{
|
||||||
primary.address, primary.view.sec, true, primary.generated_locally
|
primary.address, primary.view.sec, true, primary.generated_locally
|
||||||
};
|
};
|
||||||
@@ -794,9 +854,9 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
if (response->start_height)
|
if (response->start_height)
|
||||||
primary.restore_height = *response->start_height;
|
primary.restore_height = *response->start_height;
|
||||||
|
|
||||||
if (!primary.maj_lookahead && !primary.min_lookahead)
|
if (no_subaddresses(epee::to_span(primary.subaccounts), primary.lookahead))
|
||||||
{
|
{
|
||||||
server_lookahead = 0;
|
lookahead_error = {};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -814,26 +874,20 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lookaheads not far enough
|
// lookaheads not far enough
|
||||||
rpc::upsert_subaddrs_request request{std::move(login), {}, false /* get_all */};
|
rpc::upsert_subaddrs_request request{std::move(login), {}, true /* get_all */};
|
||||||
fill_upsert(*this, request.subaddrs_);
|
fill_upsert(*this, request.subaddrs_);
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
const auto response = rpc::invoke<rpc::upsert_subaddrs_response>(client, request).value();
|
const auto response = rpc::invoke<rpc::upsert_subaddrs_response>(client, request).value();
|
||||||
lock.lock();
|
lock.lock();
|
||||||
update_lookaheads(*this, request.subaddrs_);
|
update_lookaheads(*this, response.all_subaddrs);
|
||||||
}
|
}
|
||||||
catch (const std::system_error& e)
|
catch (const std::system_error& e)
|
||||||
{
|
{
|
||||||
if (e.code() == rpc_max_subaddresses)
|
if (e.code() == rpc_unapproved) // happens on new account creation
|
||||||
server_lookahead = {error::subaddr_server};
|
|
||||||
else if (e.code() == rpc_not_implemented)
|
|
||||||
server_lookahead = {error::subaddr_disabled};
|
|
||||||
else if (e.code() == rpc_unapproved) // happens on new account creation
|
|
||||||
return {error::approval};
|
return {error::approval};
|
||||||
else if (e.code() == wire::error::schema::array_max_element)
|
|
||||||
server_lookahead = {error::subaddr_local};
|
|
||||||
else
|
else
|
||||||
server_lookahead = e.code();
|
lookahead_error = handle_lookahead_error(e.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -849,7 +903,7 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
if (!mandatory)
|
if (!mandatory)
|
||||||
{
|
{
|
||||||
if (now - last_sync < config::refresh_interval_min)
|
if (now - last_sync < config::refresh_interval_min)
|
||||||
return refresh_status;
|
return refresh_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct call_refreshed
|
struct call_refreshed
|
||||||
@@ -877,7 +931,7 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
{
|
{
|
||||||
lock.lock();
|
lock.lock();
|
||||||
last_sync = now;
|
last_sync = now;
|
||||||
return refresh_status = failed_login;
|
return refresh_error = failed_login;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,24 +950,44 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
if (!txs_response)
|
if (!txs_response)
|
||||||
{
|
{
|
||||||
if (txs_response == rpc_unapproved)
|
if (txs_response == rpc_unapproved)
|
||||||
return refresh_status = {error::approval};
|
return refresh_error = {error::approval};
|
||||||
return refresh_status = txs_response.error();
|
return refresh_error = txs_response.error();
|
||||||
}
|
}
|
||||||
if (!outs_response)
|
if (!outs_response)
|
||||||
return refresh_status = outs_response.error();
|
return refresh_error = outs_response.error();
|
||||||
|
|
||||||
const std::uint64_t orig_scan_height = primary.scan_height;
|
const std::uint64_t orig_scan_height = primary.scan_height;
|
||||||
auto merged = merge_response(*this, *txs_response, *outs_response);
|
auto merged = merge_response(*this, *txs_response, *outs_response);
|
||||||
|
|
||||||
if (!merged.new_subaddrs.empty() && server_lookahead)
|
if (import_error || primary.requested_start < primary.restore_height)
|
||||||
{
|
{
|
||||||
server_lookahead = std::max(*server_lookahead, merged.new_subaddrs.rbegin()->key);
|
const std::uint64_t from_height = primary.requested_start;
|
||||||
|
lock.unlock();
|
||||||
|
restore_height(from_height);
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lookahead_error)
|
||||||
|
{
|
||||||
|
rpc::upsert_subaddrs_request request{login, {}, true /* get_all */};
|
||||||
|
fill_upsert(*this, request.subaddrs_);
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
const auto response = rpc::invoke<rpc::upsert_subaddrs_response>(client, request);
|
||||||
|
lock.lock();
|
||||||
|
if (response && update_lookaheads(*this, response->all_subaddrs))
|
||||||
|
lookahead_error = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!merged.new_subaddrs.empty() && !lookahead_error)
|
||||||
|
{
|
||||||
|
server_lookahead = std::max(server_lookahead, merged.new_subaddrs.rbegin()->key);
|
||||||
|
|
||||||
// ensure lookahead is from zero, or our logic is busted
|
// ensure lookahead is from zero, or our logic is busted
|
||||||
for (auto& sub : merged.new_subaddrs)
|
for (auto& sub : merged.new_subaddrs)
|
||||||
{
|
{
|
||||||
std::get<0>(sub.head) = 0;
|
std::get<0>(sub.head) = 0;
|
||||||
std::get<1>(sub.head) = add_uint32_clamp(std::get<1>(sub.head), primary.min_lookahead);
|
std::get<1>(sub.head) = add_uint32_clamp(std::get<1>(sub.head), primary.lookahead.minor);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rpc::upsert_subaddrs_request upsert_request{
|
const rpc::upsert_subaddrs_request upsert_request{
|
||||||
@@ -932,12 +1006,12 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
acct.server_lookahead = std::max(acct.server_lookahead, std::get<1>(sub.head));
|
acct.server_lookahead = std::max(acct.server_lookahead, std::get<1>(sub.head));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primary.maj_lookahead)
|
if (primary.lookahead.major)
|
||||||
{
|
{
|
||||||
const std::uint32_t last_used = upsert_request.subaddrs_.rbegin()->key;
|
const std::uint32_t last_used = upsert_request.subaddrs_.rbegin()->key;
|
||||||
const std::uint32_t maj_i = add_uint32_clamp(unsigned(1), last_used);
|
const std::uint32_t maj_i = add_uint32_clamp(unsigned(1), last_used);
|
||||||
const std::uint32_t maj_count = primary.maj_lookahead;
|
const std::uint32_t maj_count = primary.lookahead.major;
|
||||||
const std::uint32_t min_count = std::max(std::uint32_t(1), primary.min_lookahead);
|
const std::uint32_t min_count = std::max(std::uint32_t(1), primary.lookahead.minor);
|
||||||
const rpc::provision_subaddrs_request provision_request{
|
const rpc::provision_subaddrs_request provision_request{
|
||||||
login, maj_i, 0, maj_count, min_count, false /* get_all */
|
login, maj_i, 0, maj_count, min_count, false /* get_all */
|
||||||
};
|
};
|
||||||
@@ -947,23 +1021,22 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
|
|
||||||
if (provision_response)
|
if (provision_response)
|
||||||
{
|
server_lookahead = std::max(server_lookahead, add_uint32_clamp(last_used, maj_count));
|
||||||
if (server_lookahead) // in case another thread jumped in
|
|
||||||
server_lookahead = std::max(*server_lookahead, add_uint32_clamp(last_used, maj_count));
|
|
||||||
}
|
|
||||||
else // if !provision_response
|
else // if !provision_response
|
||||||
server_lookahead = handle_lookahead_error(provision_response.error());
|
lookahead_error = handle_lookahead_error(provision_response.error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // if !upsert_response
|
else // if !upsert_response
|
||||||
server_lookahead = handle_lookahead_error(upsert_response.error());
|
lookahead_error = handle_lookahead_error(upsert_response.error());
|
||||||
} // if new subaddress(es)
|
} // if new subaddress(es)
|
||||||
|
|
||||||
// return error if subaddresses enabled, and recovered wallet
|
// return error if subaddresses enabled, and recovered wallet
|
||||||
refresh_on_exit.release(); // release before acquiring `sync_listener`.
|
refresh_on_exit.release(); // release before acquiring `sync_listener`.
|
||||||
|
const std::error_code rc = refresh_error =
|
||||||
|
import_error ? import_error : lookahead_error;
|
||||||
const boost::lock_guard<boost::mutex> lock_listener{sync_listener};
|
const boost::lock_guard<boost::mutex> lock_listener{sync_listener};
|
||||||
if (!listener)
|
if (!listener)
|
||||||
return refresh_status = server_lookahead.error();
|
return rc;
|
||||||
|
|
||||||
// Call listener functions without holding `sync`, in case a call is made
|
// Call listener functions without holding `sync`, in case a call is made
|
||||||
// back into the library.
|
// back into the library.
|
||||||
@@ -993,7 +1066,7 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
listener->moneySpent(txid, tx->amount);
|
listener->moneySpent(txid, tx->amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return refresh_status = server_lookahead.error();
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code wallet::register_subaccount(const std::uint32_t maj_i)
|
std::error_code wallet::register_subaccount(const std::uint32_t maj_i)
|
||||||
@@ -1011,30 +1084,26 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!server_lookahead)
|
const std::uint32_t needed_maj_i = add_uint32_clamp(maj_i, primary.lookahead.major);
|
||||||
return server_lookahead.error();
|
if (!lookahead_error && needed_maj_i <= server_lookahead)
|
||||||
|
|
||||||
const std::uint32_t needed_maj_i = add_uint32_clamp(maj_i, primary.maj_lookahead);
|
|
||||||
if (needed_maj_i <= *server_lookahead)
|
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// Increase the lookahead by one
|
const std::uint32_t major_count = needed_maj_i - server_lookahead;
|
||||||
const std::uint32_t major_count = needed_maj_i - *server_lookahead;
|
const std::uint32_t minor_count = std::max(std::uint32_t(1), primary.lookahead.minor);
|
||||||
const std::uint32_t minor_count = std::max(std::uint32_t(1), primary.min_lookahead);
|
|
||||||
const rpc::provision_subaddrs_request request{
|
const rpc::provision_subaddrs_request request{
|
||||||
rpc::login{primary.address, primary.view.sec},
|
rpc::login{primary.address, primary.view.sec},
|
||||||
*server_lookahead + 1, 0, major_count, minor_count, false /* get_all */
|
server_lookahead + 1, 0, major_count, minor_count, false /* get_all */
|
||||||
};
|
};
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
const auto response = rpc::invoke<rpc::provision_subaddrs_response>(client, request);
|
const auto response = rpc::invoke<rpc::provision_subaddrs_response>(client, request);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
if (response && server_lookahead)
|
if (response)
|
||||||
server_lookahead = std::max(*server_lookahead, needed_maj_i);
|
server_lookahead = std::max(server_lookahead, needed_maj_i);
|
||||||
else if (!response)
|
else if (!lookahead_error)
|
||||||
server_lookahead = handle_lookahead_error(response.error());
|
lookahead_error = handle_lookahead_error(response.error());
|
||||||
|
|
||||||
return server_lookahead.error();
|
return lookahead_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code wallet::register_subaddress(const std::uint32_t maj_i, const std::uint32_t min_i)
|
std::error_code wallet::register_subaddress(const std::uint32_t maj_i, const std::uint32_t min_i)
|
||||||
@@ -1056,8 +1125,8 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::uint32_t needed_min_i = add_uint32_clamp(min_i, primary.min_lookahead);
|
const std::uint32_t needed_min_i = add_uint32_clamp(min_i, primary.lookahead.minor);
|
||||||
if (needed_min_i <= maj->server_lookahead)
|
if (!lookahead_error && needed_min_i <= maj->server_lookahead)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const std::uint32_t needed_count = add_uint32_clamp(unsigned(1), needed_min_i);
|
const std::uint32_t needed_count = add_uint32_clamp(unsigned(1), needed_min_i);
|
||||||
@@ -1075,29 +1144,21 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
// address could've changed during unlock
|
// address could've changed during unlock
|
||||||
maj = std::addressof(primary.subaccounts.at(maj_i));
|
maj = std::addressof(primary.subaccounts.at(maj_i));
|
||||||
maj->server_lookahead = std::max(maj->server_lookahead, needed_min_i);
|
maj->server_lookahead = std::max(maj->server_lookahead, needed_min_i);
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
else if (server_lookahead)
|
else if (!lookahead_error)
|
||||||
server_lookahead = handle_lookahead_error(response.error());
|
lookahead_error = handle_lookahead_error(response.error());
|
||||||
return server_lookahead.error();
|
return lookahead_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code wallet::set_lookahead(std::uint32_t major, std::uint32_t minor)
|
std::error_code wallet::set_lookahead(std::uint32_t major, std::uint32_t minor)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> lock{sync};
|
boost::unique_lock<boost::mutex> lock{sync};
|
||||||
const bool extending =
|
const bool extending =
|
||||||
primary.maj_lookahead < major ||
|
primary.lookahead.major < major ||
|
||||||
primary.min_lookahead < minor;
|
primary.lookahead.minor < minor;
|
||||||
|
|
||||||
primary.maj_lookahead = major;
|
primary.lookahead.major = major;
|
||||||
primary.min_lookahead = minor;
|
primary.lookahead.minor = minor;
|
||||||
|
|
||||||
if ((!extending && server_lookahead) || (!major && !minor))
|
|
||||||
{
|
|
||||||
if (!server_lookahead)
|
|
||||||
server_lookahead = 0;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!passed_login)
|
if (!passed_login)
|
||||||
{
|
{
|
||||||
@@ -1108,8 +1169,11 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!extending && !lookahead_error)
|
||||||
|
return {};
|
||||||
|
|
||||||
rpc::upsert_subaddrs_request request{
|
rpc::upsert_subaddrs_request request{
|
||||||
rpc::login{primary.address, primary.view.sec}, {}, false /* get_all */
|
rpc::login{primary.address, primary.view.sec}, {}, true /* get_all */
|
||||||
};
|
};
|
||||||
fill_upsert(*this, request.subaddrs_);
|
fill_upsert(*this, request.subaddrs_);
|
||||||
|
|
||||||
@@ -1117,16 +1181,83 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
const auto response = rpc::invoke<rpc::upsert_subaddrs_response>(client, request);
|
const auto response = rpc::invoke<rpc::upsert_subaddrs_response>(client, request);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
if (!response)
|
if (!response)
|
||||||
return (server_lookahead = handle_lookahead_error(response.error())).error();
|
return (lookahead_error = handle_lookahead_error(response.error()));
|
||||||
|
|
||||||
update_lookaheads(*this, request.subaddrs_);
|
update_lookaheads(*this, response->all_subaddrs);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code wallet::restore_height(const std::uint64_t /*height not supported */)
|
std::error_code wallet::restore_height(const std::uint64_t height)
|
||||||
{
|
{
|
||||||
/* TODO */
|
boost::unique_lock<boost::mutex> lock{sync};
|
||||||
throw std::logic_error{"restore_height not implemented"};
|
|
||||||
|
if (primary.requested_start < height)
|
||||||
|
return import_error;
|
||||||
|
|
||||||
|
if (!passed_login)
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
const std::error_code error = login();
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// current api does not allow height selection, defaults to 0
|
||||||
|
rpc::login login{primary.address, primary.view.sec};
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
const auto import = rpc::invoke<rpc::import_response>(client, login);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
if (!import)
|
||||||
|
return import_error = import.error();
|
||||||
|
|
||||||
|
if (import->request_fulfilled)
|
||||||
|
return import_error = {};
|
||||||
|
|
||||||
|
const unsigned total =
|
||||||
|
unsigned(bool(import->import_fee)) + bool(import->payment_address);
|
||||||
|
switch (total)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
return import_error = error::import_pending;
|
||||||
|
case 1:
|
||||||
|
return import_error = error::import_invalid;
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptonote::address_parse_info info{};
|
||||||
|
if (!cryptonote::get_account_address_from_str(info, convert_net_type(primary.type), *import->payment_address))
|
||||||
|
return import_error = error::import_invalid;
|
||||||
|
if (info.has_payment_id && import->payment_id)
|
||||||
|
return import_error = error::import_invalid;
|
||||||
|
if (import->payment_id && (import->payment_id->empty() || (import->payment_id->size() != sizeof(crypto::hash8) && import->payment_id->size() != sizeof(crypto::hash))))
|
||||||
|
return import_error = error::import_invalid;
|
||||||
|
|
||||||
|
std::string payment_id;
|
||||||
|
if (import->payment_id)
|
||||||
|
payment_id = epee::to_hex::string(epee::to_span(*import->payment_id));
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i < primary.addressbook.size(); ++i)
|
||||||
|
{
|
||||||
|
const bool existing =
|
||||||
|
primary.addressbook[i].address == *import->payment_address &&
|
||||||
|
primary.addressbook[i].payment_id == payment_id;
|
||||||
|
if (existing)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description = "Payment of " + cryptonote::print_money(*import->import_fee) + " XMR is needed to import/restore height";
|
||||||
|
if (i == primary.addressbook.size())
|
||||||
|
primary.addressbook.push_back(address_book_entry{std::move(*import->payment_address), std::move(payment_id), std::move(description)});
|
||||||
|
else
|
||||||
|
primary.addressbook[i] = address_book_entry{std::move(*import->payment_address), std::move(payment_id), std::move(description)};
|
||||||
|
|
||||||
|
return import_error = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code wallet::send_tx(epee::byte_slice tx_bytes)
|
std::error_code wallet::send_tx(epee::byte_slice tx_bytes)
|
||||||
|
|||||||
+15
-10
@@ -199,16 +199,16 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
{
|
{
|
||||||
account() = delete;
|
account() = delete;
|
||||||
|
|
||||||
std::string address; //!> not serialized, recovered on read_bytes
|
std::string address; //!< not serialized, recovered on read_bytes
|
||||||
std::string language;
|
std::string language;
|
||||||
std::vector<address_book_entry> addressbook;
|
std::vector<address_book_entry> addressbook;
|
||||||
std::vector<sub_account> subaccounts; //! Enabled major subaccounts. Index indicates `sub.major` value.
|
std::vector<sub_account> subaccounts; //! Enabled major subaccounts. Index indicates `sub.major` value.
|
||||||
std::unordered_map<crypto::hash, std::shared_ptr<transaction>> txes;
|
std::unordered_map<crypto::hash, std::shared_ptr<transaction>> txes;
|
||||||
std::unordered_map<std::string, std::string> attributes;
|
std::unordered_map<std::string, std::string> attributes;
|
||||||
std::uint64_t scan_height;
|
std::uint64_t scan_height; //!< last block scanned
|
||||||
std::uint64_t restore_height;
|
std::uint64_t restore_height; //!< aka scan start height
|
||||||
std::uint32_t maj_lookahead = config::subaddr_major_lookahead;
|
std::uint64_t requested_start = std::numeric_limits<std::uint64_t>::max();
|
||||||
std::uint32_t min_lookahead = config::subaddr_minor_lookahead;
|
config::lookahead lookahead = config::default_lookahead; //<! Lookahead as requested by user
|
||||||
Monero::NetworkType type;
|
Monero::NetworkType type;
|
||||||
keypair view;
|
keypair view;
|
||||||
keypair spend;
|
keypair spend;
|
||||||
@@ -222,12 +222,14 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
Monero::WalletListener* listener;
|
Monero::WalletListener* listener;
|
||||||
rpc::http_client client;
|
rpc::http_client client;
|
||||||
account primary;
|
account primary;
|
||||||
std::error_code refresh_status; //!< Cached because `refresh(...)` is rate limited
|
std::error_code refresh_error; //!< Cached because `refresh(...)` is rate limited
|
||||||
expect<std::uint32_t> server_lookahead; //!< Status of major lookahead server-side. Inclusive
|
std::error_code lookahead_error; //!< warnings/errors of `server_lookahead` value
|
||||||
|
std::error_code import_error; //!< Error from `import_wallet_request`
|
||||||
std::chrono::steady_clock::time_point last_sync;
|
std::chrono::steady_clock::time_point last_sync;
|
||||||
std::uint64_t blockchain_height;
|
std::uint64_t blockchain_height;
|
||||||
std::uint64_t per_byte_fee;
|
std::uint64_t per_byte_fee;
|
||||||
std::uint64_t fee_mask;
|
std::uint64_t fee_mask;
|
||||||
|
std::uint32_t server_lookahead; //!< Status of major lookahead server-side. Inclusive
|
||||||
mutable boost::mutex sync;
|
mutable boost::mutex sync;
|
||||||
boost::mutex sync_listener;
|
boost::mutex sync_listener;
|
||||||
boost::mutex sync_refresh;
|
boost::mutex sync_refresh;
|
||||||
@@ -239,16 +241,18 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
crypto::public_key get_spend_public(const rpc::address_meta& index) const;
|
crypto::public_key get_spend_public(const rpc::address_meta& index) const;
|
||||||
std::string get_spend_address(const rpc::address_meta& index) const;
|
std::string get_spend_address(const rpc::address_meta& index) const;
|
||||||
|
|
||||||
//! Serializate `this` wallet to msgpack. Locks contents.
|
//! Serialize `this` wallet to msgpack. Locks contents.
|
||||||
expect<epee::byte_slice> to_bytes() const;
|
expect<epee::byte_slice> to_bytes() const;
|
||||||
|
|
||||||
//! De-serialize `this` from msgpack. Locks+replaces contents.
|
//! De-serialize `this` from msgpack. Locks+replaces contents.
|
||||||
std::error_code from_bytes(epee::byte_slice source);
|
std::error_code from_bytes(epee::byte_slice source);
|
||||||
|
|
||||||
//! Attempt login and sync subaddresses. \return no errors if login succeeded
|
/*! Attempt login and sync subaddresses. The result of subaddress syncing,
|
||||||
|
including errors, is stored in `server_lookahead`.
|
||||||
|
\return No errors if login succeeded. */
|
||||||
std::error_code login();
|
std::error_code login();
|
||||||
|
|
||||||
//! Refreshes txes information. Strong exception guarantee. Spuriously returns success.
|
//! Refreshes txes information. Strong exception guarantee.
|
||||||
std::error_code refresh(bool mandatory = false);
|
std::error_code refresh(bool mandatory = false);
|
||||||
|
|
||||||
//! Notify server that new major accounts need to be watched.
|
//! Notify server that new major accounts need to be watched.
|
||||||
@@ -257,6 +261,7 @@ namespace lwsf { namespace internal { namespace backend
|
|||||||
//! Notify server that new minor accounts need to be watched.
|
//! Notify server that new minor accounts need to be watched.
|
||||||
std::error_code register_subaddress(std::uint32_t maj_i, std::uint32_t min_i);
|
std::error_code register_subaddress(std::uint32_t maj_i, std::uint32_t min_i);
|
||||||
|
|
||||||
|
//! Modify local and possibly server lookahead
|
||||||
std::error_code set_lookahead(std::uint32_t major, std::uint32_t minor);
|
std::error_code set_lookahead(std::uint32_t major, std::uint32_t minor);
|
||||||
|
|
||||||
std::error_code restore_height(const std::uint64_t height);
|
std::error_code restore_height(const std::uint64_t height);
|
||||||
|
|||||||
+10
-16
@@ -41,28 +41,22 @@ namespace lwsf
|
|||||||
return "Configuration issue";
|
return "Configuration issue";
|
||||||
case error::create:
|
case error::create:
|
||||||
return "Account creation not possible";
|
return "Account creation not possible";
|
||||||
case error::crypto_failure:
|
case error::import_fee:
|
||||||
return "Cryptography issue";
|
return "Import/restore from height needs payment, see address book";
|
||||||
case error::invalid_encoding:
|
case error::import_invalid:
|
||||||
return "File had invalid encoding";
|
return "Import/restore from height failed due to invalid fee";
|
||||||
case error::invalid_scheme:
|
case error::import_pending:
|
||||||
return "Invalid URL scheme";
|
return "Import/restore from height is awaiting approval";
|
||||||
case error::network_type:
|
case error::network_type:
|
||||||
return "Mismatch on network type";
|
return "Mismatch on network type";
|
||||||
case error::read_failure:
|
case error::subaddr_ahead:
|
||||||
return "Failed to read file";
|
return "server has limits too low for requested subaddress lookahead";
|
||||||
case error::subaddr_disabled:
|
case error::subaddr_disabled:
|
||||||
return "light-wallet-server has subaddresses disabled";
|
return "server has subaddresses disabled";
|
||||||
case error::subaddr_local:
|
case error::subaddr_local:
|
||||||
return "lwsf limits on subaddresses too small";
|
return "lwsf (local) limits on subaddresses too small";
|
||||||
case error::subaddr_server:
|
|
||||||
return "light-wallet-server has limits on subaddresses that are too small";
|
|
||||||
case error::unexpected_userinfo:
|
case error::unexpected_userinfo:
|
||||||
return "Unexpected user+pass field in URL";
|
return "Unexpected user+pass field in URL";
|
||||||
case error::unsupported_format:
|
|
||||||
return "Could not unpack file";
|
|
||||||
case error::write_failure:
|
|
||||||
return "Failed to write file";
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
+4
-6
@@ -37,17 +37,15 @@ namespace lwsf
|
|||||||
approval, //!< Account needs approval
|
approval, //!< Account needs approval
|
||||||
configuration, //!< Bad Configuration
|
configuration, //!< Bad Configuration
|
||||||
create, //!< Account creation not possible
|
create, //!< Account creation not possible
|
||||||
crypto_failure, //!< Failure in crypto
|
import_fee, //!< `import_wallet_request` has fee
|
||||||
invalid_encoding, //!< Invalid percent encoding
|
import_invalid, //!< `import_wallet_request` has invalid fee
|
||||||
|
import_pending, //!< `import_wallet_request` not fulfilled/approved
|
||||||
invalid_scheme, //!< Invalid network scheme
|
invalid_scheme, //!< Invalid network scheme
|
||||||
network_type, //!< Mismatch on network type
|
network_type, //!< Mismatch on network type
|
||||||
read_failure, //!< Failed to read file
|
subaddr_ahead, //!< Server limits on subaddresses affects lookahead
|
||||||
subaddr_disabled, //!< Server has subaddresses disabled
|
subaddr_disabled, //!< Server has subaddresses disabled
|
||||||
subaddr_local, //!< Local limits on subaddresses too small
|
subaddr_local, //!< Local limits on subaddresses too small
|
||||||
subaddr_server, //!< Server limits on subaddresses too small
|
|
||||||
unexpected_userinfo,//!< Unexpected user+pass provided
|
unexpected_userinfo,//!< Unexpected user+pass provided
|
||||||
unsupported_format, //!< File format could not be unpacked
|
|
||||||
write_failure //!< Failed to write file
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \return Error message string.
|
//! \return Error message string.
|
||||||
|
|||||||
+8
-4
@@ -34,9 +34,17 @@
|
|||||||
|
|
||||||
namespace lwsf { namespace config
|
namespace lwsf { namespace config
|
||||||
{
|
{
|
||||||
|
struct lookahead
|
||||||
|
{
|
||||||
|
std::uint32_t major = 0;
|
||||||
|
std::uint32_t minor = 0;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr const std::chrono::seconds connect_timeout{5};
|
constexpr const std::chrono::seconds connect_timeout{5};
|
||||||
constexpr const std::chrono::seconds daemon_status_cache{10};
|
constexpr const std::chrono::seconds daemon_status_cache{10};
|
||||||
constexpr const std::string_view default_account_name{"Untitled account"};
|
constexpr const std::string_view default_account_name{"Untitled account"};
|
||||||
|
constexpr const lookahead default_lookahead{50, 200};
|
||||||
|
constexpr const lookahead default_minimal_lookahead{5, 15};
|
||||||
constexpr const std::string_view default_primary_name{"Primary account"};
|
constexpr const std::string_view default_primary_name{"Primary account"};
|
||||||
constexpr const std::size_t initial_buffer_size = 1024 * 64; // 64 KiB
|
constexpr const std::size_t initial_buffer_size = 1024 * 64; // 64 KiB
|
||||||
constexpr const std::size_t max_txes_in_rpc = 2048;
|
constexpr const std::size_t max_txes_in_rpc = 2048;
|
||||||
@@ -44,8 +52,4 @@ namespace lwsf { namespace config
|
|||||||
constexpr const std::chrono::seconds refresh_interval{30};
|
constexpr const std::chrono::seconds refresh_interval{30};
|
||||||
constexpr const std::chrono::seconds refresh_interval_min{5};
|
constexpr const std::chrono::seconds refresh_interval_min{5};
|
||||||
constexpr const std::chrono::seconds rpc_timeout{5};
|
constexpr const std::chrono::seconds rpc_timeout{5};
|
||||||
constexpr const std::uint32_t subaddr_major_lookahead = 50;
|
|
||||||
constexpr const std::uint32_t subaddr_minor_lookahead = 200;
|
|
||||||
constexpr const unsigned subaddr_retry{10};
|
|
||||||
|
|
||||||
}} // lwsf // config
|
}} // lwsf // config
|
||||||
|
|||||||
@@ -399,6 +399,8 @@ namespace lwsf { namespace internal { namespace rpc
|
|||||||
void read_bytes(wire::json_reader& source, import_response& self)
|
void read_bytes(wire::json_reader& source, import_response& self)
|
||||||
{
|
{
|
||||||
wire::object(source,
|
wire::object(source,
|
||||||
|
WIRE_OPTIONAL_FIELD(payment_address),
|
||||||
|
WIRE_OPTIONAL_FIELD(payment_id),
|
||||||
WIRE_OPTIONAL_FIELD(import_fee),
|
WIRE_OPTIONAL_FIELD(import_fee),
|
||||||
WIRE_FIELD(request_fulfilled)
|
WIRE_FIELD(request_fulfilled)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -229,11 +229,11 @@ namespace lwsf { namespace internal { namespace rpc
|
|||||||
|
|
||||||
struct subaddrs
|
struct subaddrs
|
||||||
{
|
{
|
||||||
subaddrs()
|
constexpr subaddrs() noexcept
|
||||||
: head({0, 0}), key(0)
|
: head({0, 0}), key(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
explicit subaddrs(const std::uint32_t key)
|
constexpr explicit subaddrs(const std::uint32_t key) noexcept
|
||||||
: head({0, 0}), key(key)
|
: head({0, 0}), key(key)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -243,11 +243,11 @@ namespace lwsf { namespace internal { namespace rpc
|
|||||||
WIRE_JSON_DECLARE_OBJECT(subaddrs);
|
WIRE_JSON_DECLARE_OBJECT(subaddrs);
|
||||||
void read_bytes(wire::json_reader&, subaddrs&);
|
void read_bytes(wire::json_reader&, subaddrs&);
|
||||||
|
|
||||||
inline bool operator<(const subaddrs& lhs, const subaddrs& rhs) noexcept
|
constexpr inline bool operator<(const subaddrs& lhs, const subaddrs& rhs) noexcept
|
||||||
{ return lhs.key < rhs.key; }
|
{ return lhs.key < rhs.key; }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline bool operator<(const subaddrs& lhs, const T rhs) noexcept
|
constexpr inline bool operator<(const subaddrs& lhs, const T rhs) noexcept
|
||||||
{
|
{
|
||||||
static_assert(std::is_unsigned<T>());
|
static_assert(std::is_unsigned<T>());
|
||||||
return lhs.key < rhs;
|
return lhs.key < rhs;
|
||||||
@@ -337,6 +337,10 @@ namespace lwsf { namespace internal { namespace rpc
|
|||||||
struct import_response
|
struct import_response
|
||||||
{
|
{
|
||||||
import_response() = delete;
|
import_response() = delete;
|
||||||
|
static constexpr const char* endpoint() noexcept { return "/import_wallet_request"; }
|
||||||
|
|
||||||
|
std::optional<std::string> payment_address;
|
||||||
|
std::optional<epee::byte_slice> payment_id;
|
||||||
std::optional<uint64_string> import_fee;
|
std::optional<uint64_string> import_fee;
|
||||||
bool request_fulfilled;
|
bool request_fulfilled;
|
||||||
};
|
};
|
||||||
@@ -398,7 +402,7 @@ namespace lwsf { namespace internal { namespace rpc
|
|||||||
static constexpr const char* endpoint() noexcept { return "/upsert_subaddrs"; }
|
static constexpr const char* endpoint() noexcept { return "/upsert_subaddrs"; }
|
||||||
|
|
||||||
boost::container::flat_set<subaddrs> new_subaddrs;
|
boost::container::flat_set<subaddrs> new_subaddrs;
|
||||||
boost::container::flat_set<subaddrs> all_subaddrs;
|
boost::container::flat_set<subaddrs, std::less<>> all_subaddrs;
|
||||||
};
|
};
|
||||||
void read_bytes(wire::json_reader&, upsert_subaddrs_response&);
|
void read_bytes(wire::json_reader&, upsert_subaddrs_response&);
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ namespace lwsf { namespace internal
|
|||||||
|
|
||||||
for (const auto& tx : data_->primary.txes)
|
for (const auto& tx : data_->primary.txes)
|
||||||
{
|
{
|
||||||
|
if (tx.second->failed)
|
||||||
|
continue;
|
||||||
|
|
||||||
const bool unlocked = tx.second->is_unlocked(chain_height, net_type);
|
const bool unlocked = tx.second->is_unlocked(chain_height, net_type);
|
||||||
for (const auto& receive : tx.second->receives)
|
for (const auto& receive : tx.second->receives)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -100,7 +100,11 @@ namespace lwsf { namespace internal
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||||
data_->primary.subaccounts.at(accountIndex).detail[addressIndex].label = label;
|
auto& acct = data_->primary.subaccounts.at(accountIndex);
|
||||||
|
if (!addressIndex || !label.empty())
|
||||||
|
acct.detail.try_emplace(addressIndex).first->second.label = label;
|
||||||
|
else
|
||||||
|
acct.detail.erase(addressIndex);
|
||||||
}
|
}
|
||||||
refresh(accountIndex);
|
refresh(accountIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-128
@@ -70,7 +70,9 @@ namespace lwsf { namespace internal
|
|||||||
boost::unique_future<boost::asio::ip::tcp::socket>
|
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||||
operator()(const std::string&, const std::string&, boost::asio::steady_timer&) const
|
operator()(const std::string&, const std::string&, boost::asio::steady_timer&) const
|
||||||
{
|
{
|
||||||
throw std::runtime_error{"Unable to connect"};
|
return boost::make_exceptional_future<boost::asio::ip::tcp::socket>(
|
||||||
|
std::runtime_error{"invalid proxy value"}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -344,31 +346,29 @@ namespace lwsf { namespace internal
|
|||||||
}
|
}
|
||||||
} // anonymous
|
} // anonymous
|
||||||
|
|
||||||
bool wallet::set_error(const std::error_code status, const bool update_iff_error) const
|
bool wallet::set_error(const std::error_code error, const bool clear) const
|
||||||
{
|
{
|
||||||
const boost::lock_guard<boost::mutex> lock{error_sync_};
|
if (clear || error)
|
||||||
if (!update_iff_error || status)
|
{
|
||||||
status_ = status;
|
const boost::lock_guard<boost::mutex> lock{error_sync_};
|
||||||
return !status_;
|
error_ = error;
|
||||||
|
}
|
||||||
|
return !error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wallet::set_critical(const std::exception& e) const
|
void wallet::set_critical(const std::exception& e) const
|
||||||
{
|
{
|
||||||
const boost::lock_guard<boost::mutex> lock{error_sync_};
|
const boost::lock_guard<boost::mutex> lock{error_sync_};
|
||||||
exception_error_ = e.what();
|
exception_error_ = e.what();
|
||||||
status_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void wallet::queue_work(F&& f)
|
void wallet::queue_work(F&& f)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
{
|
||||||
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
const boost::lock_guard<boost::mutex> lock{refresh_sync_};
|
||||||
work_queue_.push_back(std::forward<F>(f));
|
work_queue_.push_back(std::forward<F>(f));
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
|
||||||
{ set_critical(e); }
|
|
||||||
startRefresh();
|
startRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +407,7 @@ namespace lwsf { namespace internal
|
|||||||
work_queue_.pop_front();
|
work_queue_.pop_front();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
if (work)
|
if (work)
|
||||||
set_error(work(), true);
|
set_error(work());
|
||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,11 +415,12 @@ namespace lwsf { namespace internal
|
|||||||
mandatory_refresh_ = false;
|
mandatory_refresh_ = false;
|
||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
if (refresh_interval_ <= now - last_refresh && (thread_state_ == state::run || mandatory_refresh))
|
if (mandatory_refresh_ || (refresh_interval_ <= now - last_refresh && thread_state_ == state::run))
|
||||||
{
|
{
|
||||||
// refresh has strong exception guarantee - never in partial state.
|
// refresh has strong exception guarantee - never in partial state.
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
set_error(data_->refresh(mandatory_refresh), false);
|
last_refresh = now;
|
||||||
|
set_error(data_->refresh(mandatory_refresh), true /*clear*/);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
else if (thread_state_ == state::skip_once)
|
else if (thread_state_ == state::skip_once)
|
||||||
@@ -498,7 +499,7 @@ namespace lwsf { namespace internal
|
|||||||
password_(std::move(password)),
|
password_(std::move(password)),
|
||||||
work_queue_(),
|
work_queue_(),
|
||||||
exception_error_(),
|
exception_error_(),
|
||||||
status_(),
|
error_(),
|
||||||
thread_(),
|
thread_(),
|
||||||
iterations_(kdf_rounds),
|
iterations_(kdf_rounds),
|
||||||
mixin_(config::mixin_default),
|
mixin_(config::mixin_default),
|
||||||
@@ -530,7 +531,7 @@ namespace lwsf { namespace internal
|
|||||||
password_(std::move(password)),
|
password_(std::move(password)),
|
||||||
work_queue_(),
|
work_queue_(),
|
||||||
exception_error_(),
|
exception_error_(),
|
||||||
status_(),
|
error_(),
|
||||||
thread_(),
|
thread_(),
|
||||||
iterations_(kdf_rounds),
|
iterations_(kdf_rounds),
|
||||||
mixin_(config::mixin_default),
|
mixin_(config::mixin_default),
|
||||||
@@ -542,40 +543,29 @@ namespace lwsf { namespace internal
|
|||||||
thread_state_(state::stop),
|
thread_state_(state::stop),
|
||||||
mandatory_refresh_(false)
|
mandatory_refresh_(false)
|
||||||
{
|
{
|
||||||
if (!data_)
|
|
||||||
throw std::invalid_argument{"lwsf::backend::wallet cannot be null"};
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
epee::byte_slice file = try_load(filename_ + ".new");
|
epee::byte_slice file = try_load(filename_ + ".new");
|
||||||
if (file.empty())
|
if (file.empty())
|
||||||
file = try_load(filename_);
|
file = try_load(filename_);
|
||||||
|
|
||||||
if (!filename_.empty() && !file.empty())
|
if (file.empty())
|
||||||
{
|
throw std::runtime_error{"Unable to open wallet file " + filename_};
|
||||||
encrypted_file contents{};
|
|
||||||
if (!(status_ = wire::msgpack::from_bytes(std::move(file), contents)))
|
encrypted_file contents{};
|
||||||
{
|
if (std::error_code error = wire::msgpack::from_bytes(std::move(file), contents))
|
||||||
epee::byte_slice payload = contents.get_payload(password_);
|
throw std::system_error{error};
|
||||||
if (!payload.empty())
|
|
||||||
{
|
epee::byte_slice payload = contents.get_payload(password_);
|
||||||
if (!(status_ = data_->from_bytes(std::move(payload))))
|
if (std::error_code error = data_->from_bytes(std::move(payload)))
|
||||||
{
|
throw std::system_error{error};
|
||||||
// lock not needed; data_ was created and unique to us
|
|
||||||
if (nettype != data_->primary.type)
|
// lock not needed; data_ was created and unique to us
|
||||||
throw std::runtime_error{"Wallet file NetworkType does not match requested"};
|
if (nettype != data_->primary.type)
|
||||||
}
|
throw std::runtime_error{"Wallet file NetworkType does not match requested"};
|
||||||
}
|
|
||||||
else
|
|
||||||
status_ = error::unsupported_format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
status_ = error::read_failure;
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
status_.clear();
|
|
||||||
exception_error_ = e.what();
|
exception_error_ = e.what();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,7 +580,7 @@ namespace lwsf { namespace internal
|
|||||||
password_(std::move(password)),
|
password_(std::move(password)),
|
||||||
work_queue_(),
|
work_queue_(),
|
||||||
exception_error_(),
|
exception_error_(),
|
||||||
status_(),
|
error_(),
|
||||||
thread_(),
|
thread_(),
|
||||||
iterations_(kdf_rounds),
|
iterations_(kdf_rounds),
|
||||||
mixin_(config::mixin_default),
|
mixin_(config::mixin_default),
|
||||||
@@ -630,7 +620,7 @@ namespace lwsf { namespace internal
|
|||||||
password_(std::move(password)),
|
password_(std::move(password)),
|
||||||
work_queue_(),
|
work_queue_(),
|
||||||
exception_error_(),
|
exception_error_(),
|
||||||
status_(),
|
error_(),
|
||||||
thread_(),
|
thread_(),
|
||||||
iterations_(kdf_rounds),
|
iterations_(kdf_rounds),
|
||||||
mixin_(config::mixin_default),
|
mixin_(config::mixin_default),
|
||||||
@@ -679,25 +669,21 @@ namespace lwsf { namespace internal
|
|||||||
|
|
||||||
void wallet::add_subaddress(const std::uint32_t accountIndex, std::string label)
|
void wallet::add_subaddress(const std::uint32_t accountIndex, std::string label)
|
||||||
{
|
{
|
||||||
try
|
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||||
{
|
auto& accts = data_->primary.subaccounts;
|
||||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
|
||||||
auto& accts = data_->primary.subaccounts;
|
|
||||||
|
|
||||||
if (accts.size() <= accountIndex)
|
if (accts.size() <= accountIndex)
|
||||||
throw std::runtime_error{"add_subaddress: account does not exist"};
|
throw std::runtime_error{"add_subaddress: account does not exist"};
|
||||||
|
|
||||||
auto& acct = accts.at(accountIndex);
|
auto& acct = accts.at(accountIndex);
|
||||||
if (std::numeric_limits<std::uint32_t>::max() <= acct.last)
|
if (std::numeric_limits<std::uint32_t>::max() <= acct.last)
|
||||||
throw std::runtime_error{"add_subaddress: exceeded minor indexes"};
|
throw std::runtime_error{"add_subaddress: exceeded minor indexes"};
|
||||||
|
|
||||||
const std::uint32_t min_i = ++acct.last;
|
const std::uint32_t min_i = ++acct.last;
|
||||||
|
if (!label.empty())
|
||||||
acct.detail.try_emplace(min_i).first->second.label = std::move(label);
|
acct.detail.try_emplace(min_i).first->second.label = std::move(label);
|
||||||
|
|
||||||
queue_work([=] () { return data_->register_subaddress(accountIndex, min_i); });
|
queue_work([=] () { return data_->register_subaddress(accountIndex, min_i); });
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{ set_critical(e); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string wallet::seed(const std::string& seed_offset) const
|
std::string wallet::seed(const std::string& seed_offset) const
|
||||||
@@ -761,10 +747,10 @@ namespace lwsf { namespace internal
|
|||||||
status = Status_Critical;
|
status = Status_Critical;
|
||||||
errorString = exception_error_;
|
errorString = exception_error_;
|
||||||
}
|
}
|
||||||
else if (status_)
|
else if (error_)
|
||||||
{
|
{
|
||||||
status = Status_Error;
|
status = Status_Error;
|
||||||
errorString = status_.message();
|
errorString = error_.message();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -816,39 +802,33 @@ namespace lwsf { namespace internal
|
|||||||
{
|
{
|
||||||
const std::string& real_path = path.empty() ? filename_ : path;
|
const std::string& real_path = path.empty() ? filename_ : path;
|
||||||
|
|
||||||
std::error_code status{};
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
expect<epee::byte_slice> payload = data_->to_bytes();
|
epee::byte_slice payload = data_->to_bytes().value();
|
||||||
if (payload)
|
const auto contents = encrypted_file::make(payload, iterations_, password_);
|
||||||
{
|
const auto payload_size = payload.size();
|
||||||
const auto contents = encrypted_file::make(*payload, iterations_, password_);
|
payload = nullptr; // free up some memory that is no longer needed
|
||||||
const auto payload_size = payload->size();
|
|
||||||
*payload = nullptr; // free up some memory that is no longer needed
|
|
||||||
|
|
||||||
epee::byte_stream buffer;
|
epee::byte_stream buffer;
|
||||||
buffer.reserve(payload_size + 2048);
|
buffer.reserve(payload_size + 2048);
|
||||||
buffer.write(file_magic.data(), file_magic.size());
|
buffer.write(file_magic.data(), file_magic.size());
|
||||||
|
|
||||||
if (!(status = wire::msgpack::to_bytes(buffer, contents)))
|
if (std::error_code error = wire::msgpack::to_bytes(buffer, contents))
|
||||||
{
|
throw std::system_error{error};
|
||||||
const std::filesystem::path file = real_path;
|
|
||||||
const std::filesystem::path new_file = real_path + ".new";
|
|
||||||
const std::filesystem::path directory =
|
|
||||||
std::filesystem::path{real_path}.remove_filename();
|
|
||||||
|
|
||||||
if (std::filesystem::exists(new_file))
|
const std::filesystem::path file = real_path;
|
||||||
std::filesystem::rename(new_file, file, status);
|
const std::filesystem::path new_file = real_path + ".new";
|
||||||
|
const std::filesystem::path directory =
|
||||||
// blocks until file and directory contents are synced
|
std::filesystem::path{real_path}.remove_filename();
|
||||||
if (atomic_file_write(new_file, directory, epee::byte_slice{std::move(buffer)}))
|
|
||||||
std::filesystem::rename(new_file, file, status);
|
if (std::filesystem::exists(new_file))
|
||||||
else
|
std::filesystem::rename(new_file, file);
|
||||||
status = error::write_failure;
|
|
||||||
}
|
// blocks until file and directory contents are synced
|
||||||
}
|
if (!atomic_file_write(new_file, directory, epee::byte_slice{std::move(buffer)}))
|
||||||
else
|
throw std::runtime_error{"Failed to write file " + real_path};
|
||||||
status = payload.error();
|
|
||||||
|
std::filesystem::rename(new_file, file);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
@@ -856,8 +836,7 @@ namespace lwsf { namespace internal
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will clear any existing errors
|
return true;
|
||||||
return set_error(status, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wallet::init(const std::string &daemon_address, uint64_t, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool light_wallet, const std::string &proxy_address)
|
bool wallet::init(const std::string &daemon_address, uint64_t, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool light_wallet, const std::string &proxy_address)
|
||||||
@@ -873,7 +852,6 @@ namespace lwsf { namespace internal
|
|||||||
if (!url.m_uri_content.m_path.empty())
|
if (!url.m_uri_content.m_path.empty())
|
||||||
throw std::runtime_error{"LWS URL contains path (unsupported)"};
|
throw std::runtime_error{"LWS URL contains path (unsupported)"};
|
||||||
|
|
||||||
|
|
||||||
if (!proxy_address.empty() && !setProxy(proxy_address))
|
if (!proxy_address.empty() && !setProxy(proxy_address))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -901,8 +879,10 @@ namespace lwsf { namespace internal
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wallet::setRefreshFromBlockHeight(uint64_t height)
|
void wallet::setRefreshFromBlockHeight(const std::uint64_t height)
|
||||||
{
|
{
|
||||||
|
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||||
|
data_->primary.requested_start = std::min(data_->primary.requested_start, height);
|
||||||
queue_work([this, height] () { return data_->restore_height(height); });
|
queue_work([this, height] () { return data_->restore_height(height); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -919,28 +899,17 @@ namespace lwsf { namespace internal
|
|||||||
|
|
||||||
bool wallet::connectToDaemon()
|
bool wallet::connectToDaemon()
|
||||||
{
|
{
|
||||||
struct on_throw
|
const bool connected = data_->client.is_connected();
|
||||||
{
|
boost::unique_lock<boost::mutex> lock{data_->sync};
|
||||||
void operator()(wallet* self) const
|
if (connected && data_->passed_login)
|
||||||
{
|
|
||||||
if (self)
|
|
||||||
self->data_->client.disconnect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data_->client.is_connected())
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
std::unique_ptr<wallet, on_throw> disconnect{this};
|
if (connected || data_->client.connect(config::connect_timeout))
|
||||||
if (data_->client.connect(config::connect_timeout))
|
|
||||||
{
|
{
|
||||||
const std::error_code err = data_->login();
|
lock.unlock();
|
||||||
if (!err)
|
return set_error(data_->login());
|
||||||
disconnect.release();
|
|
||||||
return set_error(err, false);
|
|
||||||
}
|
}
|
||||||
set_error(rpc::error::no_response, false);
|
return set_error(rpc::error::no_response);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Monero::Wallet::ConnectionStatus wallet::connected() const
|
Monero::Wallet::ConnectionStatus wallet::connected() const
|
||||||
@@ -955,13 +924,19 @@ namespace lwsf { namespace internal
|
|||||||
|
|
||||||
bool wallet::setProxy(const std::string &address)
|
bool wallet::setProxy(const std::string &address)
|
||||||
{
|
{
|
||||||
auto endpoint = net::get_tcp_endpoint(address);
|
data_->client.disconnect();
|
||||||
if (!endpoint)
|
if (address.empty())
|
||||||
|
data_->client.set_connector(epee::net_utils::direct_connect{});
|
||||||
|
else
|
||||||
{
|
{
|
||||||
data_->client.set_connector(null_connector{});
|
auto endpoint = net::get_tcp_endpoint(address);
|
||||||
return set_error(endpoint.error(), false);
|
if (!endpoint)
|
||||||
|
{
|
||||||
|
data_->client.set_connector(null_connector{});
|
||||||
|
return set_error(endpoint.error());
|
||||||
|
}
|
||||||
|
data_->client.set_connector(net::socks::connector{std::move(*endpoint)});
|
||||||
}
|
}
|
||||||
data_->client.set_connector(net::socks::connector{std::move(*endpoint)});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,7 +1057,7 @@ namespace lwsf { namespace internal
|
|||||||
|
|
||||||
bool wallet::refresh()
|
bool wallet::refresh()
|
||||||
{
|
{
|
||||||
try { return set_error(data_->refresh(true), true); }
|
try { return set_error(data_->refresh(true), true /* clear */); }
|
||||||
catch (const std::exception& e) { set_critical(e); }
|
catch (const std::exception& e) { set_critical(e); }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1118,20 +1093,15 @@ namespace lwsf { namespace internal
|
|||||||
|
|
||||||
void wallet::addSubaddressAccount(const std::string& label)
|
void wallet::addSubaddressAccount(const std::string& label)
|
||||||
{
|
{
|
||||||
try
|
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||||
{
|
auto& accts = data_->primary.subaccounts;
|
||||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
|
||||||
auto& accts = data_->primary.subaccounts;
|
|
||||||
|
|
||||||
const std::size_t index = accts.size();
|
const std::size_t index = accts.size();
|
||||||
if (std::numeric_limits<std::uint32_t>::max() < index)
|
if (std::numeric_limits<std::uint32_t>::max() < index)
|
||||||
throw std::runtime_error{"addSubddressAccount exceeded subaddress indexes"};
|
throw std::runtime_error{"addSubddressAccount exceeded subaddress indexes"};
|
||||||
|
|
||||||
accts.emplace_back().detail.try_emplace(0).first->second.label = label;
|
accts.emplace_back().detail.try_emplace(0).first->second.label = label;
|
||||||
queue_work([this, index] () { return data_->register_subaccount(index); });
|
queue_work([this, index] () { return data_->register_subaccount(index); });
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{ set_critical(e); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t wallet::numSubaddressAccounts() const
|
std::size_t wallet::numSubaddressAccounts() const
|
||||||
@@ -1169,7 +1139,10 @@ namespace lwsf { namespace internal
|
|||||||
auto& acct = data_->primary.subaccounts[accountIndex];
|
auto& acct = data_->primary.subaccounts[accountIndex];
|
||||||
if (addressIndex <= acct.last)
|
if (addressIndex <= acct.last)
|
||||||
{
|
{
|
||||||
acct.detail.try_emplace(addressIndex).first->second.label = label;
|
if (!addressIndex || !label.empty())
|
||||||
|
acct.detail.try_emplace(addressIndex).first->second.label = label;
|
||||||
|
else
|
||||||
|
acct.detail.erase(addressIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -63,7 +63,7 @@ namespace internal
|
|||||||
std::string language_;
|
std::string language_;
|
||||||
std::deque<std::function<std::error_code()>> work_queue_;
|
std::deque<std::function<std::error_code()>> work_queue_;
|
||||||
mutable std::string exception_error_;
|
mutable std::string exception_error_;
|
||||||
mutable std::error_code status_;
|
mutable std::error_code error_;
|
||||||
boost::thread thread_;
|
boost::thread thread_;
|
||||||
const std::uint64_t iterations_;
|
const std::uint64_t iterations_;
|
||||||
std::chrono::milliseconds refresh_interval_;
|
std::chrono::milliseconds refresh_interval_;
|
||||||
@@ -75,7 +75,7 @@ namespace internal
|
|||||||
state thread_state_;
|
state thread_state_;
|
||||||
bool mandatory_refresh_;
|
bool mandatory_refresh_;
|
||||||
|
|
||||||
bool set_error(std::error_code status, bool update_iff_error) const;
|
bool set_error(std::error_code status, bool clear = false) const;
|
||||||
void set_critical(const std::exception& e) const;
|
void set_critical(const std::exception& e) const;
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
|
|||||||
Reference in New Issue
Block a user