Skip to content

Commit

Permalink
Check bank capitalization (#11927)
Browse files Browse the repository at this point in the history
* Check bank capitalization

* Simplify and unify capitalization calculation

* Improve and add tests

* Avoid overflow and inhibit automatic restart

* Fix test

* Tweak checked sum for cap. and add tests

* Fix broken build after merge conflicts..

* Rename to ClusterType

* Rename confusing method

* Clarify comment

* Verify cap. in rent and inflation tests

Co-authored-by: Stephen Akridge <[email protected]>
  • Loading branch information
ryoqun and sakridge authored Sep 11, 2020
1 parent f276656 commit de4a613
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 99 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions accounts-bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ solana-logger = { path = "../logger", version = "1.4.0" }
solana-runtime = { path = "../runtime", version = "1.4.0" }
solana-measure = { path = "../measure", version = "1.4.0" }
solana-sdk = { path = "../sdk", version = "1.4.0" }
solana-version = { path = "../version", version = "1.4.0" }
rand = "0.7.0"
clap = "2.33.1"
crossbeam-channel = "0.4"
Expand Down
14 changes: 8 additions & 6 deletions accounts-bench/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use clap::{value_t, App, Arg};
use clap::{crate_description, crate_name, value_t, App, Arg};
use rayon::prelude::*;
use solana_measure::measure::Measure;
use solana_runtime::{
accounts::{create_test_accounts, update_accounts, Accounts},
accounts_index::Ancestors,
};
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
use std::env;
use std::fs;
use std::path::PathBuf;

fn main() {
solana_logger::setup();

let matches = App::new("crate")
.about("about")
.version("version")
let matches = App::new(crate_name!())
.about(crate_description!())
.version(solana_version::version!())
.arg(
Arg::with_name("num_slots")
.long("num_slots")
Expand Down Expand Up @@ -50,7 +51,8 @@ fn main() {
let clean = matches.is_present("clean");
println!("clean: {:?}", clean);

let path = PathBuf::from("farf/accounts-bench");
let path = PathBuf::from(env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_owned()))
.join("accounts-bench");
if fs::remove_dir_all(path.clone()).is_err() {
println!("Warning: Couldn't remove {:?}", path);
}
Expand Down Expand Up @@ -96,7 +98,7 @@ fn main() {
} else {
let mut pubkeys: Vec<Pubkey> = vec![];
let mut time = Measure::start("hash");
let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors);
let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors).0;
time.stop();
println!("hash: {} {}", hash, time);
create_test_accounts(&accounts, &mut pubkeys, 1, 0);
Expand Down
2 changes: 1 addition & 1 deletion core/src/non_circulating_supply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn calculate_non_circulating_supply(bank: &Arc<Bank>) -> NonCirculatingSuppl
let withdraw_authority_list = withdraw_authority();

let clock = bank.clock();
let stake_accounts = bank.get_program_accounts(Some(&solana_stake_program::id()));
let stake_accounts = bank.get_program_accounts(&solana_stake_program::id());
for (pubkey, account) in stake_accounts.iter() {
let stake_account = StakeState::from(&account).unwrap_or_default();
match stake_account {
Expand Down
2 changes: 1 addition & 1 deletion core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1291,7 +1291,7 @@ fn get_filtered_program_accounts(
program_id: &Pubkey,
filters: Vec<RpcFilterType>,
) -> impl Iterator<Item = (Pubkey, Account)> {
bank.get_program_accounts(Some(&program_id))
bank.get_program_accounts(&program_id)
.into_iter()
.filter(move |(_, account)| {
filters.iter().all(|filter_type| match filter_type {
Expand Down
17 changes: 4 additions & 13 deletions ledger-tool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use solana_vote_program::{
};
use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
convert::{TryFrom, TryInto},
convert::TryInto,
ffi::OsStr,
fs::{self, File},
io::{self, stdout, BufRead, BufReader, Write},
Expand Down Expand Up @@ -708,16 +708,7 @@ fn open_genesis_config_by(ledger_path: &Path, matches: &ArgMatches<'_>) -> Genes
}

fn assert_capitalization(bank: &Bank) {
let calculated_capitalization = bank.calculate_capitalization();
assert_eq!(
bank.capitalization(),
calculated_capitalization,
"Capitalization mismatch!?: +/-{}",
Sol(u64::try_from(
(i128::from(calculated_capitalization) - i128::from(bank.capitalization())).abs()
)
.unwrap()),
);
assert!(bank.calculate_and_verify_capitalization());
}

#[allow(clippy::cognitive_complexity)]
Expand Down Expand Up @@ -1695,7 +1686,7 @@ fn main() {

if remove_stake_accounts {
for (address, mut account) in bank
.get_program_accounts(Some(&solana_stake_program::id()))
.get_program_accounts(&solana_stake_program::id())
.into_iter()
{
account.lamports = 0;
Expand Down Expand Up @@ -1723,7 +1714,7 @@ fn main() {

// Delete existing vote accounts
for (address, mut account) in bank
.get_program_accounts(Some(&solana_vote_program::id()))
.get_program_accounts(&solana_vote_program::id())
.into_iter()
{
account.lamports = 0;
Expand Down
10 changes: 10 additions & 0 deletions ledger/src/blockstore_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ pub enum BlockstoreProcessorError {

#[error("invalid hard fork")]
InvalidHardFork(Slot),

#[error("root bank with mismatched capitalization at {0}")]
RootBankWithMismatchedCapitalization(Slot),
}

/// Callback for accessing bank state while processing the blockstore
Expand Down Expand Up @@ -481,6 +484,13 @@ fn do_process_blockstore_from_root(
);
assert!(bank_forks.active_banks().is_empty());

// We might be promptly restarted after bad capitalization was detected while creating newer snapshot.
// In that case, we're most likely restored from the last good snapshot and replayed up to this root.
// So again check here for the bad capitalization to avoid to continue until the next snapshot creation.
if !bank_forks.root_bank().calculate_and_verify_capitalization() {
return Err(BlockstoreProcessorError::RootBankWithMismatchedCapitalization(root));
}

Ok((bank_forks, leader_schedule_cache))
}

Expand Down
8 changes: 5 additions & 3 deletions runtime/benches/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ fn test_accounts_hash_bank_hash(bencher: &mut Bencher) {
&ClusterType::Development,
);
let mut pubkeys: Vec<Pubkey> = vec![];
create_test_accounts(&accounts, &mut pubkeys, 60000, 0);
let num_accounts = 60_000;
let slot = 0;
create_test_accounts(&accounts, &mut pubkeys, num_accounts, slot);
let ancestors = vec![(0, 0)].into_iter().collect();
accounts.accounts_db.update_accounts_hash(0, &ancestors);
bencher.iter(|| assert!(accounts.verify_bank_hash(0, &ancestors)));
let (_, total_lamports) = accounts.accounts_db.update_accounts_hash(0, &ancestors);
bencher.iter(|| assert!(accounts.verify_bank_hash_and_lamports(0, &ancestors, total_lamports)));
}

#[bench]
Expand Down
31 changes: 27 additions & 4 deletions runtime/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,32 @@ impl Accounts {
accounts_balances
}

pub fn calculate_capitalization(&self, ancestors: &Ancestors) -> u64 {
let balances = self
.load_all(ancestors)
.into_iter()
.map(|(_pubkey, account, _slot)| {
AccountsDB::account_balance_for_capitalization(
account.lamports,
&account.owner,
account.executable,
)
});

AccountsDB::checked_sum_for_capitalization(balances)
}

#[must_use]
pub fn verify_bank_hash(&self, slot: Slot, ancestors: &Ancestors) -> bool {
if let Err(err) = self.accounts_db.verify_bank_hash(slot, ancestors) {
pub fn verify_bank_hash_and_lamports(
&self,
slot: Slot,
ancestors: &Ancestors,
total_lamports: u64,
) -> bool {
if let Err(err) =
self.accounts_db
.verify_bank_hash_and_lamports(slot, ancestors, total_lamports)
{
warn!("verify_bank_hash failed: {:?}", err);
false
} else {
Expand Down Expand Up @@ -460,13 +483,13 @@ impl Accounts {
pub fn load_by_program(
&self,
ancestors: &Ancestors,
program_id: Option<&Pubkey>,
program_id: &Pubkey,
) -> Vec<(Pubkey, Account)> {
self.accounts_db.scan_accounts(
ancestors,
|collector: &mut Vec<(Pubkey, Account)>, some_account_tuple| {
Self::load_while_filtering(collector, some_account_tuple, |account| {
program_id.is_none() || Some(&account.owner) == program_id
account.owner == *program_id
})
},
)
Expand Down
Loading

0 comments on commit de4a613

Please sign in to comment.