2021-12-14 07:31:42 -08:00
//! AccountInfo represents a reference to AccountSharedData in either an AppendVec or the write cache.
//! AccountInfo is not persisted anywhere between program runs.
//! AccountInfo is purely runtime state.
//! Note that AccountInfo is saved to disk buckets during runtime, but disk buckets are recreated at startup.
2023-02-21 10:08:37 -08:00
use {
crate ::{
accounts_db ::AppendVecId ,
accounts_index ::{ IsCached , ZeroLamport } ,
append_vec ::ALIGN_BOUNDARY_OFFSET ,
} ,
modular_bitfield ::prelude ::* ,
2021-12-13 15:35:07 -08:00
} ;
/// offset within an append vec to account data
pub type Offset = usize ;
2021-12-14 11:08:49 -08:00
/// bytes used to store this account in append vec
2021-12-14 11:50:19 -08:00
/// Note this max needs to be big enough to handle max data len of 10MB, which is a const
pub type StoredSize = u32 ;
2021-12-14 11:08:49 -08:00
2021-12-13 15:35:07 -08:00
/// specify where account data is located
2023-01-03 06:34:05 -08:00
#[ derive(Debug, PartialEq, Eq) ]
2021-12-13 15:35:07 -08:00
pub enum StorageLocation {
AppendVec ( AppendVecId , Offset ) ,
Cached ,
}
2021-12-11 09:47:05 -08:00
2021-12-14 08:27:17 -08:00
impl StorageLocation {
pub fn is_offset_equal ( & self , other : & StorageLocation ) -> bool {
match self {
StorageLocation ::Cached = > {
matches! ( other , StorageLocation ::Cached ) // technically, 2 cached entries match in offset
}
StorageLocation ::AppendVec ( _ , offset ) = > {
match other {
StorageLocation ::Cached = > {
false // 1 cached, 1 not
}
StorageLocation ::AppendVec ( _ , other_offset ) = > other_offset = = offset ,
}
}
}
}
pub fn is_store_id_equal ( & self , other : & StorageLocation ) -> bool {
match self {
StorageLocation ::Cached = > {
matches! ( other , StorageLocation ::Cached ) // 2 cached entries are same store id
}
StorageLocation ::AppendVec ( store_id , _ ) = > {
match other {
StorageLocation ::Cached = > {
false // 1 cached, 1 not
}
StorageLocation ::AppendVec ( other_store_id , _ ) = > other_store_id = = store_id ,
}
}
}
}
}
2021-12-15 13:41:11 -08:00
/// how large the offset we store in AccountInfo is
/// Note this is a smaller datatype than 'Offset'
/// AppendVecs store accounts aligned to u64, so offset is always a multiple of 8 (sizeof(u64))
pub type OffsetReduced = u32 ;
2023-02-21 10:08:37 -08:00
/// This is an illegal value for 'offset'.
/// Account size on disk would have to be pointing to the very last 8 byte value in the max sized append vec.
/// That would mean there was a maximum size of 8 bytes for the last entry in the append vec.
/// A pubkey alone is 32 bytes, so there is no way for a valid offset to be this high of a value.
/// Realistically, a max offset is (1<<31 - 156) bytes or so for an account with zero data length. Of course, this
/// depends on the layout on disk, compression, etc. But, 8 bytes per account will never be possible.
/// So, we use this last value as a sentinel to say that the account info refers to an entry in the write cache.
const CACHED_OFFSET : OffsetReduced = ( 1 < < ( OffsetReduced ::BITS - 1 ) ) - 1 ;
#[ bitfield(bits = 32) ]
#[ repr(C) ]
#[ derive(Debug, Default, Copy, Clone, Eq, PartialEq) ]
pub struct PackedOffsetAndFlags {
/// this provides 2^31 bits, which when multipled by 8 (sizeof(u64)) = 16G, which is the maximum size of an append vec
offset_reduced : B31 ,
/// use 1 bit to specify that the entry is zero lamport
is_zero_lamport : bool ,
}
2022-05-22 18:00:42 -07:00
#[ derive(Default, Debug, PartialEq, Eq, Clone, Copy) ]
2021-12-11 09:47:05 -08:00
pub struct AccountInfo {
/// index identifying the append storage
2021-12-13 19:35:30 -08:00
store_id : AppendVecId ,
2021-12-11 09:47:05 -08:00
2023-02-21 10:08:37 -08:00
/// offset = 'packed_offset_and_flags.offset_reduced()' * ALIGN_BOUNDARY_OFFSET into the storage
2021-12-15 13:41:11 -08:00
/// Note this is a smaller type than 'Offset'
2023-02-21 10:08:37 -08:00
packed_offset_and_flags : PackedOffsetAndFlags ,
2021-12-11 09:47:05 -08:00
2023-02-21 10:08:37 -08:00
stored_size : StoredSize ,
2021-12-11 09:47:05 -08:00
}
impl ZeroLamport for AccountInfo {
fn is_zero_lamport ( & self ) -> bool {
2023-02-21 10:08:37 -08:00
self . packed_offset_and_flags . is_zero_lamport ( )
2021-12-11 09:47:05 -08:00
}
}
2021-12-12 12:36:05 -08:00
2021-12-13 15:35:07 -08:00
impl IsCached for AccountInfo {
fn is_cached ( & self ) -> bool {
2023-02-21 10:08:37 -08:00
self . packed_offset_and_flags . offset_reduced ( ) = = CACHED_OFFSET
2021-12-13 15:35:07 -08:00
}
}
2021-12-14 08:27:17 -08:00
impl IsCached for StorageLocation {
fn is_cached ( & self ) -> bool {
matches! ( self , StorageLocation ::Cached )
}
}
2021-12-16 14:37:08 -08:00
/// We have to have SOME value for store_id when we are cached
const CACHE_VIRTUAL_STORAGE_ID : AppendVecId = AppendVecId ::MAX ;
2021-12-12 12:36:05 -08:00
impl AccountInfo {
2021-12-15 09:49:24 -08:00
pub fn new ( storage_location : StorageLocation , stored_size : StoredSize , lamports : u64 ) -> Self {
2023-02-21 10:08:37 -08:00
let mut packed_offset_and_flags = PackedOffsetAndFlags ::default ( ) ;
let store_id = match storage_location {
StorageLocation ::AppendVec ( store_id , offset ) = > {
let reduced_offset = Self ::get_reduced_offset ( offset ) ;
assert_ne! (
CACHED_OFFSET , reduced_offset ,
" illegal offset for non-cached item "
) ;
packed_offset_and_flags . set_offset_reduced ( Self ::get_reduced_offset ( offset ) ) ;
assert_eq! (
Self ::reduced_offset_to_offset ( packed_offset_and_flags . offset_reduced ( ) ) ,
offset ,
" illegal offset "
) ;
store_id
}
2021-12-15 09:49:24 -08:00
StorageLocation ::Cached = > {
2023-02-21 10:08:37 -08:00
packed_offset_and_flags . set_offset_reduced ( CACHED_OFFSET ) ;
CACHE_VIRTUAL_STORAGE_ID
2021-12-15 09:49:24 -08:00
}
2021-12-13 15:35:07 -08:00
} ;
2023-02-21 10:08:37 -08:00
packed_offset_and_flags . set_is_zero_lamport ( lamports = = 0 ) ;
Self {
2021-12-12 12:36:05 -08:00
store_id ,
2023-02-21 10:08:37 -08:00
packed_offset_and_flags ,
stored_size ,
}
}
fn get_reduced_offset ( offset : usize ) -> OffsetReduced {
( offset / ALIGN_BOUNDARY_OFFSET ) as OffsetReduced
2021-12-12 12:36:05 -08:00
}
2021-12-13 08:10:06 -08:00
2021-12-14 11:08:49 -08:00
pub fn store_id ( & self ) -> AppendVecId {
2022-02-15 13:22:36 -08:00
// if the account is in a cached store, the store_id is meaningless
assert! ( ! self . is_cached ( ) ) ;
2021-12-13 19:35:30 -08:00
self . store_id
}
2021-12-14 11:08:49 -08:00
pub fn offset ( & self ) -> Offset {
2023-02-21 10:08:37 -08:00
Self ::reduced_offset_to_offset ( self . packed_offset_and_flags . offset_reduced ( ) )
}
fn reduced_offset_to_offset ( reduced_offset : OffsetReduced ) -> Offset {
( reduced_offset as Offset ) * ALIGN_BOUNDARY_OFFSET
2021-12-13 12:43:26 -08:00
}
2021-12-14 11:08:49 -08:00
pub fn stored_size ( & self ) -> StoredSize {
2023-02-21 10:08:37 -08:00
self . stored_size
2021-12-13 08:10:06 -08:00
}
2021-12-14 08:27:17 -08:00
pub fn storage_location ( & self ) -> StorageLocation {
if self . is_cached ( ) {
StorageLocation ::Cached
} else {
2021-12-15 13:41:11 -08:00
StorageLocation ::AppendVec ( self . store_id , self . offset ( ) )
}
}
}
#[ cfg(test) ]
mod test {
use { super ::* , crate ::append_vec ::MAXIMUM_APPEND_VEC_FILE_SIZE } ;
#[ test ]
fn test_limits ( ) {
for offset in [
2023-02-21 10:08:37 -08:00
// MAXIMUM_APPEND_VEC_FILE_SIZE is too big. That would be an offset at the first invalid byte in the max file size.
// MAXIMUM_APPEND_VEC_FILE_SIZE - 8 bytes would reference the very last 8 bytes in the file size. It makes no sense to reference that since element sizes are always more than 8.
// MAXIMUM_APPEND_VEC_FILE_SIZE - 16 bytes would reference the second to last 8 bytes in the max file size. This is still likely meaningless, but it is 'valid' as far as the index
// is concerned.
( MAXIMUM_APPEND_VEC_FILE_SIZE - 2 * ( ALIGN_BOUNDARY_OFFSET as u64 ) ) as Offset ,
2021-12-15 13:41:11 -08:00
0 ,
ALIGN_BOUNDARY_OFFSET ,
4 * ALIGN_BOUNDARY_OFFSET ,
] {
let info = AccountInfo ::new ( StorageLocation ::AppendVec ( 0 , offset ) , 0 , 0 ) ;
assert! ( info . offset ( ) = = offset ) ;
2021-12-14 08:27:17 -08:00
}
}
2021-12-15 13:41:11 -08:00
2023-02-21 10:08:37 -08:00
#[ test ]
#[ should_panic(expected = " illegal offset " ) ]
fn test_illegal_offset ( ) {
let offset = ( MAXIMUM_APPEND_VEC_FILE_SIZE - ( ALIGN_BOUNDARY_OFFSET as u64 ) ) as Offset ;
AccountInfo ::new ( StorageLocation ::AppendVec ( 0 , offset ) , 0 , 0 ) ;
}
2021-12-15 13:41:11 -08:00
#[ test ]
#[ should_panic(expected = " illegal offset " ) ]
fn test_alignment ( ) {
let offset = 1 ; // not aligned
AccountInfo ::new ( StorageLocation ::AppendVec ( 0 , offset ) , 0 , 0 ) ;
}
2021-12-12 12:36:05 -08:00
}