Ledger balance consistency checks

This commit is contained in:
Piotr Wójcik 2025-06-04 18:34:15 +02:00
commit e3d998dcda
6 changed files with 92 additions and 41 deletions

View file

@ -1373,7 +1373,7 @@ int main (int argc, char * const * argv)
auto node_flags = nano::inactive_node_flag_defaults ();
nano::update_flags (node_flags, vm);
node_flags.generate_cache.enable_all ();
node_flags.generate_cache = nano::generate_cache_flags::all_enabled ();
nano::inactive_node inactive_node_l (data_path, node_flags);
nano::node_rpc_config config;

View file

@ -19,10 +19,7 @@ nano::node_flags const & nano::inactive_node_flag_defaults ()
static nano::node_flags node_flags;
node_flags.inactive_node = true;
node_flags.read_only = true;
node_flags.generate_cache.reps = false;
node_flags.generate_cache.cemented_count = false;
node_flags.generate_cache.unchecked_count = false;
node_flags.generate_cache.account_count = false;
node_flags.generate_cache = nano::generate_cache_flags::all_disabled ();
node_flags.disable_bootstrap_listener = true;
node_flags.disable_tcp_realtime = true;
return node_flags;

View file

@ -22,7 +22,6 @@ add_library(
common.cpp
fwd.hpp
generate_cache_flags.hpp
generate_cache_flags.cpp
ledger.hpp
ledger.cpp
ledger_set_any.hpp

View file

@ -1,9 +0,0 @@
#include <nano/secure/generate_cache_flags.hpp>
void nano::generate_cache_flags::enable_all ()
{
reps = true;
cemented_count = true;
unchecked_count = true;
account_count = true;
}

View file

@ -7,24 +7,29 @@ namespace nano
class generate_cache_flags
{
public:
bool reps = true;
bool cemented_count = true;
bool unchecked_count = true;
bool account_count = true;
bool block_count = true;
void enable_all ();
bool reps{ true };
bool cemented_count{ true };
bool unchecked_count{ true };
bool account_count{ true };
bool block_count{ true };
bool consistency_check{ true };
public:
static generate_cache_flags all_enabled ()
{
return {};
}
static generate_cache_flags all_disabled ()
{
generate_cache_flags flags;
flags.reps = false;
flags.cemented_count = false;
flags.unchecked_count = false;
flags.account_count = false;
flags.block_count = false;
return flags;
return {
.reps = false,
.cemented_count = false,
.unchecked_count = false,
.account_count = false,
.block_count = false,
.consistency_check = false,
};
}
};
}

View file

@ -790,6 +790,8 @@ void nano::ledger::initialize (nano::generate_cache_flags const & generate_cache
if (generate_cache_flags.account_count || generate_cache_flags.block_count)
{
logger.debug (nano::log::type::ledger, "Generating block count cache...");
store.account.for_each_par (
[this] (store::read_transaction const &, auto i, auto n) {
uint64_t block_count_l{ 0 };
@ -803,10 +805,40 @@ void nano::ledger::initialize (nano::generate_cache_flags const & generate_cache
this->cache.block_count += block_count_l;
this->cache.account_count += account_count_l;
});
logger.debug (nano::log::type::ledger, "Block count cache generated");
}
if (generate_cache_flags.cemented_count)
{
logger.debug (nano::log::type::ledger, "Generating cemented count cache...");
store.confirmation_height.for_each_par (
[this] (store::read_transaction const &, auto i, auto n) {
uint64_t cemented_count_l (0);
for (; i != n; ++i)
{
cemented_count_l += i->second.height;
}
this->cache.cemented_count += cemented_count_l;
});
logger.debug (nano::log::type::ledger, "Cemented count cache generated");
}
{
logger.debug (nano::log::type::ledger, "Generating pruned count cache...");
auto transaction = store.tx_begin_read ();
cache.pruned_count = store.pruned.count (transaction);
logger.debug (nano::log::type::ledger, "Pruned count cache generated");
}
if (generate_cache_flags.reps)
{
logger.debug (nano::log::type::ledger, "Generating representative weights cache...");
store.rep_weight.for_each_par (
[this] (store::read_transaction const &, auto i, auto n) {
nano::rep_weights rep_weights_l{ this->store.rep_weight };
@ -828,24 +860,50 @@ void nano::ledger::initialize (nano::generate_cache_flags const & generate_cache
});
rep_weights.verify_consistency ();
logger.debug (nano::log::type::ledger, "Representative weights cache generated");
}
if (generate_cache_flags.cemented_count)
nano::uint128_t account_balance, pending_balance;
if (generate_cache_flags.consistency_check)
{
store.confirmation_height.for_each_par (
[this] (store::read_transaction const &, auto i, auto n) {
uint64_t cemented_count_l (0);
logger.debug (nano::log::type::ledger, "Verifying ledger balance consistency...");
// Verify sum of all account and pending balances
nano::locked<nano::uint128_t> accounts_balance_s{ 0 };
nano::locked<nano::uint128_t> pending_balance_s{ 0 };
store.account.for_each_par (
[&] (store::read_transaction const &, auto i, auto n) {
nano::uint128_t balance_l{ 0 };
for (; i != n; ++i)
{
cemented_count_l += i->second.height;
nano::account_info const & info = i->second;
balance_l += info.balance.number ();
}
this->cache.cemented_count += cemented_count_l;
(*accounts_balance_s.lock ()) += balance_l;
});
}
{
auto transaction (store.tx_begin_read ());
cache.pruned_count = store.pruned.count (transaction);
store.pending.for_each_par (
[&] (store::read_transaction const &, auto i, auto n) {
nano::uint128_t balance_l{ 0 };
for (; i != n; ++i)
{
nano::pending_info const & info = i->second;
balance_l += info.amount.number ();
}
(*pending_balance_s.lock ()) += balance_l;
});
account_balance = *accounts_balance_s.lock ();
pending_balance = *pending_balance_s.lock ();
release_assert (account_balance + pending_balance == constants.genesis_amount, "ledger corruption detected: account and pending balances do not match genesis amount", to_string (account_balance) + " + " + to_string (pending_balance) + " != " + to_string (constants.genesis_amount));
release_assert (account_balance == rep_weights.get_weight_committed (), "ledger corruption detected: account balance does not match committed representative weights", to_string (account_balance) + " != " + to_string (rep_weights.get_weight_committed ()));
release_assert (pending_balance == rep_weights.get_weight_unused (), "ledger corruption detected: pending balance does not match unused representative weights", to_string (pending_balance) + " != " + to_string (rep_weights.get_weight_unused ()));
logger.debug (nano::log::type::ledger, "Ledger balance consistency verified");
}
logger.info (nano::log::type::ledger, "Block count: {:>11}", cache.block_count.load ());
@ -853,6 +911,9 @@ void nano::ledger::initialize (nano::generate_cache_flags const & generate_cache
logger.info (nano::log::type::ledger, "Account count: {:>11}", cache.account_count.load ());
logger.info (nano::log::type::ledger, "Pruned count: {:>11}", cache.pruned_count.load ());
logger.info (nano::log::type::ledger, "Representative count: {:>5}", rep_weights.size ());
logger.info (nano::log::type::ledger, "Total balance: {} | pending: {}",
nano::uint128_union{ account_balance }.format_balance (nano::nano_ratio, 0, true),
nano::uint128_union{ pending_balance }.format_balance (nano::nano_ratio, 0, true));
logger.info (nano::log::type::ledger, "Weight commited: {} | unused: {}",
nano::uint128_union{ rep_weights.get_weight_committed () }.format_balance (nano::nano_ratio, 0, true),
nano::uint128_union{ rep_weights.get_weight_unused () }.format_balance (nano::nano_ratio, 0, true));
@ -1267,7 +1328,6 @@ std::optional<nano::account> nano::ledger::linked_account (secure::transaction c
{
return any.block_account (transaction, block.source ());
}
return std::nullopt;
}
@ -1602,7 +1662,6 @@ nano::epoch nano::ledger::version (nano::block const & block)
{
return block.sideband ().details.epoch;
}
return nano::epoch::epoch_0;
}