1use {
3 crate::{accounts_db::IncludeSlotInHash, append_vec::StoredAccountMeta},
4 solana_sdk::{account::ReadableAccount, clock::Slot, hash::Hash, pubkey::Pubkey},
5};
6
7pub trait StorableAccounts<'a, T: ReadableAccount + Sync>: Sync {
13 fn pubkey(&self, index: usize) -> &Pubkey;
15 fn account(&self, index: usize) -> &T;
17 fn account_default_if_zero_lamport(&self, index: usize) -> Option<&T> {
19 let account = self.account(index);
20 (account.lamports() != 0).then_some(account)
21 }
22 fn slot(&self, index: usize) -> Slot;
24 fn target_slot(&self) -> Slot;
26 fn is_empty(&self) -> bool {
28 self.len() == 0
29 }
30 fn len(&self) -> usize;
32 fn contains_multiple_slots(&self) -> bool {
35 false
36 }
37 fn include_slot_in_hash(&self) -> IncludeSlotInHash;
39
40 fn has_hash_and_write_version(&self) -> bool {
43 false
44 }
45
46 fn hash(&self, _index: usize) -> &Hash {
49 unimplemented!();
51 }
52
53 fn write_version(&self, _index: usize) -> u64 {
56 unimplemented!();
58 }
59}
60
61pub struct StorableAccountsMovingSlots<'a, T: ReadableAccount + Sync> {
66 pub accounts: &'a [(&'a Pubkey, &'a T)],
67 pub target_slot: Slot,
69 pub old_slot: Slot,
71 pub include_slot_in_hash: IncludeSlotInHash,
73}
74
75impl<'a, T: ReadableAccount + Sync> StorableAccounts<'a, T> for StorableAccountsMovingSlots<'a, T> {
76 fn pubkey(&self, index: usize) -> &Pubkey {
77 self.accounts[index].0
78 }
79 fn account(&self, index: usize) -> &T {
80 self.accounts[index].1
81 }
82 fn slot(&self, _index: usize) -> Slot {
83 self.old_slot
85 }
86 fn target_slot(&self) -> Slot {
87 self.target_slot
88 }
89 fn len(&self) -> usize {
90 self.accounts.len()
91 }
92 fn include_slot_in_hash(&self) -> IncludeSlotInHash {
93 self.include_slot_in_hash
94 }
95}
96
97impl<'a, T: ReadableAccount + Sync> StorableAccounts<'a, T>
100 for (Slot, &'a [(&'a Pubkey, &'a T)], IncludeSlotInHash)
101{
102 fn pubkey(&self, index: usize) -> &Pubkey {
103 self.1[index].0
104 }
105 fn account(&self, index: usize) -> &T {
106 self.1[index].1
107 }
108 fn slot(&self, _index: usize) -> Slot {
109 self.target_slot()
111 }
112 fn target_slot(&self) -> Slot {
113 self.0
114 }
115 fn len(&self) -> usize {
116 self.1.len()
117 }
118 fn include_slot_in_hash(&self) -> IncludeSlotInHash {
119 self.2
120 }
121}
122
123impl<'a> StorableAccounts<'a, StoredAccountMeta<'a>>
126 for (Slot, &'a [&'a StoredAccountMeta<'a>], IncludeSlotInHash)
127{
128 fn pubkey(&self, index: usize) -> &Pubkey {
129 self.account(index).pubkey()
130 }
131 fn account(&self, index: usize) -> &StoredAccountMeta<'a> {
132 self.1[index]
133 }
134 fn slot(&self, _index: usize) -> Slot {
135 self.0
137 }
138 fn target_slot(&self) -> Slot {
139 self.0
140 }
141 fn len(&self) -> usize {
142 self.1.len()
143 }
144 fn include_slot_in_hash(&self) -> IncludeSlotInHash {
145 self.2
146 }
147 fn has_hash_and_write_version(&self) -> bool {
148 true
149 }
150 fn hash(&self, index: usize) -> &Hash {
151 self.account(index).hash
152 }
153 fn write_version(&self, index: usize) -> u64 {
154 self.account(index).meta.write_version_obsolete
155 }
156}
157
158pub struct StorableAccountsBySlot<'a> {
160 target_slot: Slot,
161 slots_and_accounts: &'a [(Slot, &'a [&'a StoredAccountMeta<'a>])],
163 include_slot_in_hash: IncludeSlotInHash,
164
165 starting_offsets: Vec<usize>,
170 contains_multiple_slots: bool,
172 len: usize,
174}
175
176impl<'a> StorableAccountsBySlot<'a> {
177 #[allow(dead_code)]
178 pub(crate) fn new(
180 target_slot: Slot,
181 slots_and_accounts: &'a [(Slot, &'a [&'a StoredAccountMeta<'a>])],
182 include_slot_in_hash: IncludeSlotInHash,
183 ) -> Self {
184 let mut cumulative_len = 0usize;
185 let mut starting_offsets = Vec::with_capacity(slots_and_accounts.len());
186 let first_slot = slots_and_accounts
187 .first()
188 .map(|(slot, _)| *slot)
189 .unwrap_or_default();
190 let mut contains_multiple_slots = false;
191 for (slot, accounts) in slots_and_accounts {
192 cumulative_len = cumulative_len.saturating_add(accounts.len());
193 starting_offsets.push(cumulative_len);
194 contains_multiple_slots |= &first_slot != slot;
195 }
196 Self {
197 target_slot,
198 slots_and_accounts,
199 starting_offsets,
200 include_slot_in_hash,
201 contains_multiple_slots,
202 len: cumulative_len,
203 }
204 }
205 fn find_internal_index(&self, index: usize) -> (usize, usize) {
208 for (offset_index, next_offset) in self.starting_offsets.iter().enumerate() {
211 if next_offset > &index {
212 let prior_offset = if offset_index > 0 {
214 self.starting_offsets[offset_index.saturating_sub(1)]
215 } else {
216 0
217 };
218 return (offset_index, index - prior_offset);
219 }
220 }
221 panic!("failed");
222 }
223}
224
225impl<'a> StorableAccounts<'a, StoredAccountMeta<'a>> for StorableAccountsBySlot<'a> {
228 fn pubkey(&self, index: usize) -> &Pubkey {
229 self.account(index).pubkey()
230 }
231 fn account(&self, index: usize) -> &StoredAccountMeta<'a> {
232 let indexes = self.find_internal_index(index);
233 self.slots_and_accounts[indexes.0].1[indexes.1]
234 }
235 fn slot(&self, index: usize) -> Slot {
236 let indexes = self.find_internal_index(index);
237 self.slots_and_accounts[indexes.0].0
238 }
239 fn target_slot(&self) -> Slot {
240 self.target_slot
241 }
242 fn len(&self) -> usize {
243 self.len
244 }
245 fn contains_multiple_slots(&self) -> bool {
246 self.contains_multiple_slots
247 }
248 fn include_slot_in_hash(&self) -> IncludeSlotInHash {
249 self.include_slot_in_hash
250 }
251 fn has_hash_and_write_version(&self) -> bool {
252 true
253 }
254 fn hash(&self, index: usize) -> &Hash {
255 self.account(index).hash
256 }
257 fn write_version(&self, index: usize) -> u64 {
258 self.account(index).meta.write_version_obsolete
259 }
260}
261
262impl<'a> StorableAccounts<'a, StoredAccountMeta<'a>>
265 for (
266 Slot,
267 &'a [&'a StoredAccountMeta<'a>],
268 IncludeSlotInHash,
269 Slot,
270 )
271{
272 fn pubkey(&self, index: usize) -> &Pubkey {
273 self.account(index).pubkey()
274 }
275 fn account(&self, index: usize) -> &StoredAccountMeta<'a> {
276 self.1[index]
277 }
278 fn slot(&self, _index: usize) -> Slot {
279 self.3
281 }
282 fn target_slot(&self) -> Slot {
283 self.0
284 }
285 fn len(&self) -> usize {
286 self.1.len()
287 }
288 fn include_slot_in_hash(&self) -> IncludeSlotInHash {
289 self.2
290 }
291 fn has_hash_and_write_version(&self) -> bool {
292 true
293 }
294 fn hash(&self, index: usize) -> &Hash {
295 self.account(index).hash
296 }
297 fn write_version(&self, index: usize) -> u64 {
298 self.account(index).meta.write_version_obsolete
299 }
300}
301
302#[cfg(test)]
303pub mod tests {
304 use {
305 super::*,
306 crate::{
307 accounts_db::INCLUDE_SLOT_IN_HASH_TESTS,
308 append_vec::{AccountMeta, StoredAccountMeta, StoredMeta},
309 },
310 solana_sdk::{
311 account::{accounts_equal, AccountSharedData, WritableAccount},
312 hash::Hash,
313 },
314 };
315
316 fn compare<
317 'a,
318 T: ReadableAccount + Sync + PartialEq + std::fmt::Debug,
319 U: ReadableAccount + Sync + PartialEq + std::fmt::Debug,
320 >(
321 a: &impl StorableAccounts<'a, T>,
322 b: &impl StorableAccounts<'a, U>,
323 ) {
324 assert_eq!(a.target_slot(), b.target_slot());
325 assert_eq!(a.len(), b.len());
326 assert_eq!(a.is_empty(), b.is_empty());
327 (0..a.len()).for_each(|i| {
328 assert_eq!(a.pubkey(i), b.pubkey(i));
329 assert!(accounts_equal(a.account(i), b.account(i)));
330 })
331 }
332
333 #[test]
334 fn test_contains_multiple_slots() {
335 let pk = Pubkey::from([1; 32]);
336 let slot = 0;
337 let lamports = 1;
338 let owner = Pubkey::default();
339 let executable = false;
340 let rent_epoch = 0;
341 let meta = StoredMeta {
342 write_version_obsolete: 5,
343 pubkey: pk,
344 data_len: 7,
345 };
346 let account_meta = AccountMeta {
347 lamports,
348 owner,
349 executable,
350 rent_epoch,
351 };
352 let data = Vec::default();
353 let offset = 99;
354 let stored_size = 101;
355 let hash = Hash::new_unique();
356 let stored_account = StoredAccountMeta {
357 meta: &meta,
358 account_meta: &account_meta,
359 data: &data,
360 offset,
361 stored_size,
362 hash: &hash,
363 };
364
365 let test3 = (
366 slot,
367 &vec![&stored_account, &stored_account][..],
368 INCLUDE_SLOT_IN_HASH_TESTS,
369 slot,
370 );
371 assert!(!test3.contains_multiple_slots());
372 }
373
374 #[test]
375 fn test_storable_accounts() {
376 let max_slots = 3_u64;
377 for target_slot in 0..max_slots {
378 for entries in 0..2 {
379 for starting_slot in 0..max_slots {
380 let data = Vec::default();
381 let hash = Hash::new_unique();
382 let mut raw = Vec::new();
383 let mut raw2 = Vec::new();
384 for entry in 0..entries {
385 let pk = Pubkey::from([entry; 32]);
386 let account = AccountSharedData::create(
387 (entry as u64) * starting_slot,
388 Vec::default(),
389 Pubkey::default(),
390 false,
391 0,
392 );
393
394 raw.push((
395 pk,
396 account.clone(),
397 starting_slot % max_slots,
398 StoredMeta {
399 write_version_obsolete: 0, pubkey: pk,
401 data_len: u64::MAX, },
403 AccountMeta {
404 lamports: account.lamports(),
405 owner: *account.owner(),
406 executable: account.executable(),
407 rent_epoch: account.rent_epoch(),
408 },
409 ));
410 }
411 for entry in 0..entries {
412 let offset = 99;
413 let stored_size = 101;
414 raw2.push(StoredAccountMeta {
415 meta: &raw[entry as usize].3,
416 account_meta: &raw[entry as usize].4,
417 data: &data,
418 offset,
419 stored_size,
420 hash: &hash,
421 });
422 }
423
424 let mut two = Vec::new();
425 let mut three = Vec::new();
426 raw.iter().zip(raw2.iter()).for_each(|(raw, raw2)| {
427 two.push((&raw.0, &raw.1)); three.push(raw2);
429 });
430 let test2 = (target_slot, &two[..], INCLUDE_SLOT_IN_HASH_TESTS);
431
432 let source_slot = starting_slot % max_slots;
433 let test3 = (
434 target_slot,
435 &three[..],
436 INCLUDE_SLOT_IN_HASH_TESTS,
437 source_slot,
438 );
439 let old_slot = starting_slot;
440 let test_moving_slots = StorableAccountsMovingSlots {
441 accounts: &two[..],
442 target_slot,
443 old_slot,
444 include_slot_in_hash: INCLUDE_SLOT_IN_HASH_TESTS,
445 };
446 let for_slice = [(old_slot, &three[..])];
447 let test_moving_slots2 = StorableAccountsBySlot::new(
448 target_slot,
449 &for_slice,
450 INCLUDE_SLOT_IN_HASH_TESTS,
451 );
452 compare(&test2, &test3);
453 compare(&test2, &test_moving_slots);
454 compare(&test2, &test_moving_slots2);
455 for (i, raw) in raw.iter().enumerate() {
456 assert_eq!(raw.0, *test3.pubkey(i));
457 assert!(accounts_equal(&raw.1, test3.account(i)));
458 assert_eq!(raw.2, test3.slot(i));
459 assert_eq!(target_slot, test2.slot(i));
460 assert_eq!(old_slot, test_moving_slots.slot(i));
461 assert_eq!(old_slot, test_moving_slots2.slot(i));
462 }
463 assert_eq!(target_slot, test3.target_slot());
464 assert_eq!(target_slot, test_moving_slots2.target_slot());
465 assert!(!test2.contains_multiple_slots());
466 assert!(!test_moving_slots.contains_multiple_slots());
467 assert_eq!(test3.contains_multiple_slots(), entries > 1);
468 }
469 }
470 }
471 }
472
473 #[test]
474 fn test_storable_accounts_by_slot() {
475 solana_logger::setup();
476 for entries in 0..6 {
479 let data = Vec::default();
480 let hashes = (0..entries).map(|_| Hash::new_unique()).collect::<Vec<_>>();
481 let mut raw = Vec::new();
482 let mut raw2 = Vec::new();
483 for entry in 0..entries {
484 let pk = Pubkey::from([entry; 32]);
485 let account = AccountSharedData::create(
486 entry as u64,
487 Vec::default(),
488 Pubkey::default(),
489 false,
490 0,
491 );
492 raw.push((
493 pk,
494 account.clone(),
495 StoredMeta {
496 write_version_obsolete: 500 + (entry * 3) as u64, pubkey: pk,
498 data_len: (entry * 2) as u64, },
500 AccountMeta {
501 lamports: account.lamports(),
502 owner: *account.owner(),
503 executable: account.executable(),
504 rent_epoch: account.rent_epoch(),
505 },
506 ));
507 }
508 for entry in 0..entries {
509 let offset = 99;
510 let stored_size = 101;
511 raw2.push(StoredAccountMeta {
512 meta: &raw[entry as usize].2,
513 account_meta: &raw[entry as usize].3,
514 data: &data,
515 offset,
516 stored_size,
517 hash: &hashes[entry as usize],
518 });
519 }
520 let raw2_refs = raw2.iter().collect::<Vec<_>>();
521
522 for entries0 in 0..=entries {
524 let remaining1 = entries.saturating_sub(entries0);
525 for entries1 in 0..=remaining1 {
526 let remaining2 = entries.saturating_sub(entries0 + entries1);
527 for entries2 in 0..=remaining2 {
528 let remaining3 = entries.saturating_sub(entries0 + entries1 + entries2);
529 let entries_by_level = vec![entries0, entries1, entries2, remaining3];
530 let mut overall_index = 0;
531 let mut expected_slots = Vec::default();
532 let slots_and_accounts = entries_by_level
533 .iter()
534 .enumerate()
535 .filter_map(|(slot, count)| {
536 let slot = slot as Slot;
537 let count = *count as usize;
538 (overall_index < raw2.len()).then(|| {
539 let range = overall_index..(overall_index + count);
540 let result = &raw2_refs[range.clone()];
541 range.for_each(|_| expected_slots.push(slot));
542 overall_index += count;
543 (slot, result)
544 })
545 })
546 .collect::<Vec<_>>();
547 let storable = StorableAccountsBySlot::new(
548 99,
549 &slots_and_accounts[..],
550 INCLUDE_SLOT_IN_HASH_TESTS,
551 );
552 assert!(storable.has_hash_and_write_version());
553 assert_eq!(99, storable.target_slot());
554 assert_eq!(entries0 != entries, storable.contains_multiple_slots());
555 (0..entries).for_each(|index| {
556 let index = index as usize;
557 assert_eq!(storable.account(index), &raw2[index]);
558 assert_eq!(storable.pubkey(index), raw2[index].pubkey());
559 assert_eq!(storable.hash(index), raw2[index].hash);
560 assert_eq!(storable.slot(index), expected_slots[index]);
561 assert_eq!(
562 storable.write_version(index),
563 raw2[index].meta.write_version_obsolete
564 );
565 })
566 }
567 }
568 }
569 }
570 }
571}