diff --git a/docs/src/proposals/epoch_accounts_hash.md b/docs/src/proposals/epoch_accounts_hash.md index 343dd068e..56817e7c6 100644 --- a/docs/src/proposals/epoch_accounts_hash.md +++ b/docs/src/proposals/epoch_accounts_hash.md @@ -149,6 +149,30 @@ contain the EAH. Same as (4). +#### Snapshot Verification + +If a snapshot archive includes an EAH, we want to verify the EAH is correct at +load time (instead of waiting until `stop slot`, which could be far in the +future). + +If the snapshot archive is for a slot within the `calculation window`†¹, then it +*must* include an EAH. The snapshot hash itself will now also incorporate the +EAH. In pseudo code: +```pseudo +if slot is in calculation window + let snapshot hash = hash(accounts hash, epoch accounts hash) +else + let snapshot hash = accounts hash +endif +``` +Since loading from a snapshot archive already verifies the snapshot archive's +hash against the deserialized bank, the EAH will be implicitly verified as +well. + +†¹: The `calculation window` is `[start slot, stop slot)`, based on the epoch + of the referenced `Bank`. + + #### Corner Cases #### Minimum Slots per Epoch diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 771c31dd2..04c71acb2 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -6984,7 +6984,8 @@ impl Bank { pub fn get_snapshot_hash(&self) -> SnapshotHash { let accounts_hash = self.get_accounts_hash(); - SnapshotHash::new(&accounts_hash) + let epoch_accounts_hash = self.get_epoch_accounts_hash_to_serialize(); + SnapshotHash::new(&accounts_hash, epoch_accounts_hash.as_ref()) } pub fn get_thread_pool(&self) -> &ThreadPool { diff --git a/runtime/src/snapshot_hash.rs b/runtime/src/snapshot_hash.rs index 1d114b543..a23634e29 100644 --- a/runtime/src/snapshot_hash.rs +++ b/runtime/src/snapshot_hash.rs @@ -1,5 +1,11 @@ //! Helper types and functions for handling and dealing with snapshot hashes. -use solana_sdk::{clock::Slot, hash::Hash}; +use { + crate::epoch_accounts_hash::EpochAccountsHash, + solana_sdk::{ + clock::Slot, + hash::{Hash, Hasher}, + }, +}; /// At startup, when loading from snapshots, the starting snapshot hashes need to be passed to /// SnapshotPackagerService, which is in charge of pushing the hashes to CRDS. This struct wraps @@ -48,11 +54,19 @@ pub struct IncrementalSnapshotHashes { pub struct SnapshotHash(pub Hash); impl SnapshotHash { - /// Make a snapshot hash from an accounts hash - /// - /// Will soon also incorporate the epoch accounts hash + /// Make a snapshot hash from an accounts hash and epoch accounts hash #[must_use] - pub fn new(accounts_hash: &Hash) -> Self { - Self(*accounts_hash) + pub fn new(accounts_hash: &Hash, epoch_accounts_hash: Option<&EpochAccountsHash>) -> Self { + let snapshot_hash = match epoch_accounts_hash { + None => *accounts_hash, + Some(epoch_accounts_hash) => { + let mut hasher = Hasher::default(); + hasher.hash(accounts_hash.as_ref()); + hasher.hash(epoch_accounts_hash.as_ref().as_ref()); + hasher.result() + } + }; + + Self(snapshot_hash) } } diff --git a/runtime/src/snapshot_package.rs b/runtime/src/snapshot_package.rs index baad60840..a496b47ff 100644 --- a/runtime/src/snapshot_package.rs +++ b/runtime/src/snapshot_package.rs @@ -3,6 +3,7 @@ use { accounts::Accounts, accounts_db::SnapshotStorages, bank::{Bank, BankSlotDelta}, + epoch_accounts_hash::EpochAccountsHash, rent_collector::RentCollector, snapshot_archive_info::{SnapshotArchiveInfo, SnapshotArchiveInfoGetter}, snapshot_hash::SnapshotHash, @@ -107,6 +108,7 @@ impl AccountsPackage { incremental_snapshot_archives_dir: incremental_snapshot_archives_dir .as_ref() .to_path_buf(), + epoch_accounts_hash: bank.get_epoch_accounts_hash_to_serialize(), }; Ok(Self::_new( package_type, @@ -177,6 +179,7 @@ impl AccountsPackage { snapshot_version: SnapshotVersion::default(), full_snapshot_archives_dir: PathBuf::default(), incremental_snapshot_archives_dir: PathBuf::default(), + epoch_accounts_hash: Option::default(), }), enqueued: Instant::now(), } @@ -217,6 +220,7 @@ pub struct SupplementalSnapshotInfo { pub snapshot_version: SnapshotVersion, pub full_snapshot_archives_dir: PathBuf, pub incremental_snapshot_archives_dir: PathBuf, + pub epoch_accounts_hash: Option, } /// Accounts packages are sent to the Accounts Hash Verifier for processing. There are multiple @@ -247,7 +251,8 @@ impl SnapshotPackage { let Some(snapshot_info) = accounts_package.snapshot_info else { panic!("The AccountsPackage must have snapshot info in order to make a SnapshotPackage!"); }; - let snapshot_hash = SnapshotHash::new(&accounts_hash); + let snapshot_hash = + SnapshotHash::new(&accounts_hash, snapshot_info.epoch_accounts_hash.as_ref()); let mut snapshot_storages = accounts_package.snapshot_storages; let snapshot_archive_path = match snapshot_type { SnapshotType::FullSnapshot => snapshot_utils::build_full_snapshot_archive_path(