wallet2: fix spurious reorg detection with untrusted nodes

When forced to deal with an untrusted node, a wallet will quantize
its current height to disguise the real height to the adversary, to
try and minimize the daemon's ability to distinguish returning
wallets.
Daemons will thus return more blocks than the wallet needs, starting
from earlier in the chain. These extra blocks will be disregarded
by the wallet, which had already scanned them.
However, for the purposes of reorg size detection, the wallet assumes
all blocks the daemon sends are different, which is only correct if
the wallet hasn't been coy, which is only the case for trusted
daemons (which you should use). This causes an issue when the size
of this "fake reorg" is above the sanity check threshold at which
the wallet refuses a reorg.
To fix this, the reorg size check is moved later on, when the reorg
is about to actually happen, after the wallet has checked which
blocks are actually different from the ones it expects.
This commit is contained in:
moneromooo-monero 2022-05-15 11:50:28 +00:00
parent 8349cfe4a6
commit fde7c96b5c
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3

View File

@ -2882,6 +2882,11 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
" (height " + std::to_string(start_height) + "), local block id at this height: " +
string_tools::pod_to_hex(m_blockchain[current_index]));
const uint64_t reorg_depth = m_blockchain.size() - current_index;
THROW_WALLET_EXCEPTION_IF(reorg_depth > m_max_reorg_depth, error::reorg_depth_error,
tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") +
std::to_string(reorg_depth));
detach_blockchain(current_index, output_tracker_cache);
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
}
@ -3532,15 +3537,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
first = false;
if (!next_blocks.empty())
{
const uint64_t expected_start_height = std::max(static_cast<uint64_t>(m_blockchain.size()), uint64_t(1)) - 1;
const uint64_t reorg_depth = expected_start_height - std::min(expected_start_height, next_blocks_start_height);
THROW_WALLET_EXCEPTION_IF(reorg_depth > m_max_reorg_depth, error::reorg_depth_error,
tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") +
std::to_string(reorg_depth));
}
// if we've got at least 10 blocks to refresh, assume we're starting
// a long refresh, and setup a tracking output cache if we need to
if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10)