parent
4dd0367136
commit
88ba8439fc
|
@ -4488,6 +4488,7 @@ dependencies = [
|
||||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -78,6 +78,7 @@ pub struct ValidatorConfig {
|
||||||
pub trusted_validators: Option<HashSet<Pubkey>>, // None = trust all
|
pub trusted_validators: Option<HashSet<Pubkey>>, // None = trust all
|
||||||
pub halt_on_trusted_validators_accounts_hash_mismatch: bool,
|
pub halt_on_trusted_validators_accounts_hash_mismatch: bool,
|
||||||
pub accounts_hash_fault_injection_slots: u64, // 0 = no fault injection
|
pub accounts_hash_fault_injection_slots: u64, // 0 = no fault injection
|
||||||
|
pub frozen_accounts: Vec<Pubkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ValidatorConfig {
|
impl Default for ValidatorConfig {
|
||||||
|
@ -102,6 +103,7 @@ impl Default for ValidatorConfig {
|
||||||
trusted_validators: None,
|
trusted_validators: None,
|
||||||
halt_on_trusted_validators_accounts_hash_mismatch: false,
|
halt_on_trusted_validators_accounts_hash_mismatch: false,
|
||||||
accounts_hash_fault_injection_slots: 0,
|
accounts_hash_fault_injection_slots: 0,
|
||||||
|
frozen_accounts: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,6 +596,7 @@ fn new_banks_from_blockstore(
|
||||||
poh_verify,
|
poh_verify,
|
||||||
dev_halt_at_slot: config.dev_halt_at_slot,
|
dev_halt_at_slot: config.dev_halt_at_slot,
|
||||||
new_hard_forks: config.new_hard_forks.clone(),
|
new_hard_forks: config.new_hard_forks.clone(),
|
||||||
|
frozen_accounts: config.frozen_accounts.clone(),
|
||||||
..blockstore_processor::ProcessOptions::default()
|
..blockstore_processor::ProcessOptions::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ mod tests {
|
||||||
let bank0 = Bank::new_with_paths(
|
let bank0 = Bank::new_with_paths(
|
||||||
&genesis_config_info.genesis_config,
|
&genesis_config_info.genesis_config,
|
||||||
vec![accounts_dir.path().to_path_buf()],
|
vec![accounts_dir.path().to_path_buf()],
|
||||||
|
&[],
|
||||||
);
|
);
|
||||||
bank0.freeze();
|
bank0.freeze();
|
||||||
let mut bank_forks = BankForks::new(0, bank0);
|
let mut bank_forks = BankForks::new(0, bank0);
|
||||||
|
@ -80,6 +81,7 @@ mod tests {
|
||||||
|
|
||||||
let deserialized_bank = snapshot_utils::bank_from_archive(
|
let deserialized_bank = snapshot_utils::bank_from_archive(
|
||||||
&account_paths,
|
&account_paths,
|
||||||
|
&[],
|
||||||
&old_bank_forks
|
&old_bank_forks
|
||||||
.snapshot_config
|
.snapshot_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -66,6 +66,7 @@ pub fn load(
|
||||||
|
|
||||||
let deserialized_bank = snapshot_utils::bank_from_archive(
|
let deserialized_bank = snapshot_utils::bank_from_archive(
|
||||||
&account_paths,
|
&account_paths,
|
||||||
|
&process_options.frozen_accounts,
|
||||||
&snapshot_config.snapshot_path,
|
&snapshot_config.snapshot_path,
|
||||||
&archive_filename,
|
&archive_filename,
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,6 +23,7 @@ use solana_sdk::{
|
||||||
clock::{Slot, MAX_RECENT_BLOCKHASHES},
|
clock::{Slot, MAX_RECENT_BLOCKHASHES},
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
pubkey::Pubkey,
|
||||||
signature::Keypair,
|
signature::Keypair,
|
||||||
timing::duration_as_ms,
|
timing::duration_as_ms,
|
||||||
transaction::{Result, Transaction, TransactionError},
|
transaction::{Result, Transaction, TransactionError},
|
||||||
|
@ -271,6 +272,7 @@ pub struct ProcessOptions {
|
||||||
pub entry_callback: Option<ProcessCallback>,
|
pub entry_callback: Option<ProcessCallback>,
|
||||||
pub override_num_threads: Option<usize>,
|
pub override_num_threads: Option<usize>,
|
||||||
pub new_hard_forks: Option<Vec<Slot>>,
|
pub new_hard_forks: Option<Vec<Slot>>,
|
||||||
|
pub frozen_accounts: Vec<Pubkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_blockstore(
|
pub fn process_blockstore(
|
||||||
|
@ -289,7 +291,11 @@ pub fn process_blockstore(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup bank for slot 0
|
// Setup bank for slot 0
|
||||||
let bank0 = Arc::new(Bank::new_with_paths(&genesis_config, account_paths));
|
let bank0 = Arc::new(Bank::new_with_paths(
|
||||||
|
&genesis_config,
|
||||||
|
account_paths,
|
||||||
|
&opts.frozen_accounts,
|
||||||
|
));
|
||||||
info!("processing ledger for slot 0...");
|
info!("processing ledger for slot 0...");
|
||||||
let recyclers = VerifyRecyclers::default();
|
let recyclers = VerifyRecyclers::default();
|
||||||
process_bank_0(&bank0, blockstore, &opts, &recyclers)?;
|
process_bank_0(&bank0, blockstore, &opts, &recyclers)?;
|
||||||
|
@ -2611,7 +2617,7 @@ pub mod tests {
|
||||||
genesis_config: &GenesisConfig,
|
genesis_config: &GenesisConfig,
|
||||||
account_paths: Vec<PathBuf>,
|
account_paths: Vec<PathBuf>,
|
||||||
) -> EpochSchedule {
|
) -> EpochSchedule {
|
||||||
let bank = Bank::new_with_paths(&genesis_config, account_paths);
|
let bank = Bank::new_with_paths(&genesis_config, account_paths, &[]);
|
||||||
bank.epoch_schedule().clone()
|
bank.epoch_schedule().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use solana_runtime::{
|
||||||
MAX_SNAPSHOT_DATA_FILE_SIZE,
|
MAX_SNAPSHOT_DATA_FILE_SIZE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use solana_sdk::{clock::Slot, hash::Hash};
|
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
env,
|
env,
|
||||||
|
@ -432,6 +432,7 @@ pub fn remove_snapshot<P: AsRef<Path>>(slot: Slot, snapshot_path: P) -> Result<(
|
||||||
|
|
||||||
pub fn bank_from_archive<P: AsRef<Path>>(
|
pub fn bank_from_archive<P: AsRef<Path>>(
|
||||||
account_paths: &[PathBuf],
|
account_paths: &[PathBuf],
|
||||||
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
snapshot_path: &PathBuf,
|
snapshot_path: &PathBuf,
|
||||||
snapshot_tar: P,
|
snapshot_tar: P,
|
||||||
) -> Result<Bank> {
|
) -> Result<Bank> {
|
||||||
|
@ -450,6 +451,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
|
||||||
let bank = rebuild_bank_from_snapshots(
|
let bank = rebuild_bank_from_snapshots(
|
||||||
snapshot_version.trim(),
|
snapshot_version.trim(),
|
||||||
account_paths,
|
account_paths,
|
||||||
|
frozen_account_pubkeys,
|
||||||
&unpacked_snapshots_dir,
|
&unpacked_snapshots_dir,
|
||||||
unpacked_accounts_dir,
|
unpacked_accounts_dir,
|
||||||
)?;
|
)?;
|
||||||
|
@ -575,6 +577,7 @@ pub fn untar_snapshot_in<P: AsRef<Path>, Q: AsRef<Path>>(
|
||||||
fn rebuild_bank_from_snapshots<P>(
|
fn rebuild_bank_from_snapshots<P>(
|
||||||
snapshot_version: &str,
|
snapshot_version: &str,
|
||||||
account_paths: &[PathBuf],
|
account_paths: &[PathBuf],
|
||||||
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
unpacked_snapshots_dir: &PathBuf,
|
unpacked_snapshots_dir: &PathBuf,
|
||||||
append_vecs_path: P,
|
append_vecs_path: P,
|
||||||
) -> Result<Bank>
|
) -> Result<Bank>
|
||||||
|
@ -606,12 +609,16 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info!("Rebuilding accounts...");
|
info!("Rebuilding accounts...");
|
||||||
bank.set_bank_rc(
|
let rc = bank::BankRc::from_stream(
|
||||||
bank::BankRc::new(account_paths.to_vec(), 0, bank.slot()),
|
account_paths,
|
||||||
bank::StatusCacheRc::default(),
|
bank.slot(),
|
||||||
);
|
&bank.ancestors,
|
||||||
bank.rc
|
frozen_account_pubkeys,
|
||||||
.accounts_from_stream(stream.by_ref(), &append_vecs_path)?;
|
stream.by_ref(),
|
||||||
|
&append_vecs_path,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
bank.set_bank_rc(rc, bank::StatusCacheRc::default());
|
||||||
Ok(bank)
|
Ok(bank)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -300,7 +300,7 @@ impl LocalCluster {
|
||||||
self.exit();
|
self.exit();
|
||||||
for (_, node) in self.validators.iter_mut() {
|
for (_, node) in self.validators.iter_mut() {
|
||||||
if let Some(v) = node.validator.take() {
|
if let Some(v) = node.validator.take() {
|
||||||
v.join().unwrap();
|
v.join().expect("Validator join failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,14 @@ use solana_local_cluster::{
|
||||||
local_cluster::{ClusterConfig, LocalCluster},
|
local_cluster::{ClusterConfig, LocalCluster},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
client::SyncClient,
|
client::{AsyncClient, SyncClient},
|
||||||
clock::{self, Slot},
|
clock::{self, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH},
|
epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH},
|
||||||
genesis_config::OperatingMode,
|
genesis_config::OperatingMode,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
poh_config::PohConfig,
|
poh_config::PohConfig,
|
||||||
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
@ -548,7 +549,7 @@ fn test_listener_startup() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_softlaunch_operating_mode() {
|
fn test_stable_operating_mode() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
let config = ClusterConfig {
|
let config = ClusterConfig {
|
||||||
|
@ -567,7 +568,7 @@ fn test_softlaunch_operating_mode() {
|
||||||
solana_core::cluster_info::VALIDATOR_PORT_RANGE,
|
solana_core::cluster_info::VALIDATOR_PORT_RANGE,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Programs that are available at soft launch epoch 0
|
// Programs that are available at epoch 0
|
||||||
for program_id in [
|
for program_id in [
|
||||||
&solana_config_program::id(),
|
&solana_config_program::id(),
|
||||||
&solana_sdk::system_program::id(),
|
&solana_sdk::system_program::id(),
|
||||||
|
@ -587,7 +588,7 @@ fn test_softlaunch_operating_mode() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Programs that are not available at soft launch epoch 0
|
// Programs that are not available at epoch 0
|
||||||
for program_id in [
|
for program_id in [
|
||||||
&solana_sdk::bpf_loader::id(),
|
&solana_sdk::bpf_loader::id(),
|
||||||
&solana_storage_program::id(),
|
&solana_storage_program::id(),
|
||||||
|
@ -607,6 +608,110 @@ fn test_softlaunch_operating_mode() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_frozen_account_panic(mut cluster: LocalCluster, frozen_account: Arc<Keypair>) {
|
||||||
|
let client = cluster
|
||||||
|
.get_validator_client(&frozen_account.pubkey())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check the validator is alive by poking it over RPC
|
||||||
|
trace!(
|
||||||
|
"validator slot: {}",
|
||||||
|
client
|
||||||
|
.get_slot_with_commitment(CommitmentConfig::recent())
|
||||||
|
.expect("get slot")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset the frozen account panic signal
|
||||||
|
solana_runtime::accounts_db::FROZEN_ACCOUNT_PANIC.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Wait for the frozen account panic signal
|
||||||
|
let mut i = 0;
|
||||||
|
while !solana_runtime::accounts_db::FROZEN_ACCOUNT_PANIC.load(Ordering::Relaxed) {
|
||||||
|
// Transfer from frozen account
|
||||||
|
let (blockhash, _fee_calculator) = client
|
||||||
|
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||||
|
.unwrap();
|
||||||
|
client
|
||||||
|
.async_transfer(1, &frozen_account, &Pubkey::new_rand(), blockhash)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
sleep(Duration::from_secs(1));
|
||||||
|
i += 1;
|
||||||
|
if i > 10 {
|
||||||
|
panic!("FROZEN_ACCOUNT_PANIC still false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The validator is now broken and won't shutdown properly. Avoid LocalCluster panic in Drop
|
||||||
|
// with some manual cleanup:
|
||||||
|
cluster.exit();
|
||||||
|
cluster.validators = HashMap::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_frozen_account_from_genesis() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let validator_identity =
|
||||||
|
Arc::new(solana_sdk::signature::keypair_from_seed(&[0u8; 32]).unwrap());
|
||||||
|
|
||||||
|
let config = ClusterConfig {
|
||||||
|
validator_keys: Some(vec![validator_identity.clone()]),
|
||||||
|
node_stakes: vec![100; 1],
|
||||||
|
cluster_lamports: 1_000,
|
||||||
|
validator_configs: vec![
|
||||||
|
ValidatorConfig {
|
||||||
|
// Freeze the validator identity account
|
||||||
|
frozen_accounts: vec![validator_identity.pubkey()],
|
||||||
|
..ValidatorConfig::default()
|
||||||
|
};
|
||||||
|
1
|
||||||
|
],
|
||||||
|
..ClusterConfig::default()
|
||||||
|
};
|
||||||
|
generate_frozen_account_panic(LocalCluster::new(&config), validator_identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_frozen_account_from_snapshot() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let validator_identity =
|
||||||
|
Arc::new(solana_sdk::signature::keypair_from_seed(&[0u8; 32]).unwrap());
|
||||||
|
|
||||||
|
let mut snapshot_test_config = setup_snapshot_validator_config(5, 1);
|
||||||
|
// Freeze the validator identity account
|
||||||
|
snapshot_test_config.validator_config.frozen_accounts = vec![validator_identity.pubkey()];
|
||||||
|
|
||||||
|
let config = ClusterConfig {
|
||||||
|
validator_keys: Some(vec![validator_identity.clone()]),
|
||||||
|
node_stakes: vec![100; 1],
|
||||||
|
cluster_lamports: 1_000,
|
||||||
|
validator_configs: vec![snapshot_test_config.validator_config.clone()],
|
||||||
|
..ClusterConfig::default()
|
||||||
|
};
|
||||||
|
let mut cluster = LocalCluster::new(&config);
|
||||||
|
|
||||||
|
let snapshot_package_output_path = &snapshot_test_config
|
||||||
|
.validator_config
|
||||||
|
.snapshot_config
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.snapshot_package_output_path;
|
||||||
|
|
||||||
|
trace!("Waiting for snapshot at {:?}", snapshot_package_output_path);
|
||||||
|
let (archive_filename, _archive_snapshot_hash) =
|
||||||
|
wait_for_next_snapshot(&cluster, &snapshot_package_output_path);
|
||||||
|
|
||||||
|
trace!("Found snapshot: {:?}", archive_filename);
|
||||||
|
|
||||||
|
// Restart the validator from a snapshot
|
||||||
|
let validator_info = cluster.exit_node(&validator_identity.pubkey());
|
||||||
|
cluster.restart_node(&validator_identity.pubkey(), validator_info);
|
||||||
|
|
||||||
|
generate_frozen_account_panic(cluster, validator_identity);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_consistency_halt() {
|
fn test_consistency_halt() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ byteorder = "1.3.4"
|
||||||
fnv = "1.0.6"
|
fnv = "1.0.6"
|
||||||
fs_extra = "1.1.0"
|
fs_extra = "1.1.0"
|
||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2.68"
|
libc = "0.2.68"
|
||||||
libloading = "0.5.2"
|
libloading = "0.5.2"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn bench_has_duplicates(bencher: &mut Bencher) {
|
||||||
#[bench]
|
#[bench]
|
||||||
fn test_accounts_create(bencher: &mut Bencher) {
|
fn test_accounts_create(bencher: &mut Bencher) {
|
||||||
let (genesis_config, _) = create_genesis_config(10_000);
|
let (genesis_config, _) = create_genesis_config(10_000);
|
||||||
let bank0 = Bank::new_with_paths(&genesis_config, vec![PathBuf::from("bench_a0")]);
|
let bank0 = Bank::new_with_paths(&genesis_config, vec![PathBuf::from("bench_a0")], &[]);
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
let mut pubkeys: Vec<Pubkey> = vec![];
|
let mut pubkeys: Vec<Pubkey> = vec![];
|
||||||
deposit_many(&bank0, &mut pubkeys, 1000);
|
deposit_many(&bank0, &mut pubkeys, 1000);
|
||||||
|
@ -46,6 +46,7 @@ fn test_accounts_squash(bencher: &mut Bencher) {
|
||||||
banks.push(Arc::new(Bank::new_with_paths(
|
banks.push(Arc::new(Bank::new_with_paths(
|
||||||
&genesis_config,
|
&genesis_config,
|
||||||
vec![PathBuf::from("bench_a1")],
|
vec![PathBuf::from("bench_a1")],
|
||||||
|
&[],
|
||||||
)));
|
)));
|
||||||
let mut pubkeys: Vec<Pubkey> = vec![];
|
let mut pubkeys: Vec<Pubkey> = vec![];
|
||||||
deposit_many(&banks[0], &mut pubkeys, 250000);
|
deposit_many(&banks[0], &mut pubkeys, 250000);
|
||||||
|
|
|
@ -60,15 +60,9 @@ pub type TransactionLoadResult = (TransactionAccounts, TransactionLoaders, Trans
|
||||||
|
|
||||||
impl Accounts {
|
impl Accounts {
|
||||||
pub fn new(paths: Vec<PathBuf>) -> Self {
|
pub fn new(paths: Vec<PathBuf>) -> Self {
|
||||||
let accounts_db = Arc::new(AccountsDB::new(paths));
|
Self::new_with_frozen_accounts(paths, &HashMap::default(), &[])
|
||||||
|
|
||||||
Accounts {
|
|
||||||
slot: 0,
|
|
||||||
accounts_db,
|
|
||||||
account_locks: Mutex::new(HashSet::new()),
|
|
||||||
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_parent(parent: &Accounts, slot: Slot, parent_slot: Slot) -> Self {
|
pub fn new_from_parent(parent: &Accounts, slot: Slot, parent_slot: Slot) -> Self {
|
||||||
let accounts_db = parent.accounts_db.clone();
|
let accounts_db = parent.accounts_db.clone();
|
||||||
accounts_db.set_hash(slot, parent_slot);
|
accounts_db.set_hash(slot, parent_slot);
|
||||||
|
@ -80,13 +74,48 @@ impl Accounts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accounts_from_stream<R: Read, P: AsRef<Path>>(
|
pub fn new_with_frozen_accounts(
|
||||||
&self,
|
paths: Vec<PathBuf>,
|
||||||
|
ancestors: &HashMap<Slot, usize>,
|
||||||
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
|
) -> Self {
|
||||||
|
let mut accounts = Accounts {
|
||||||
|
slot: 0,
|
||||||
|
accounts_db: Arc::new(AccountsDB::new(paths)),
|
||||||
|
account_locks: Mutex::new(HashSet::new()),
|
||||||
|
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
|
||||||
|
};
|
||||||
|
accounts.freeze_accounts(ancestors, frozen_account_pubkeys);
|
||||||
|
accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn freeze_accounts(
|
||||||
|
&mut self,
|
||||||
|
ancestors: &HashMap<Slot, usize>,
|
||||||
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
|
) {
|
||||||
|
Arc::get_mut(&mut self.accounts_db)
|
||||||
|
.unwrap()
|
||||||
|
.freeze_accounts(ancestors, frozen_account_pubkeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_stream<R: Read, P: AsRef<Path>>(
|
||||||
|
account_paths: &[PathBuf],
|
||||||
|
ancestors: &HashMap<Slot, usize>,
|
||||||
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
stream: &mut BufReader<R>,
|
stream: &mut BufReader<R>,
|
||||||
append_vecs_path: P,
|
stream_append_vecs_path: P,
|
||||||
) -> std::result::Result<(), IOError> {
|
) -> std::result::Result<Self, IOError> {
|
||||||
self.accounts_db
|
let mut accounts_db = AccountsDB::new(account_paths.to_vec());
|
||||||
.accounts_from_stream(stream, append_vecs_path)
|
accounts_db.accounts_from_stream(stream, stream_append_vecs_path)?;
|
||||||
|
accounts_db.freeze_accounts(ancestors, frozen_account_pubkeys);
|
||||||
|
|
||||||
|
Ok(Accounts {
|
||||||
|
slot: 0,
|
||||||
|
accounts_db: Arc::new(accounts_db),
|
||||||
|
account_locks: Mutex::new(HashSet::new()),
|
||||||
|
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the slice has any duplicate elements
|
/// Return true if the slice has any duplicate elements
|
||||||
|
@ -1401,10 +1430,14 @@ mod tests {
|
||||||
let buf = writer.into_inner();
|
let buf = writer.into_inner();
|
||||||
let mut reader = BufReader::new(&buf[..]);
|
let mut reader = BufReader::new(&buf[..]);
|
||||||
let (_accounts_dir, daccounts_paths) = get_temp_accounts_paths(2).unwrap();
|
let (_accounts_dir, daccounts_paths) = get_temp_accounts_paths(2).unwrap();
|
||||||
let daccounts = Accounts::new(daccounts_paths.clone());
|
let daccounts = Accounts::from_stream(
|
||||||
assert!(daccounts
|
&daccounts_paths,
|
||||||
.accounts_from_stream(&mut reader, copied_accounts.path())
|
&HashMap::default(),
|
||||||
.is_ok());
|
&[],
|
||||||
|
&mut reader,
|
||||||
|
copied_accounts.path(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
check_accounts(&daccounts, &pubkeys, 100);
|
check_accounts(&daccounts, &pubkeys, 100);
|
||||||
assert_eq!(accounts.bank_hash_at(0), daccounts.bank_hash_at(0));
|
assert_eq!(accounts.bank_hash_at(0), daccounts.bank_hash_at(0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ use crate::{
|
||||||
use bincode::{deserialize_from, serialize_into};
|
use bincode::{deserialize_from, serialize_into};
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use fs_extra::dir::CopyOptions;
|
use fs_extra::dir::CopyOptions;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use rayon::{prelude::*, ThreadPool};
|
use rayon::{prelude::*, ThreadPool};
|
||||||
|
@ -56,6 +57,12 @@ pub const DEFAULT_FILE_SIZE: u64 = 4 * 1024 * 1024;
|
||||||
pub const DEFAULT_NUM_THREADS: u32 = 8;
|
pub const DEFAULT_NUM_THREADS: u32 = 8;
|
||||||
pub const DEFAULT_NUM_DIRS: u32 = 4;
|
pub const DEFAULT_NUM_DIRS: u32 = 4;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
// FROZEN_ACCOUNT_PANIC is used to signal local_cluster that an AccountsDB panic has occurred,
|
||||||
|
// as |cargo test| cannot observe panics in other threads
|
||||||
|
pub static ref FROZEN_ACCOUNT_PANIC: Arc<AtomicBool> = { Arc::new(AtomicBool::new(false)) };
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ErrorCounters {
|
pub struct ErrorCounters {
|
||||||
pub total: usize,
|
pub total: usize,
|
||||||
|
@ -426,6 +433,12 @@ pub struct BankHashInfo {
|
||||||
pub stats: BankHashStats,
|
pub stats: BankHashStats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FrozenAccountInfo {
|
||||||
|
pub hash: Hash, // Hash generated by hash_frozen_account_data()
|
||||||
|
pub lamports: u64, // Account balance cannot be lower than this amount
|
||||||
|
}
|
||||||
|
|
||||||
// This structure handles the load/store of the accounts
|
// This structure handles the load/store of the accounts
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AccountsDB {
|
pub struct AccountsDB {
|
||||||
|
@ -448,6 +461,9 @@ pub struct AccountsDB {
|
||||||
/// Starting file size of appendvecs
|
/// Starting file size of appendvecs
|
||||||
file_size: u64,
|
file_size: u64,
|
||||||
|
|
||||||
|
/// Accounts that will cause a panic! if data modified or lamports decrease
|
||||||
|
frozen_accounts: HashMap<Pubkey, FrozenAccountInfo>,
|
||||||
|
|
||||||
/// Thread pool used for par_iter
|
/// Thread pool used for par_iter
|
||||||
pub thread_pool: ThreadPool,
|
pub thread_pool: ThreadPool,
|
||||||
|
|
||||||
|
@ -478,6 +494,7 @@ impl Default for AccountsDB {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
min_num_stores: num_threads,
|
min_num_stores: num_threads,
|
||||||
bank_hashes: RwLock::new(bank_hashes),
|
bank_hashes: RwLock::new(bank_hashes),
|
||||||
|
frozen_accounts: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +508,7 @@ impl AccountsDB {
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create a temprorary set of accounts directories, used primarily
|
// Create a temporary set of accounts directories, used primarily
|
||||||
// for testing
|
// for testing
|
||||||
let (temp_dirs, paths) = get_temp_accounts_paths(DEFAULT_NUM_DIRS).unwrap();
|
let (temp_dirs, paths) = get_temp_accounts_paths(DEFAULT_NUM_DIRS).unwrap();
|
||||||
Self {
|
Self {
|
||||||
|
@ -526,7 +543,7 @@ impl AccountsDB {
|
||||||
pub fn accounts_from_stream<R: Read, P: AsRef<Path>>(
|
pub fn accounts_from_stream<R: Read, P: AsRef<Path>>(
|
||||||
&self,
|
&self,
|
||||||
mut stream: &mut BufReader<R>,
|
mut stream: &mut BufReader<R>,
|
||||||
append_vecs_path: P,
|
stream_append_vecs_path: P,
|
||||||
) -> Result<(), IOError> {
|
) -> Result<(), IOError> {
|
||||||
let _len: usize =
|
let _len: usize =
|
||||||
deserialize_from(&mut stream).map_err(|e| AccountsDB::get_io_error(&e.to_string()))?;
|
deserialize_from(&mut stream).map_err(|e| AccountsDB::get_io_error(&e.to_string()))?;
|
||||||
|
@ -549,8 +566,9 @@ impl AccountsDB {
|
||||||
// at by `local_dir`
|
// at by `local_dir`
|
||||||
let append_vec_relative_path =
|
let append_vec_relative_path =
|
||||||
AppendVec::new_relative_path(slot_id, storage_entry.id);
|
AppendVec::new_relative_path(slot_id, storage_entry.id);
|
||||||
let append_vec_abs_path =
|
let append_vec_abs_path = stream_append_vecs_path
|
||||||
append_vecs_path.as_ref().join(&append_vec_relative_path);
|
.as_ref()
|
||||||
|
.join(&append_vec_relative_path);
|
||||||
let target = local_dir.join(append_vec_abs_path.file_name().unwrap());
|
let target = local_dir.join(append_vec_abs_path.file_name().unwrap());
|
||||||
if std::fs::rename(append_vec_abs_path.clone(), target).is_err() {
|
if std::fs::rename(append_vec_abs_path.clone(), target).is_err() {
|
||||||
let mut copy_options = CopyOptions::new();
|
let mut copy_options = CopyOptions::new();
|
||||||
|
@ -976,6 +994,21 @@ impl AccountsDB {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_frozen_account_data(account: &Account) -> Hash {
|
||||||
|
let mut hasher = Hasher::default();
|
||||||
|
|
||||||
|
hasher.hash(&account.data);
|
||||||
|
hasher.hash(&account.owner.as_ref());
|
||||||
|
|
||||||
|
if account.executable {
|
||||||
|
hasher.hash(&[1u8; 1]);
|
||||||
|
} else {
|
||||||
|
hasher.hash(&[0u8; 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher.result()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hash_account_data(
|
pub fn hash_account_data(
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
|
@ -1387,8 +1420,62 @@ impl AccountsDB {
|
||||||
hashes
|
hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn freeze_accounts(
|
||||||
|
&mut self,
|
||||||
|
ancestors: &HashMap<Slot, usize>,
|
||||||
|
account_pubkeys: &[Pubkey],
|
||||||
|
) {
|
||||||
|
for account_pubkey in account_pubkeys {
|
||||||
|
if let Some((account, _slot)) = self.load_slow(ancestors, &account_pubkey) {
|
||||||
|
let frozen_account_info = FrozenAccountInfo {
|
||||||
|
hash: Self::hash_frozen_account_data(&account),
|
||||||
|
lamports: account.lamports,
|
||||||
|
};
|
||||||
|
warn!(
|
||||||
|
"Account {} is now frozen at lamports={}, hash={}",
|
||||||
|
account_pubkey, frozen_account_info.lamports, frozen_account_info.hash
|
||||||
|
);
|
||||||
|
self.frozen_accounts
|
||||||
|
.insert(*account_pubkey, frozen_account_info);
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"Unable to freeze an account that does not exist: {}",
|
||||||
|
account_pubkey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cause a panic if frozen accounts would be affected by data in `accounts`
|
||||||
|
fn assert_frozen_accounts(&self, accounts: &[(&Pubkey, &Account)]) {
|
||||||
|
if self.frozen_accounts.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (account_pubkey, account) in accounts.iter() {
|
||||||
|
if let Some(frozen_account_info) = self.frozen_accounts.get(*account_pubkey) {
|
||||||
|
if account.lamports < frozen_account_info.lamports {
|
||||||
|
FROZEN_ACCOUNT_PANIC.store(true, Ordering::Relaxed);
|
||||||
|
panic!(
|
||||||
|
"Frozen account {} modified. Lamports decreased from {} to {}",
|
||||||
|
account_pubkey, frozen_account_info.lamports, account.lamports,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let hash = Self::hash_frozen_account_data(&account);
|
||||||
|
if hash != frozen_account_info.hash {
|
||||||
|
FROZEN_ACCOUNT_PANIC.store(true, Ordering::Relaxed);
|
||||||
|
panic!(
|
||||||
|
"Frozen account {} modified. Hash changed from {} to {}",
|
||||||
|
account_pubkey, frozen_account_info.hash, hash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Store the account update.
|
/// Store the account update.
|
||||||
pub fn store(&self, slot_id: Slot, accounts: &[(&Pubkey, &Account)]) {
|
pub fn store(&self, slot_id: Slot, accounts: &[(&Pubkey, &Account)]) {
|
||||||
|
self.assert_frozen_accounts(accounts);
|
||||||
let hashes = self.hash_accounts(slot_id, accounts);
|
let hashes = self.hash_accounts(slot_id, accounts);
|
||||||
self.store_with_hashes(slot_id, accounts, &hashes);
|
self.store_with_hashes(slot_id, accounts, &hashes);
|
||||||
}
|
}
|
||||||
|
@ -2712,6 +2799,139 @@ pub mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash_frozen_account_data() {
|
||||||
|
let account = Account::new(1, 42, &Pubkey::default());
|
||||||
|
|
||||||
|
let hash = AccountsDB::hash_frozen_account_data(&account);
|
||||||
|
assert_ne!(hash, Hash::default()); // Better not be the default Hash
|
||||||
|
|
||||||
|
// Lamports changes to not affect the hash
|
||||||
|
let mut account_modified = account.clone();
|
||||||
|
account_modified.lamports -= 1;
|
||||||
|
assert_eq!(
|
||||||
|
hash,
|
||||||
|
AccountsDB::hash_frozen_account_data(&account_modified)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rent epoch may changes to not affect the hash
|
||||||
|
let mut account_modified = account.clone();
|
||||||
|
account_modified.rent_epoch += 1;
|
||||||
|
assert_eq!(
|
||||||
|
hash,
|
||||||
|
AccountsDB::hash_frozen_account_data(&account_modified)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Account data may not be modified
|
||||||
|
let mut account_modified = account.clone();
|
||||||
|
account_modified.data[0] = 42;
|
||||||
|
assert_ne!(
|
||||||
|
hash,
|
||||||
|
AccountsDB::hash_frozen_account_data(&account_modified)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Owner may not be modified
|
||||||
|
let mut account_modified = account.clone();
|
||||||
|
account_modified.owner =
|
||||||
|
Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
|
||||||
|
assert_ne!(
|
||||||
|
hash,
|
||||||
|
AccountsDB::hash_frozen_account_data(&account_modified)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Executable may not be modified
|
||||||
|
let mut account_modified = account.clone();
|
||||||
|
account_modified.executable = true;
|
||||||
|
assert_ne!(
|
||||||
|
hash,
|
||||||
|
AccountsDB::hash_frozen_account_data(&account_modified)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_frozen_account_lamport_increase() {
|
||||||
|
let frozen_pubkey =
|
||||||
|
Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
|
||||||
|
let mut db = AccountsDB::new(Vec::new());
|
||||||
|
|
||||||
|
let mut account = Account::new(1, 42, &frozen_pubkey);
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
db.freeze_accounts(&ancestors, &[frozen_pubkey]);
|
||||||
|
|
||||||
|
// Store with no account changes is ok
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
|
||||||
|
// Store with an increase in lamports is ok
|
||||||
|
account.lamports = 2;
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
|
||||||
|
// Store with an decrease that does not go below the frozen amount of lamports is tolerated
|
||||||
|
account.lamports = 1;
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
|
||||||
|
// A store of any value over the frozen value of '1' across different slots is also ok
|
||||||
|
account.lamports = 3;
|
||||||
|
db.store(1, &[(&frozen_pubkey, &account)]);
|
||||||
|
account.lamports = 2;
|
||||||
|
db.store(2, &[(&frozen_pubkey, &account)]);
|
||||||
|
account.lamports = 1;
|
||||||
|
db.store(3, &[(&frozen_pubkey, &account)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Frozen account My11111111111111111111111111111111111111111 modified. Lamports decreased from 1 to 0"
|
||||||
|
)]
|
||||||
|
fn test_frozen_account_lamport_decrease() {
|
||||||
|
let frozen_pubkey =
|
||||||
|
Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
|
||||||
|
let mut db = AccountsDB::new(Vec::new());
|
||||||
|
|
||||||
|
let mut account = Account::new(1, 42, &frozen_pubkey);
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
db.freeze_accounts(&ancestors, &[frozen_pubkey]);
|
||||||
|
|
||||||
|
// Store with a decrease below the frozen amount of lamports is not ok
|
||||||
|
account.lamports -= 1;
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Unable to freeze an account that does not exist: My11111111111111111111111111111111111111111"
|
||||||
|
)]
|
||||||
|
fn test_frozen_account_nonexistent() {
|
||||||
|
let frozen_pubkey =
|
||||||
|
Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
|
||||||
|
let mut db = AccountsDB::new(Vec::new());
|
||||||
|
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
db.freeze_accounts(&ancestors, &[frozen_pubkey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Frozen account My11111111111111111111111111111111111111111 modified. Hash changed from 8wHcxDkjiwdrkPAsDnmNrF1UDGJFAtZzPQBSVweY3yRA to JdscGYB1uczVssmYuJusDD1Bfe6wpNeeho8XjcH8inN"
|
||||||
|
)]
|
||||||
|
fn test_frozen_account_data_modified() {
|
||||||
|
let frozen_pubkey =
|
||||||
|
Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
|
||||||
|
let mut db = AccountsDB::new(Vec::new());
|
||||||
|
|
||||||
|
let mut account = Account::new(1, 42, &frozen_pubkey);
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
db.freeze_accounts(&ancestors, &[frozen_pubkey]);
|
||||||
|
|
||||||
|
account.data[0] = 42;
|
||||||
|
db.store(0, &[(&frozen_pubkey, &account)]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hash_stored_account() {
|
fn test_hash_stored_account() {
|
||||||
// This test uses some UNSAFE trick to detect most of account's field
|
// This test uses some UNSAFE trick to detect most of account's field
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
//! already been signed and verified.
|
//! already been signed and verified.
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{Accounts, TransactionAccounts, TransactionLoadResult, TransactionLoaders},
|
accounts::{Accounts, TransactionAccounts, TransactionLoadResult, TransactionLoaders},
|
||||||
accounts_db::{
|
accounts_db::{AccountsDBSerialize, ErrorCounters, SnapshotStorage, SnapshotStorages},
|
||||||
AccountsDBSerialize, AppendVecId, ErrorCounters, SnapshotStorage, SnapshotStorages,
|
|
||||||
},
|
|
||||||
blockhash_queue::BlockhashQueue,
|
blockhash_queue::BlockhashQueue,
|
||||||
message_processor::{MessageProcessor, ProcessInstruction},
|
message_processor::{MessageProcessor, ProcessInstruction},
|
||||||
nonce_utils,
|
nonce_utils,
|
||||||
|
@ -85,30 +83,30 @@ pub struct BankRc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BankRc {
|
impl BankRc {
|
||||||
pub fn new(account_paths: Vec<PathBuf>, id: AppendVecId, slot: Slot) -> Self {
|
pub fn from_stream<R: Read, P: AsRef<Path>>(
|
||||||
let accounts = Accounts::new(account_paths);
|
account_paths: &[PathBuf],
|
||||||
accounts
|
slot: Slot,
|
||||||
.accounts_db
|
ancestors: &HashMap<Slot, usize>,
|
||||||
.next_id
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
.store(id as usize, Ordering::Relaxed);
|
mut stream: &mut BufReader<R>,
|
||||||
BankRc {
|
stream_append_vecs_path: P,
|
||||||
|
) -> std::result::Result<Self, IOError> {
|
||||||
|
let _len: usize =
|
||||||
|
deserialize_from(&mut stream).map_err(|e| BankRc::get_io_error(&e.to_string()))?;
|
||||||
|
|
||||||
|
let accounts = Accounts::from_stream(
|
||||||
|
account_paths,
|
||||||
|
ancestors,
|
||||||
|
frozen_account_pubkeys,
|
||||||
|
stream,
|
||||||
|
stream_append_vecs_path,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(BankRc {
|
||||||
accounts: Arc::new(accounts),
|
accounts: Arc::new(accounts),
|
||||||
parent: RwLock::new(None),
|
parent: RwLock::new(None),
|
||||||
slot,
|
slot,
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accounts_from_stream<R: Read, P: AsRef<Path>>(
|
|
||||||
&self,
|
|
||||||
mut stream: &mut BufReader<R>,
|
|
||||||
append_vecs_path: P,
|
|
||||||
) -> std::result::Result<(), IOError> {
|
|
||||||
let _len: usize =
|
|
||||||
deserialize_from(&mut stream).map_err(|e| BankRc::get_io_error(&e.to_string()))?;
|
|
||||||
self.accounts
|
|
||||||
.accounts_from_stream(stream, append_vecs_path)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_snapshot_storages(&self, slot: Slot) -> SnapshotStorages {
|
pub fn get_snapshot_storages(&self, slot: Slot) -> SnapshotStorages {
|
||||||
|
@ -358,14 +356,25 @@ impl Default for BlockhashQueue {
|
||||||
|
|
||||||
impl Bank {
|
impl Bank {
|
||||||
pub fn new(genesis_config: &GenesisConfig) -> Self {
|
pub fn new(genesis_config: &GenesisConfig) -> Self {
|
||||||
Self::new_with_paths(&genesis_config, Vec::new())
|
Self::new_with_paths(&genesis_config, Vec::new(), &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_paths(genesis_config: &GenesisConfig, paths: Vec<PathBuf>) -> Self {
|
pub fn new_with_paths(
|
||||||
|
genesis_config: &GenesisConfig,
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
frozen_account_pubkeys: &[Pubkey],
|
||||||
|
) -> Self {
|
||||||
let mut bank = Self::default();
|
let mut bank = Self::default();
|
||||||
bank.ancestors.insert(bank.slot(), 0);
|
bank.ancestors.insert(bank.slot(), 0);
|
||||||
|
|
||||||
bank.rc.accounts = Arc::new(Accounts::new(paths));
|
bank.rc.accounts = Arc::new(Accounts::new(paths));
|
||||||
bank.process_genesis_config(genesis_config);
|
bank.process_genesis_config(genesis_config);
|
||||||
|
|
||||||
|
// Freeze accounts after process_genesis_config creates the initial append vecs
|
||||||
|
Arc::get_mut(&mut bank.rc.accounts)
|
||||||
|
.unwrap()
|
||||||
|
.freeze_accounts(&bank.ancestors, frozen_account_pubkeys);
|
||||||
|
|
||||||
// genesis needs stakes for all epochs up to the epoch implied by
|
// genesis needs stakes for all epochs up to the epoch implied by
|
||||||
// slot = 0 and genesis configuration
|
// slot = 0 and genesis configuration
|
||||||
{
|
{
|
||||||
|
@ -4769,14 +4778,21 @@ mod tests {
|
||||||
let (_accounts_dir, dbank_paths) = get_temp_accounts_paths(4).unwrap();
|
let (_accounts_dir, dbank_paths) = get_temp_accounts_paths(4).unwrap();
|
||||||
let ref_sc = StatusCacheRc::default();
|
let ref_sc = StatusCacheRc::default();
|
||||||
ref_sc.status_cache.write().unwrap().add_root(2);
|
ref_sc.status_cache.write().unwrap().add_root(2);
|
||||||
dbank.set_bank_rc(BankRc::new(dbank_paths.clone(), 0, dbank.slot()), ref_sc);
|
|
||||||
// Create a directory to simulate AppendVecs unpackaged from a snapshot tar
|
// Create a directory to simulate AppendVecs unpackaged from a snapshot tar
|
||||||
let copied_accounts = TempDir::new().unwrap();
|
let copied_accounts = TempDir::new().unwrap();
|
||||||
copy_append_vecs(&bank2.rc.accounts.accounts_db, copied_accounts.path()).unwrap();
|
copy_append_vecs(&bank2.rc.accounts.accounts_db, copied_accounts.path()).unwrap();
|
||||||
dbank
|
dbank.set_bank_rc(
|
||||||
.rc
|
BankRc::from_stream(
|
||||||
.accounts_from_stream(&mut reader, copied_accounts.path())
|
&dbank_paths,
|
||||||
.unwrap();
|
dbank.slot(),
|
||||||
|
&dbank.ancestors,
|
||||||
|
&[],
|
||||||
|
&mut reader,
|
||||||
|
copied_accounts.path(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
ref_sc,
|
||||||
|
);
|
||||||
assert_eq!(dbank.get_balance(&key1.pubkey()), 0);
|
assert_eq!(dbank.get_balance(&key1.pubkey()), 0);
|
||||||
assert_eq!(dbank.get_balance(&key2.pubkey()), 10);
|
assert_eq!(dbank.get_balance(&key2.pubkey()), 10);
|
||||||
assert_eq!(dbank.get_balance(&key3.pubkey()), 0);
|
assert_eq!(dbank.get_balance(&key3.pubkey()), 0);
|
||||||
|
|
|
@ -110,7 +110,7 @@ impl PreAccount {
|
||||||
return Err(InstructionError::ExecutableModified);
|
return Err(InstructionError::ExecutableModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No one modifies r ent_epoch (yet).
|
// No one modifies rent_epoch (yet).
|
||||||
if self.rent_epoch != post.rent_epoch {
|
if self.rent_epoch != post.rent_epoch {
|
||||||
return Err(InstructionError::RentEpochModified);
|
return Err(InstructionError::RentEpochModified);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use clap::{
|
use clap::{
|
||||||
crate_description, crate_name, value_t, value_t_or_exit, values_t_or_exit, App, Arg, ArgMatches,
|
crate_description, crate_name, value_t, value_t_or_exit, values_t, values_t_or_exit, App, Arg,
|
||||||
|
ArgMatches,
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
@ -662,6 +663,17 @@ pub fn main() {
|
||||||
.takes_value(false)
|
.takes_value(false)
|
||||||
.help("Abort the validator if a bank hash mismatch is detected within trusted validator set"),
|
.help("Abort the validator if a bank hash mismatch is detected within trusted validator set"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::with_name("frozen_accounts")
|
||||||
|
.long("frozen-account")
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("PUBKEY")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Freeze the specified account. This will cause the validator to \
|
||||||
|
intentionally crash should any transaction modify the frozen account in any way \
|
||||||
|
other than increasing the account balance"),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let identity_keypair = Arc::new(keypair_of(&matches, "identity").unwrap_or_else(Keypair::new));
|
let identity_keypair = Arc::new(keypair_of(&matches, "identity").unwrap_or_else(Keypair::new));
|
||||||
|
@ -733,6 +745,7 @@ pub fn main() {
|
||||||
voting_disabled: matches.is_present("no_voting"),
|
voting_disabled: matches.is_present("no_voting"),
|
||||||
wait_for_supermajority: value_t!(matches, "wait_for_supermajority", Slot).ok(),
|
wait_for_supermajority: value_t!(matches, "wait_for_supermajority", Slot).ok(),
|
||||||
trusted_validators,
|
trusted_validators,
|
||||||
|
frozen_accounts: values_t!(matches, "frozen_accounts", Pubkey).unwrap_or_default(),
|
||||||
..ValidatorConfig::default()
|
..ValidatorConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue