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

Commit d0623a3

Browse files
authored
API to match account owner against a set of owners (#30154)
1 parent bfc29a6 commit d0623a3

File tree

2 files changed

+232
-4
lines changed

2 files changed

+232
-4
lines changed

runtime/src/accounts_db.rs

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ use {
4242
get_ancient_append_vec_capacity, is_ancient, AccountsToStore, StorageSelector,
4343
},
4444
append_vec::{
45-
aligned_stored_size, AppendVec, StorableAccountsWithHashesAndWriteVersions,
46-
StoredAccountMeta, StoredMetaWriteVersion, APPEND_VEC_MMAPPED_FILES_OPEN,
47-
STORE_META_OVERHEAD,
45+
aligned_stored_size, AppendVec, MatchAccountOwnerError,
46+
StorableAccountsWithHashesAndWriteVersions, StoredAccountMeta, StoredMetaWriteVersion,
47+
APPEND_VEC_MMAPPED_FILES_OPEN, STORE_META_OVERHEAD,
4848
},
4949
cache_hash_data::{CacheHashData, CacheHashDataFile},
5050
contains::Contains,
@@ -815,6 +815,32 @@ impl<'a> LoadedAccountAccessor<'a> {
815815
}
816816
}
817817
}
818+
819+
fn account_matches_owners(&self, owners: &[&Pubkey]) -> Result<(), MatchAccountOwnerError> {
820+
match self {
821+
LoadedAccountAccessor::Cached(cached_account) => cached_account
822+
.as_ref()
823+
.and_then(|cached_account| {
824+
(!cached_account.account.is_zero_lamport()
825+
&& owners.contains(&cached_account.account.owner()))
826+
.then_some(())
827+
})
828+
.ok_or(MatchAccountOwnerError::NoMatch),
829+
LoadedAccountAccessor::Stored(maybe_storage_entry) => {
830+
// storage entry may not be present if slot was cleaned up in
831+
// between reading the accounts index and calling this function to
832+
// get account meta from the storage entry here
833+
maybe_storage_entry
834+
.as_ref()
835+
.map(|(storage_entry, offset)| {
836+
storage_entry
837+
.accounts
838+
.account_matches_owners(*offset, owners)
839+
})
840+
.unwrap_or(Err(MatchAccountOwnerError::UnableToLoad))
841+
}
842+
}
843+
}
818844
}
819845

820846
pub enum LoadedAccount<'a> {
@@ -4918,6 +4944,38 @@ impl AccountsDb {
49184944
self.do_load(ancestors, pubkey, None, load_hint, LoadZeroLamports::None)
49194945
}
49204946

4947+
pub fn account_matches_owners(
4948+
&self,
4949+
ancestors: &Ancestors,
4950+
account: &Pubkey,
4951+
owners: &[&Pubkey],
4952+
) -> Result<(), MatchAccountOwnerError> {
4953+
let (slot, storage_location, _maybe_account_accesor) = self
4954+
.read_index_for_accessor_or_load_slow(ancestors, account, None, false)
4955+
.ok_or(MatchAccountOwnerError::UnableToLoad)?;
4956+
4957+
if !storage_location.is_cached() {
4958+
let result = self.read_only_accounts_cache.load(*account, slot);
4959+
if let Some(account) = result {
4960+
return (!account.is_zero_lamport() && owners.contains(&account.owner()))
4961+
.then_some(())
4962+
.ok_or(MatchAccountOwnerError::NoMatch);
4963+
}
4964+
}
4965+
4966+
let (account_accessor, _slot) = self
4967+
.retry_to_get_account_accessor(
4968+
slot,
4969+
storage_location,
4970+
ancestors,
4971+
account,
4972+
None,
4973+
LoadHint::Unspecified,
4974+
)
4975+
.ok_or(MatchAccountOwnerError::UnableToLoad)?;
4976+
account_accessor.account_matches_owners(owners)
4977+
}
4978+
49214979
pub fn load_account_into_read_cache(&self, ancestors: &Ancestors, pubkey: &Pubkey) {
49224980
self.do_load_with_populate_read_cache(
49234981
ancestors,
@@ -14068,6 +14126,101 @@ pub mod tests {
1406814126
assert_eq!(db.read_only_accounts_cache.cache_len(), 1);
1406914127
}
1407014128

14129+
#[test]
14130+
fn test_account_matches_owners() {
14131+
let db = Arc::new(AccountsDb::new_with_config_for_tests(
14132+
Vec::new(),
14133+
&ClusterType::Development,
14134+
AccountSecondaryIndexes::default(),
14135+
AccountShrinkThreshold::default(),
14136+
));
14137+
14138+
let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
14139+
let owners_refs: Vec<&Pubkey> = owners.iter().collect();
14140+
14141+
let account1_key = Pubkey::new_unique();
14142+
let account1 = AccountSharedData::new(321, 10, &owners[0]);
14143+
14144+
let account2_key = Pubkey::new_unique();
14145+
let account2 = AccountSharedData::new(1, 1, &owners[1]);
14146+
14147+
let account3_key = Pubkey::new_unique();
14148+
let account3 = AccountSharedData::new(1, 1, &Pubkey::new_unique());
14149+
14150+
// Account with 0 lamports
14151+
let account4_key = Pubkey::new_unique();
14152+
let account4 = AccountSharedData::new(0, 1, &owners[1]);
14153+
14154+
db.store_cached((0, &[(&account1_key, &account1)][..]), None);
14155+
db.store_cached((1, &[(&account2_key, &account2)][..]), None);
14156+
db.store_cached((2, &[(&account3_key, &account3)][..]), None);
14157+
db.store_cached((3, &[(&account4_key, &account4)][..]), None);
14158+
14159+
db.add_root(0);
14160+
db.add_root(1);
14161+
db.add_root(2);
14162+
db.add_root(3);
14163+
14164+
// Flush the cache so that the account meta will be read from the storage
14165+
db.flush_accounts_cache(true, None);
14166+
db.clean_accounts_for_tests();
14167+
14168+
assert_eq!(
14169+
db.account_matches_owners(&Ancestors::default(), &account1_key, &owners_refs),
14170+
Ok(())
14171+
);
14172+
assert_eq!(
14173+
db.account_matches_owners(&Ancestors::default(), &account2_key, &owners_refs),
14174+
Ok(())
14175+
);
14176+
assert_eq!(
14177+
db.account_matches_owners(&Ancestors::default(), &account3_key, &owners_refs),
14178+
Err(MatchAccountOwnerError::NoMatch)
14179+
);
14180+
assert_eq!(
14181+
db.account_matches_owners(&Ancestors::default(), &account4_key, &owners_refs),
14182+
Err(MatchAccountOwnerError::NoMatch)
14183+
);
14184+
assert_eq!(
14185+
db.account_matches_owners(&Ancestors::default(), &Pubkey::new_unique(), &owners_refs),
14186+
Err(MatchAccountOwnerError::UnableToLoad)
14187+
);
14188+
14189+
// Flush the cache and load account1 (so that it's in the cache)
14190+
db.flush_accounts_cache(true, None);
14191+
db.clean_accounts_for_tests();
14192+
let _ = db
14193+
.do_load(
14194+
&Ancestors::default(),
14195+
&account1_key,
14196+
Some(0),
14197+
LoadHint::Unspecified,
14198+
LoadZeroLamports::SomeWithZeroLamportAccountForTests,
14199+
)
14200+
.unwrap();
14201+
14202+
assert_eq!(
14203+
db.account_matches_owners(&Ancestors::default(), &account1_key, &owners_refs),
14204+
Ok(())
14205+
);
14206+
assert_eq!(
14207+
db.account_matches_owners(&Ancestors::default(), &account2_key, &owners_refs),
14208+
Ok(())
14209+
);
14210+
assert_eq!(
14211+
db.account_matches_owners(&Ancestors::default(), &account3_key, &owners_refs),
14212+
Err(MatchAccountOwnerError::NoMatch)
14213+
);
14214+
assert_eq!(
14215+
db.account_matches_owners(&Ancestors::default(), &account4_key, &owners_refs),
14216+
Err(MatchAccountOwnerError::NoMatch)
14217+
);
14218+
assert_eq!(
14219+
db.account_matches_owners(&Ancestors::default(), &Pubkey::new_unique(), &owners_refs),
14220+
Err(MatchAccountOwnerError::UnableToLoad)
14221+
);
14222+
}
14223+
1407114224
/// a test that will accept either answer
1407214225
const LOAD_ZERO_LAMPORTS_ANY_TESTS: LoadZeroLamports = LoadZeroLamports::None;
1407314226

runtime/src/append_vec.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use {
2828
Mutex,
2929
},
3030
},
31+
thiserror::Error,
3132
};
3233

3334
pub mod test_utils;
@@ -270,6 +271,14 @@ impl<'a> Iterator for AppendVecAccountsIter<'a> {
270271
}
271272
}
272273

274+
#[derive(Error, Debug, PartialEq, Eq)]
275+
pub enum MatchAccountOwnerError {
276+
#[error("The account owner does not match with the provided list")]
277+
NoMatch,
278+
#[error("Unable to load the account")]
279+
UnableToLoad,
280+
}
281+
273282
/// A thread-safe, file-backed block of memory used to store `Account` instances. Append operations
274283
/// are serialized such that only one thread updates the internal `append_lock` at a time. No
275284
/// restrictions are placed on reading. That is, one may read items from one thread while another
@@ -572,7 +581,7 @@ impl AppendVec {
572581
Some((unsafe { &*ptr }, next))
573582
}
574583

575-
/// Return account metadata for the account at `offset` if its data doesn't overrun
584+
/// Return stored account metadata for the account at `offset` if its data doesn't overrun
576585
/// the internal buffer. Otherwise return None. Also return the offset of the first byte
577586
/// after the requested data that falls on a 64-byte boundary.
578587
pub fn get_account<'a>(&'a self, offset: usize) -> Option<(StoredAccountMeta<'a>, usize)> {
@@ -594,6 +603,31 @@ impl AppendVec {
594603
))
595604
}
596605

606+
fn get_account_meta<'a>(&self, offset: usize) -> Option<&'a AccountMeta> {
607+
// Skip over StoredMeta data in the account
608+
let offset = offset.checked_add(mem::size_of::<StoredMeta>())?;
609+
// u64_align! does an unchecked add for alignment. Check that it won't cause an overflow.
610+
offset.checked_add(ALIGN_BOUNDARY_OFFSET - 1)?;
611+
let (account_meta, _): (&AccountMeta, _) = self.get_type(u64_align!(offset))?;
612+
Some(account_meta)
613+
}
614+
615+
/// Return Some(true) if the account owner at `offset` is one of the pubkeys in `owners`.
616+
/// Return Some(false) if the account owner is not one of the pubkeys in `owners`.
617+
/// It returns None if the `offset` value causes a data overrun.
618+
pub fn account_matches_owners(
619+
&self,
620+
offset: usize,
621+
owners: &[&Pubkey],
622+
) -> Result<(), MatchAccountOwnerError> {
623+
let account_meta = self
624+
.get_account_meta(offset)
625+
.ok_or(MatchAccountOwnerError::UnableToLoad)?;
626+
(account_meta.lamports != 0 && owners.contains(&&account_meta.owner))
627+
.then_some(())
628+
.ok_or(MatchAccountOwnerError::NoMatch)
629+
}
630+
597631
#[cfg(test)]
598632
pub fn get_account_test(&self, offset: usize) -> Option<(StoredMeta, AccountSharedData)> {
599633
let (stored_account, _) = self.get_account(offset)?;
@@ -1047,6 +1081,47 @@ pub mod tests {
10471081
assert_eq!(av.get_account_test(index1).unwrap(), account1);
10481082
}
10491083

1084+
#[test]
1085+
fn test_account_matches_owners() {
1086+
let path = get_append_vec_path("test_append_data");
1087+
let av = AppendVec::new(&path.path, true, 1024 * 1024);
1088+
let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1089+
let owners_refs: Vec<&Pubkey> = owners.iter().collect();
1090+
1091+
let mut account = create_test_account(5);
1092+
account.1.set_owner(owners[0]);
1093+
let index = av.append_account_test(&account).unwrap();
1094+
assert_eq!(av.account_matches_owners(index, &owners_refs), Ok(()));
1095+
1096+
let mut account1 = create_test_account(6);
1097+
account1.1.set_owner(owners[1]);
1098+
let index1 = av.append_account_test(&account1).unwrap();
1099+
assert_eq!(av.account_matches_owners(index1, &owners_refs), Ok(()));
1100+
assert_eq!(av.account_matches_owners(index, &owners_refs), Ok(()));
1101+
1102+
let mut account2 = create_test_account(6);
1103+
account2.1.set_owner(Pubkey::new_unique());
1104+
let index2 = av.append_account_test(&account2).unwrap();
1105+
assert_eq!(
1106+
av.account_matches_owners(index2, &owners_refs),
1107+
Err(MatchAccountOwnerError::NoMatch)
1108+
);
1109+
1110+
// tests for overflow
1111+
assert_eq!(
1112+
av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners_refs),
1113+
Err(MatchAccountOwnerError::UnableToLoad)
1114+
);
1115+
1116+
assert_eq!(
1117+
av.account_matches_owners(
1118+
usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1119+
&owners_refs
1120+
),
1121+
Err(MatchAccountOwnerError::UnableToLoad)
1122+
);
1123+
}
1124+
10501125
#[test]
10511126
fn test_append_vec_append_many() {
10521127
let path = get_append_vec_path("test_append_many");

0 commit comments

Comments
 (0)