forked from such-gitea/wownero-lws
Basic "chain hardening" for slightly untrusted daemons (#93)
This commit is contained in:
committed by
Lee *!* Clagett
parent
db66d410cd
commit
351ccaa872
348
src/scanner.cpp
348
src/scanner.cpp
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2018-2023, The Monero Project
|
||||
// Copyright (c) 2018-2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
@@ -42,13 +42,16 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/error.h" // monero/src
|
||||
#include "config.h"
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "crypto/wallet/crypto.h" // monero/src
|
||||
#include "cryptonote_basic/cryptonote_basic.h" // monero/src
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h" // monero/src
|
||||
#include "db/account.h"
|
||||
#include "db/data.h"
|
||||
#include "cryptonote_basic/difficulty.h" // monero/src
|
||||
#include "error.h"
|
||||
#include "hardforks/hardforks.h" // monero/src
|
||||
#include "misc_log_ex.h" // monero/contrib/epee/include
|
||||
#include "net/net_parse_helpers.h"
|
||||
#include "net/net_ssl.h" // monero/contrib/epee/include
|
||||
@@ -57,6 +60,7 @@
|
||||
#include "rpc/json.h"
|
||||
#include "rpc/message_data_structs.h" // monero/src
|
||||
#include "rpc/webhook.h"
|
||||
#include "util/blocks.h"
|
||||
#include "util/source_location.h"
|
||||
#include "util/transactions.h"
|
||||
|
||||
@@ -98,6 +102,7 @@ namespace lws
|
||||
{
|
||||
net::ssl_verification_t webhook_verify;
|
||||
bool enable_subaddresses;
|
||||
bool untrusted_daemon;
|
||||
};
|
||||
|
||||
struct thread_data
|
||||
@@ -174,6 +179,43 @@ namespace lws
|
||||
rpc::send_webhook(client, events, "json-full-payment_hook:", "msgpack-full-payment_hook:", std::chrono::seconds{5}, verify_mode);
|
||||
}
|
||||
|
||||
std::size_t get_target_time(db::block_id height)
|
||||
{
|
||||
const hardfork_t* fork = nullptr;
|
||||
switch (config::network)
|
||||
{
|
||||
case cryptonote::network_type::MAINNET:
|
||||
if (num_mainnet_hard_forks < 2)
|
||||
MONERO_THROW(error::bad_blockchain, "expected more mainnet forks");
|
||||
fork = mainnet_hard_forks;
|
||||
break;
|
||||
case cryptonote::network_type::TESTNET:
|
||||
if (num_testnet_hard_forks < 2)
|
||||
MONERO_THROW(error::bad_blockchain, "expected more testnet forks");
|
||||
fork = testnet_hard_forks;
|
||||
break;
|
||||
case cryptonote::network_type::STAGENET:
|
||||
if (num_stagenet_hard_forks < 2)
|
||||
MONERO_THROW(error::bad_blockchain, "expected more stagenet forks");
|
||||
fork = stagenet_hard_forks;
|
||||
break;
|
||||
default:
|
||||
MONERO_THROW(error::bad_blockchain, "chain type not support with full sync");
|
||||
}
|
||||
// this is hardfork version 2
|
||||
return height < db::block_id(fork[1].height) ?
|
||||
DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
|
||||
}
|
||||
|
||||
//! For difficulty vectors only
|
||||
template<typename T>
|
||||
void update_window(T& vec)
|
||||
{
|
||||
// should only have one to pop each time
|
||||
while (DIFFICULTY_BLOCKS_COUNT < vec.size())
|
||||
vec.erase(vec.begin());
|
||||
};
|
||||
|
||||
struct by_height
|
||||
{
|
||||
bool operator()(account const& left, account const& right) const noexcept
|
||||
@@ -572,7 +614,7 @@ namespace lws
|
||||
MINFO("Updated exchange rates: " << *(*new_rates));
|
||||
}
|
||||
|
||||
void scan_loop(thread_sync& self, std::shared_ptr<thread_data> data) noexcept
|
||||
void scan_loop(thread_sync& self, std::shared_ptr<thread_data> data, const bool untrusted_daemon, const bool leader_thread) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -602,17 +644,22 @@ namespace lws
|
||||
cryptonote::rpc::GetBlocksFast::Request req{};
|
||||
req.start_height = std::uint64_t(users.begin()->scan_height());
|
||||
req.start_height = std::max(std::uint64_t(1), req.start_height);
|
||||
req.prune = true;
|
||||
req.prune = !untrusted_daemon;
|
||||
|
||||
epee::byte_slice block_request = rpc::client::make_message("get_blocks_fast", req);
|
||||
if (!send(client, block_request.clone()))
|
||||
return;
|
||||
|
||||
std::vector<crypto::hash> blockchain{};
|
||||
std::vector<db::pow_sync> new_pow{};
|
||||
db::pow_window pow_window{};
|
||||
|
||||
const db::block_info last_checkpoint = db::storage::get_last_checkpoint();
|
||||
const db::block_id last_pow = MONERO_UNWRAP(MONERO_UNWRAP(disk.start_read()).get_last_pow_block()).id;
|
||||
while (!self.update && scanner::is_running())
|
||||
{
|
||||
blockchain.clear();
|
||||
new_pow.clear();
|
||||
|
||||
auto resp = client.get_message(block_rpc_timeout);
|
||||
if (!resp)
|
||||
@@ -691,6 +738,8 @@ namespace lws
|
||||
throw std::runtime_error{"Bad daemon response - need same number of blocks and indices"};
|
||||
|
||||
blockchain.push_back(cryptonote::get_block_hash(fetched->blocks.front().block));
|
||||
if (untrusted_daemon)
|
||||
new_pow.push_back(db::pow_sync{fetched->blocks.front().block.timestamp});
|
||||
|
||||
auto blocks = epee::to_span(fetched->blocks);
|
||||
auto indices = epee::to_span(fetched->output_indices);
|
||||
@@ -704,7 +753,16 @@ namespace lws
|
||||
else
|
||||
fetched->start_height = 0;
|
||||
|
||||
if (untrusted_daemon)
|
||||
{
|
||||
pow_window = MONERO_UNWRAP(
|
||||
MONERO_UNWRAP(disk.start_read()).get_pow_window(db::block_id(fetched->start_height))
|
||||
);
|
||||
}
|
||||
|
||||
subaddress_reader reader{disk, opts.enable_subaddresses};
|
||||
db::block_difficulty::unsigned_int diff{};
|
||||
const db::block_id initial_height = db::block_id(fetched->start_height);
|
||||
for (auto block_data : boost::combine(blocks, indices))
|
||||
{
|
||||
++(fetched->start_height);
|
||||
@@ -733,12 +791,48 @@ namespace lws
|
||||
reader
|
||||
);
|
||||
|
||||
if (untrusted_daemon)
|
||||
{
|
||||
if (block.prev_id != blockchain.back())
|
||||
MONERO_THROW(error::bad_blockchain, "A blocks prev_id does not match");
|
||||
|
||||
update_window(pow_window.pow_timestamps);
|
||||
update_window(pow_window.cumulative_diffs);
|
||||
|
||||
while (BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW < pow_window.median_timestamps.size())
|
||||
pow_window.median_timestamps.erase(pow_window.median_timestamps.begin());
|
||||
|
||||
// longhash takes a while, check is_running
|
||||
if (!scanner::is_running())
|
||||
return;
|
||||
|
||||
diff = cryptonote::next_difficulty(pow_window.pow_timestamps, pow_window.cumulative_diffs, get_target_time(db::block_id(fetched->start_height)));
|
||||
|
||||
// skip POW hashing if done previously
|
||||
if (last_pow < db::block_id(fetched->start_height))
|
||||
{
|
||||
if (!verify_timestamp(block.timestamp, pow_window.median_timestamps))
|
||||
MONERO_THROW(error::bad_blockchain, "Block failed timestamp check - possible chain forgery");
|
||||
|
||||
const crypto::hash pow =
|
||||
get_block_longhash(get_block_hashing_blob(block), db::block_id(fetched->start_height), block.major_version, disk, initial_height, epee::to_span(blockchain));
|
||||
if (!cryptonote::check_hash(pow, diff))
|
||||
MONERO_THROW(error::bad_blockchain, "Block had too low difficulty");
|
||||
}
|
||||
}
|
||||
|
||||
indices.remove_prefix(1);
|
||||
if (txes.size() != indices.size())
|
||||
throw std::runtime_error{"Bad daemon respnse - need same number of txes and indices"};
|
||||
|
||||
for (auto tx_data : boost::combine(block.tx_hashes, txes, indices))
|
||||
{
|
||||
if (untrusted_daemon)
|
||||
{
|
||||
if (cryptonote::get_transaction_hash(boost::get<1>(tx_data)) != boost::get<0>(tx_data))
|
||||
MONERO_THROW(error::bad_blockchain, "Hash of transaction does not match hash in block");
|
||||
}
|
||||
|
||||
scan_transaction(
|
||||
epee::to_mut_span(users),
|
||||
db::block_id(fetched->start_height),
|
||||
@@ -750,12 +844,24 @@ namespace lws
|
||||
);
|
||||
}
|
||||
|
||||
if (untrusted_daemon)
|
||||
{
|
||||
const auto last_difficulty =
|
||||
pow_window.cumulative_diffs.empty() ?
|
||||
db::block_difficulty::unsigned_int(0) : pow_window.cumulative_diffs.back();
|
||||
|
||||
pow_window.pow_timestamps.push_back(block.timestamp);
|
||||
pow_window.median_timestamps.push_back(block.timestamp);
|
||||
pow_window.cumulative_diffs.push_back(diff + last_difficulty);
|
||||
new_pow.push_back(db::pow_sync{block.timestamp});
|
||||
new_pow.back().cumulative_diff.set_difficulty(pow_window.cumulative_diffs.back());
|
||||
}
|
||||
blockchain.push_back(cryptonote::get_block_hash(block));
|
||||
} // for each block
|
||||
|
||||
reader.reader = std::error_code{common_error::kInvalidArgument}; // cleanup reader before next write
|
||||
auto updated = disk.update(
|
||||
users.front().scan_height(), epee::to_span(blockchain), epee::to_span(users)
|
||||
users.front().scan_height(), epee::to_span(blockchain), epee::to_span(users), epee::to_span(new_pow)
|
||||
);
|
||||
if (!updated)
|
||||
{
|
||||
@@ -767,6 +873,11 @@ namespace lws
|
||||
MONERO_THROW(updated.error(), "Failed to update accounts on disk");
|
||||
}
|
||||
|
||||
if (untrusted_daemon && leader_thread && fetched->start_height % 4 == 0 && last_pow < db::block_id(fetched->start_height))
|
||||
{
|
||||
MINFO("On chain with hash " << blockchain.back() << " and difficulty " << diff << " at height " << fetched->start_height);
|
||||
}
|
||||
|
||||
MINFO("Processed " << blocks.size() << " block(s) against " << users.size() << " account(s)");
|
||||
send_payment_hook(client, epee::to_span(updated->second), opts.webhook_verify);
|
||||
if (updated->first != users.size())
|
||||
@@ -844,6 +955,7 @@ namespace lws
|
||||
|
||||
MINFO("Starting scan loops on " << std::min(thread_count, users.size()) << " thread(s) with " << users.size() << " account(s)");
|
||||
|
||||
bool leader_thread = true;
|
||||
while (!users.empty() && --thread_count)
|
||||
{
|
||||
const std::size_t per_thread = std::max(std::size_t(1), users.size() / (thread_count + 1));
|
||||
@@ -859,7 +971,8 @@ namespace lws
|
||||
auto data = std::make_shared<thread_data>(
|
||||
std::move(client), disk.clone(), std::move(thread_users), opts
|
||||
);
|
||||
threads.emplace_back(attrs, std::bind(&scan_loop, std::ref(self), std::move(data)));
|
||||
threads.emplace_back(attrs, std::bind(&scan_loop, std::ref(self), std::move(data), opts.untrusted_daemon, leader_thread));
|
||||
leader_thread = false;
|
||||
}
|
||||
|
||||
if (!users.empty())
|
||||
@@ -870,7 +983,7 @@ namespace lws
|
||||
auto data = std::make_shared<thread_data>(
|
||||
std::move(client), disk.clone(), std::move(users), opts
|
||||
);
|
||||
threads.emplace_back(attrs, std::bind(&scan_loop, std::ref(self), std::move(data)));
|
||||
threads.emplace_back(attrs, std::bind(&scan_loop, std::ref(self), std::move(data), opts.untrusted_daemon, false /*leader thread*/));
|
||||
}
|
||||
|
||||
auto last_check = std::chrono::steady_clock::now();
|
||||
@@ -931,36 +1044,20 @@ namespace lws
|
||||
accounts_cur = current_users.give_cursor();
|
||||
} // while scanning
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
expect<rpc::client> scanner::sync(db::storage disk, rpc::client client)
|
||||
{
|
||||
using get_hashes = cryptonote::rpc::GetHashesFast;
|
||||
|
||||
MINFO("Starting blockchain sync with daemon");
|
||||
|
||||
get_hashes::Request req{};
|
||||
req.start_height = 0;
|
||||
expect<std::list<crypto::hash>> get_chain_sync(expect<db::storage_reader> reader)
|
||||
{
|
||||
auto reader = disk.start_read();
|
||||
if (!reader)
|
||||
return reader.error();
|
||||
|
||||
auto chain = reader->get_chain_sync();
|
||||
if (!chain)
|
||||
return chain.error();
|
||||
|
||||
req.known_hashes = std::move(*chain);
|
||||
return reader->get_chain_sync();
|
||||
}
|
||||
|
||||
for (;;)
|
||||
template<typename R, typename Q>
|
||||
expect<typename R::response> fetch_chain(rpc::client& client, const char* endpoint, const Q& req)
|
||||
{
|
||||
if (req.known_hashes.empty())
|
||||
return {lws::error::bad_blockchain};
|
||||
|
||||
expect<void> sent{lws::error::daemon_timeout};
|
||||
|
||||
epee::byte_slice msg = rpc::client::make_message("get_hashes_fast", req);
|
||||
epee::byte_slice msg = rpc::client::make_message(endpoint, req);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
while (!(sent = client.send(std::move(msg), std::chrono::seconds{1})))
|
||||
@@ -975,10 +1072,10 @@ namespace lws
|
||||
return sent.error();
|
||||
}
|
||||
|
||||
expect<get_hashes::Response> resp{lws::error::daemon_timeout};
|
||||
expect<std::string> resp{lws::error::daemon_timeout};
|
||||
start = std::chrono::steady_clock::now();
|
||||
|
||||
while (!(resp = client.receive<get_hashes::Response>(std::chrono::seconds{1}, MLWS_CURRENT_LOCATION)))
|
||||
while (!(resp = client.get_message(std::chrono::seconds{1})))
|
||||
{
|
||||
if (!scanner::is_running())
|
||||
return {lws::error::signal_abort_process};
|
||||
@@ -989,29 +1086,180 @@ namespace lws
|
||||
if (!resp.matches(std::errc::timed_out))
|
||||
return resp.error();
|
||||
}
|
||||
|
||||
//
|
||||
// Exit loop if it appears we have synced to top of chain
|
||||
//
|
||||
if (resp->hashes.size() <= 1 || resp->hashes.back() == req.known_hashes.front())
|
||||
return {std::move(client)};
|
||||
|
||||
MONERO_CHECK(disk.sync_chain(db::block_id(resp->start_height), epee::to_span(resp->hashes)));
|
||||
|
||||
req.known_hashes.erase(req.known_hashes.begin(), --(req.known_hashes.end()));
|
||||
for (std::size_t num = 0; num < 10; ++num)
|
||||
{
|
||||
if (resp->hashes.empty())
|
||||
break;
|
||||
|
||||
req.known_hashes.insert(--(req.known_hashes.end()), resp->hashes.back());
|
||||
}
|
||||
return rpc::parse_json_response<R>(std::move(*resp));
|
||||
}
|
||||
|
||||
return {std::move(client)};
|
||||
// does not validate blockchain hashes
|
||||
expect<rpc::client> sync_quick(db::storage disk, rpc::client client)
|
||||
{
|
||||
MINFO("Starting blockchain sync with daemon");
|
||||
|
||||
cryptonote::rpc::GetHashesFast::Request req{};
|
||||
req.start_height = 0;
|
||||
req.known_hashes = MONERO_UNWRAP(MONERO_UNWRAP(disk.start_read()).get_chain_sync());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (req.known_hashes.empty())
|
||||
return {lws::error::bad_blockchain};
|
||||
|
||||
auto resp = fetch_chain<rpc::get_hashes_fast>(client, "get_hashes_fast", req);
|
||||
if (!resp)
|
||||
return resp.error();
|
||||
|
||||
//
|
||||
// exit loop if it appears we have synced to top of chain
|
||||
//
|
||||
if (resp->hashes.size() <= 1 || resp->hashes.back() == req.known_hashes.front())
|
||||
return {std::move(client)};
|
||||
|
||||
MONERO_CHECK(disk.sync_chain(db::block_id(resp->start_height), epee::to_span(resp->hashes)));
|
||||
|
||||
req.known_hashes.erase(req.known_hashes.begin(), --(req.known_hashes.end()));
|
||||
for (std::size_t num = 0; num < 10; ++num)
|
||||
{
|
||||
if (resp->hashes.empty())
|
||||
break;
|
||||
|
||||
req.known_hashes.insert(--(req.known_hashes.end()), resp->hashes.back());
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(client)};
|
||||
}
|
||||
|
||||
// validates blockchain hashes
|
||||
expect<rpc::client> sync_full(db::storage disk, rpc::client client)
|
||||
{
|
||||
MINFO("Starting blockchain sync with daemon");
|
||||
|
||||
cryptonote::rpc::GetBlocksFast::Request req{};
|
||||
req.start_height = 0;
|
||||
req.block_ids = MONERO_UNWRAP(MONERO_UNWRAP(disk.start_read()).get_pow_sync());
|
||||
req.prune = true;
|
||||
|
||||
std::vector<crypto::hash> new_hashes{};
|
||||
std::vector<db::pow_sync> new_pow{};
|
||||
for (;;)
|
||||
{
|
||||
if (req.block_ids.empty())
|
||||
return {lws::error::bad_blockchain};
|
||||
|
||||
auto resp = fetch_chain<rpc::get_blocks_fast>(client, "get_blocks_fast", req);
|
||||
if (!resp)
|
||||
return resp.error();
|
||||
|
||||
if (resp->blocks.empty())
|
||||
return {error::bad_daemon_response};
|
||||
|
||||
crypto::hash hash{};
|
||||
if (!cryptonote::get_block_hash(resp->blocks.front().block, hash))
|
||||
return {lws::error::bad_blockchain};
|
||||
|
||||
//
|
||||
// exit loop if it appears we have synced to top of chain
|
||||
//
|
||||
const db::block_info last_checkpoint = db::storage::get_last_checkpoint();
|
||||
if (resp->blocks.size() <= 1)
|
||||
{
|
||||
// error if not past last checkpoint
|
||||
const auto expected_hash =
|
||||
MONERO_UNWRAP(disk.start_read()).get_block_hash(db::block_id(resp->start_height));
|
||||
if (!expected_hash || *expected_hash != hash || db::block_id(resp->start_height) < last_checkpoint.id)
|
||||
return {error::bad_daemon_response};
|
||||
return {std::move(client)};
|
||||
}
|
||||
|
||||
// genesis block must be present as last entry
|
||||
req.block_ids.erase(req.block_ids.begin(), --(req.block_ids.end()));
|
||||
|
||||
auto pow_window =
|
||||
MONERO_UNWRAP(MONERO_UNWRAP(disk.start_read()).get_pow_window(db::block_id(resp->start_height)));
|
||||
|
||||
// overlap check performed in db::storage::pow_sync
|
||||
new_hashes.clear();
|
||||
new_pow.clear();
|
||||
new_hashes.reserve(resp->blocks.size());
|
||||
new_pow.reserve(resp->blocks.size());
|
||||
new_hashes.push_back(hash);
|
||||
new_pow.push_back(db::pow_sync{resp->blocks.front().block.timestamp});
|
||||
|
||||
// skip overlap block
|
||||
db::block_difficulty::unsigned_int diff = 0;
|
||||
for (std::size_t i = 1; i < resp->blocks.size(); ++i)
|
||||
{
|
||||
const auto& block = resp->blocks[i].block;
|
||||
const db::block_id height = db::block_id(resp->start_height + i);
|
||||
|
||||
// important check, ensure we haven't deviated from chain
|
||||
if (block.prev_id != hash)
|
||||
return {lws::error::bad_blockchain};
|
||||
|
||||
// compute block id hash
|
||||
if (!cryptonote::get_block_hash(block, hash))
|
||||
return {lws::error::bad_blockchain};
|
||||
|
||||
req.block_ids.push_front(hash);
|
||||
update_window(pow_window.pow_timestamps);
|
||||
update_window(pow_window.cumulative_diffs);
|
||||
|
||||
while (BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW < pow_window.median_timestamps.size())
|
||||
pow_window.median_timestamps.erase(pow_window.median_timestamps.begin());
|
||||
|
||||
// longhash takes a while, check is_running
|
||||
if (!scanner::is_running())
|
||||
return {error::signal_abort_process};
|
||||
|
||||
diff = cryptonote::next_difficulty(pow_window.pow_timestamps, pow_window.cumulative_diffs, get_target_time(height));
|
||||
|
||||
// skip POW hashing when sync is within checkpoint
|
||||
// storage::sync_pow(...) currently verifies checkpoint hashes
|
||||
if (last_checkpoint.id < height)
|
||||
{
|
||||
if (!verify_timestamp(block.timestamp, pow_window.median_timestamps))
|
||||
{
|
||||
MERROR("Block failed timestamp check - possible chain forgery");
|
||||
return {error::bad_blockchain};
|
||||
}
|
||||
const crypto::hash pow =
|
||||
get_block_longhash(get_block_hashing_blob(block), height, block.major_version, disk, db::block_id(resp->start_height), epee::to_span(new_hashes));
|
||||
|
||||
if (!cryptonote::check_hash(pow, diff))
|
||||
{
|
||||
MERROR("Block " << std::uint64_t(height) << "had too low difficulty");
|
||||
return {error::bad_blockchain};
|
||||
}
|
||||
}
|
||||
|
||||
const auto last_difficulty =
|
||||
pow_window.cumulative_diffs.empty() ?
|
||||
db::block_difficulty::unsigned_int(0) : pow_window.cumulative_diffs.back();
|
||||
|
||||
pow_window.pow_timestamps.push_back(block.timestamp);
|
||||
pow_window.median_timestamps.push_back(block.timestamp);
|
||||
pow_window.cumulative_diffs.push_back(diff + last_difficulty);
|
||||
new_hashes.push_back(hash);
|
||||
new_pow.push_back(db::pow_sync{block.timestamp});
|
||||
new_pow.back().cumulative_diff.set_difficulty(pow_window.cumulative_diffs.back());
|
||||
} // for every tx in block
|
||||
|
||||
MONERO_CHECK(disk.sync_pow(db::block_id(resp->start_height), epee::to_span(new_hashes), epee::to_span(new_pow)));
|
||||
MINFO("Verified up to block " << (resp->start_height + new_hashes.size() - 1) << " with hash " << hash << " and difficulty " << diff);
|
||||
|
||||
} // for until sync
|
||||
|
||||
return {std::move(client)};
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
expect<rpc::client> scanner::sync(db::storage disk, rpc::client client, const bool untrusted_daemon)
|
||||
{
|
||||
if (untrusted_daemon)
|
||||
return sync_full(std::move(disk), std::move(client));
|
||||
return sync_quick(std::move(disk), std::move(client));
|
||||
}
|
||||
|
||||
void scanner::run(db::storage disk, rpc::context ctx, std::size_t thread_count, const epee::net_utils::ssl_verification_t webhook_verify, const bool enable_subaddresses)
|
||||
void scanner::run(db::storage disk, rpc::context ctx, std::size_t thread_count, const epee::net_utils::ssl_verification_t webhook_verify, const bool enable_subaddresses, const bool untrusted_daemon)
|
||||
{
|
||||
thread_count = std::max(std::size_t(1), thread_count);
|
||||
|
||||
@@ -1065,7 +1313,7 @@ namespace lws
|
||||
checked_wait(account_poll_interval - (std::chrono::steady_clock::now() - last));
|
||||
}
|
||||
else
|
||||
check_loop(disk.clone(), ctx, thread_count, std::move(users), std::move(active), options{webhook_verify, enable_subaddresses});
|
||||
check_loop(disk.clone(), ctx, thread_count, std::move(users), std::move(active), options{webhook_verify, enable_subaddresses, untrusted_daemon});
|
||||
|
||||
if (!scanner::is_running())
|
||||
return;
|
||||
@@ -1073,7 +1321,7 @@ namespace lws
|
||||
if (!client)
|
||||
client = MONERO_UNWRAP(ctx.connect());
|
||||
|
||||
expect<rpc::client> synced = sync(disk.clone(), std::move(client));
|
||||
expect<rpc::client> synced = sync(disk.clone(), std::move(client), untrusted_daemon);
|
||||
if (!synced)
|
||||
{
|
||||
if (!synced.matches(std::errc::timed_out))
|
||||
|
||||
Reference in New Issue
Block a user