Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 1f7c714

Browse files
author
steviez
authored
Allow bank hash file to contain details for multiple slots (#34516)
The initial implementation only allowed for outputting details for one bank/slot. There are several usecases that could take advantage of the file being able to contain details for multiple banks. So, this PR adjusts the structure to contain details about multiple banks. Note that using a basic diff will not work for comparing the old format to this new format. Instead, a basic transform with jq will be necessary to do so.
1 parent 057ad2a commit 1f7c714

File tree

1 file changed

+97
-48
lines changed

1 file changed

+97
-48
lines changed

runtime/src/bank/bank_hash_details.rs

Lines changed: 97 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,51 @@ use {
2323

2424
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
2525
pub(crate) struct BankHashDetails {
26-
/// client version
26+
/// The client version
2727
pub version: String,
28+
/// The encoding format for account data buffers
2829
pub account_data_encoding: String,
30+
/// Bank hash details for a collection of banks
31+
pub bank_hash_details: Vec<BankHashSlotDetails>,
32+
}
33+
34+
impl BankHashDetails {
35+
pub fn new(bank_hash_details: Vec<BankHashSlotDetails>) -> Self {
36+
Self {
37+
version: solana_version::version!().to_string(),
38+
account_data_encoding: "base64".to_string(),
39+
bank_hash_details,
40+
}
41+
}
42+
43+
/// Determines a filename given the currently held bank details
44+
pub fn filename(&self) -> Result<String, String> {
45+
if self.bank_hash_details.is_empty() {
46+
return Err("BankHashDetails does not contains details for any banks".to_string());
47+
}
48+
// From here on, .unwrap() on .first() and .second() is safe as
49+
// self.bank_hash_details is known to be non-empty
50+
let (first_slot, first_hash) = {
51+
let details = self.bank_hash_details.first().unwrap();
52+
(details.slot, &details.bank_hash)
53+
};
54+
55+
let filename = if self.bank_hash_details.len() == 1 {
56+
format!("{first_slot}-{first_hash}.json")
57+
} else {
58+
let (last_slot, last_hash) = {
59+
let details = self.bank_hash_details.last().unwrap();
60+
(details.slot, &details.bank_hash)
61+
};
62+
format!("{first_slot}-{first_hash}_{last_slot}-{last_hash}.json")
63+
};
64+
Ok(filename)
65+
}
66+
}
67+
68+
/// The components that go into a bank hash calculation for a single bank/slot.
69+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
70+
pub(crate) struct BankHashSlotDetails {
2971
pub slot: Slot,
3072
pub bank_hash: String,
3173
pub parent_bank_hash: String,
@@ -35,7 +77,7 @@ pub(crate) struct BankHashDetails {
3577
pub accounts: BankHashAccounts,
3678
}
3779

38-
impl BankHashDetails {
80+
impl BankHashSlotDetails {
3981
pub fn new(
4082
slot: Slot,
4183
bank_hash: Hash,
@@ -46,8 +88,6 @@ impl BankHashDetails {
4688
accounts: BankHashAccounts,
4789
) -> Self {
4890
Self {
49-
version: solana_version::version!().to_string(),
50-
account_data_encoding: "base64".to_string(),
5191
slot,
5292
bank_hash: bank_hash.to_string(),
5393
parent_bank_hash: parent_bank_hash.to_string(),
@@ -59,7 +99,7 @@ impl BankHashDetails {
5999
}
60100
}
61101

62-
impl TryFrom<&Bank> for BankHashDetails {
102+
impl TryFrom<&Bank> for BankHashSlotDetails {
63103
type Error = String;
64104

65105
fn try_from(bank: &Bank) -> Result<Self, Self::Error> {
@@ -99,15 +139,16 @@ impl TryFrom<&Bank> for BankHashDetails {
99139
}
100140
}
101141

102-
// Wrap the Vec<...> so we can implement custom Serialize/Deserialize traits on the wrapper type
142+
/// Wrapper around a Vec<_> to facilitate custom Serialize/Deserialize trait
143+
/// implementations.
103144
#[derive(Clone, Debug, Eq, PartialEq)]
104145
pub(crate) struct BankHashAccounts {
105146
pub accounts: Vec<PubkeyHashAccount>,
106147
}
107148

108-
#[derive(Deserialize, Serialize)]
109149
/// Used as an intermediate for serializing and deserializing account fields
110150
/// into a human readable format.
151+
#[derive(Deserialize, Serialize)]
111152
struct SerdeAccount {
112153
pubkey: String,
113154
hash: String,
@@ -193,24 +234,22 @@ impl<'de> Deserialize<'de> for BankHashAccounts {
193234
}
194235
}
195236

196-
/// Output the components that comprise bank hash
237+
/// Output the components that comprise the overall bank hash for the supplied `Bank`
197238
pub fn write_bank_hash_details_file(bank: &Bank) -> std::result::Result<(), String> {
198-
let details = BankHashDetails::try_from(bank)?;
239+
let slot_details = BankHashSlotDetails::try_from(bank)?;
240+
let details = BankHashDetails::new(vec![slot_details]);
199241

200-
let slot = details.slot;
201-
let hash = &details.bank_hash;
202-
let file_name = format!("{slot}-{hash}.json");
203242
let parent_dir = bank
204243
.rc
205244
.accounts
206245
.accounts_db
207246
.get_base_working_path()
208247
.join("bank_hash_details");
209-
let path = parent_dir.join(file_name);
248+
let path = parent_dir.join(details.filename()?);
210249
// A file with the same name implies the same hash for this slot. Skip
211250
// rewriting a duplicate file in this scenario
212251
if !path.exists() {
213-
info!("writing details of bank {} to {}", slot, path.display());
252+
info!("writing bank hash details file: {}", path.display());
214253

215254
// std::fs::write may fail (depending on platform) if the full directory
216255
// path does not exist. So, call std::fs_create_dir_all first.
@@ -228,44 +267,54 @@ pub fn write_bank_hash_details_file(bank: &Bank) -> std::result::Result<(), Stri
228267
pub mod tests {
229268
use super::*;
230269

231-
#[test]
232-
fn test_serde_bank_hash_details() {
233-
use solana_sdk::hash::hash;
270+
fn build_details(num_slots: usize) -> BankHashDetails {
271+
use solana_sdk::hash::{hash, hashv};
234272

235-
let slot = 123_456_789;
236-
let signature_count = 314;
273+
let slot_details: Vec<_> = (0..num_slots)
274+
.map(|slot| {
275+
let signature_count = 314;
237276

238-
let account = AccountSharedData::from(Account {
239-
lamports: 123_456_789,
240-
data: vec![0, 9, 1, 8, 2, 7, 3, 6, 4, 5],
241-
owner: Pubkey::new_unique(),
242-
executable: true,
243-
rent_epoch: 123,
244-
});
245-
let account_pubkey = Pubkey::new_unique();
246-
let account_hash = AccountHash(hash("account".as_bytes()));
247-
let accounts = BankHashAccounts {
248-
accounts: vec![PubkeyHashAccount {
249-
pubkey: account_pubkey,
250-
hash: account_hash,
251-
account,
252-
}],
253-
};
277+
let account = AccountSharedData::from(Account {
278+
lamports: 123_456_789,
279+
data: vec![0, 9, 1, 8, 2, 7, 3, 6, 4, 5],
280+
owner: Pubkey::new_unique(),
281+
executable: true,
282+
rent_epoch: 123,
283+
});
284+
let account_pubkey = Pubkey::new_unique();
285+
let account_hash = AccountHash(hash("account".as_bytes()));
286+
let accounts = BankHashAccounts {
287+
accounts: vec![PubkeyHashAccount {
288+
pubkey: account_pubkey,
289+
hash: account_hash,
290+
account,
291+
}],
292+
};
254293

255-
let bank_hash = hash("bank".as_bytes());
256-
let parent_bank_hash = hash("parent_bank".as_bytes());
257-
let accounts_delta_hash = hash("accounts_delta".as_bytes());
258-
let last_blockhash = hash("last_blockhash".as_bytes());
294+
let bank_hash = hashv(&["bank".as_bytes(), &slot.to_le_bytes()]);
295+
let parent_bank_hash = hash("parent_bank".as_bytes());
296+
let accounts_delta_hash = hash("accounts_delta".as_bytes());
297+
let last_blockhash = hash("last_blockhash".as_bytes());
259298

260-
let bank_hash_details = BankHashDetails::new(
261-
slot,
262-
bank_hash,
263-
parent_bank_hash,
264-
accounts_delta_hash,
265-
signature_count,
266-
last_blockhash,
267-
accounts,
268-
);
299+
BankHashSlotDetails::new(
300+
slot as Slot,
301+
bank_hash,
302+
parent_bank_hash,
303+
accounts_delta_hash,
304+
signature_count,
305+
last_blockhash,
306+
accounts,
307+
)
308+
})
309+
.collect();
310+
311+
BankHashDetails::new(slot_details)
312+
}
313+
314+
#[test]
315+
fn test_serde_bank_hash_details() {
316+
let num_slots = 10;
317+
let bank_hash_details = build_details(num_slots);
269318

270319
let serialized_bytes = serde_json::to_vec(&bank_hash_details).unwrap();
271320
let deserialized_bank_hash_details: BankHashDetails =

0 commit comments

Comments
 (0)