2323
2424#[ derive( Clone , Debug , Deserialize , Eq , PartialEq , Serialize ) ]
2525pub ( 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 ) ]
104145pub ( 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 ) ]
111152struct 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`
197238pub 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
228267pub 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