-
Notifications
You must be signed in to change notification settings - Fork 4.6k
API to match account owner against a set of owners #30154
Conversation
looks reasonable to me. Not ideal to be copying these fields out (like owner). Depends on usage I guess. I'm not sure what happens in the case where the account is in the read cache. |
Actually owner is the specific field we need for runtime V2. If the owner is rBPF/SBF loader, we'll treat it as an executable. (The executable flag by itself will be deprecated in the future). |
Depends then on how many times this is called then. Is it just on suspected exes or is it for every account in every tx? |
Yes that's a good point. I'll update the PR. Thanks for reviewing it! |
Updated the PR as per your suggestion. |
pub fn get_account_meta(&self, offset: usize) -> Option<AccountMeta> { | ||
// Skip over StoredMeta data in the account | ||
let offset = offset.checked_add(mem::size_of::<StoredMeta>())?; | ||
// u64_align! does an unchecked add for alignment. Check that it won't cause an overflow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this a danger in the current callers of u64_align
right now like
solana/runtime/src/append_vec.rs
Line 516 in 9ed4eac
let next = u64_align!(next); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a side note it might be good to extract all of these "align and offset" calculation functions into a common utility that can be reused in places like this:
solana/runtime/src/append_vec.rs
Lines 511 to 516 in 9ed4eac
let (next, overflow) = offset.overflowing_add(size); | |
if overflow || next > self.len() { | |
return None; | |
} | |
let data = &self.map[offset..next]; | |
let next = u64_align!(next); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this a danger in the current callers of
u64_align
right now likesolana/runtime/src/append_vec.rs
Line 516 in 9ed4eac
let next = u64_align!(next);
Yes. In my test, if next
is usize::MAX
, it'll panic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a side note it might be good to extract all of these "align and offset" calculation functions into a common utility that can be reused in places like this:
solana/runtime/src/append_vec.rs
Lines 511 to 516 in 9ed4eac
let (next, overflow) = offset.overflowing_add(size); if overflow || next > self.len() { return None; } let data = &self.map[offset..next]; let next = u64_align!(next); into a common utility so that we can reuse them across these functions
Yes, that will be ideal. Separate PR though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd vote for separate pr. @alessandrod is good at getting this right. And @brooksprumo
return Some(owners.contains(&account.owner())); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this section of code was repurposed from here:
solana/runtime/src/accounts_db.rs
Lines 5341 to 5357 in c7fb4b1
let (slot, storage_location, _maybe_account_accesor) = | |
self.read_index_for_accessor_or_load_slow(ancestors, pubkey, max_root, false)?; | |
// Notice the subtle `?` at previous line, we bail out pretty early if missing. | |
let in_write_cache = storage_location.is_cached(); | |
if !load_into_read_cache_only { | |
if !in_write_cache { | |
let result = self.read_only_accounts_cache.load(*pubkey, slot); | |
if let Some(account) = result { | |
if matches!(load_zero_lamports, LoadZeroLamports::None) | |
&& account.is_zero_lamport() | |
{ | |
return None; | |
} | |
return Some((account, slot)); | |
} | |
} |
@jeffwashington is it important to also make the zero lamports check https://github.com/solana-labs/solana/blob/c7fb4b10401a10c21f27a2ccf47176ccb659b646/runtime/src/accounts_db.rs#L5350C5-L5351 in order to be consistent? I.e. right now when we look up accounts with zero lamports it returns None
, what should be done here in those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested with a 0 lamport account, and the owner gets set to SystemProgram
. I don't think the check is useful for our usecase (filtering executables/programs). If some other usecase tries to filter SystemProgram
accounts, 0 lamport account will show up there. Not sure if that's a problem though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the answer will be wrong if the account is zero lamport and you did look for system program owner. The result should be 'false' for zero lamport account to be consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, missed this comment. I'll push another update.
runtime/src/accounts_db.rs
Outdated
@@ -809,6 +809,26 @@ impl<'a> LoadedAccountAccessor<'a> { | |||
} | |||
} | |||
} | |||
|
|||
fn account_matches_owners(&self, owners: &[&Pubkey]) -> Option<bool> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Option return value is more confusing than it could be. I suggest an enum:
matches, does not match, unable to determine (for overflow case). Or, maybe even a result since unable to determine is an error (which should ideally never happen!?!?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you like a similar change for the append_vec.rs
API?
I had modeled this mostly on load()
API. And, a lot of internal APIs currently return an Option
. I do agree that a Result
with specific errors will be cleaner approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed all the new APIs to return Result
#[derive(Error, Debug, PartialEq, Eq)] | ||
pub enum MatchAccountOwnerError { | ||
#[error("The account owner does not match with the provided list")] | ||
NoMatch, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with this.
I was thinking though Result<bool, MatchAccountOwnerError>
Where bool was matched true/false
And error was only for the frustrating UnableToLoad
case. Either way is ok with me and could be tweaked later. Making whatever you can pub(crate)
would be great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but to be clear, either way is ok. This is like an enum, which is the most clear, imho.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Actually,
#30154 (comment)
is outstanding.
The latest commit should handle account with 0 lamports. |
} | ||
|
||
let (account_accessor, _slot) = self | ||
.retry_to_get_account_accessor( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't look like zero lamport is handled for the case where we load from the append vec. I think you need this same type of code here:
!account.is_zero_lamport() && owners.contains(&account.owner()))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The append_vec.rs
makes the check and returns the error for 0 lamports.
The append_vec
API is not returning the account meta, otherwise we could've consolidated this check in accounts_db.rs
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my apologies. I was looking at the CURRENT master code to see that we weren't doing anything special on zero lamport accounts...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Problem
The runtime V2 needs an API to filter executable/program accounts in a transaction, without reading the whole account in the memory. We don't currently have such an API.
An API that can match account's owner with a set of pubkeys (loader pubkeys) will be ideal.
Summary of Changes
Add API to
account_matches_owners
inappend_vec.rs
andaccounts_db.rs
. Also, add unit tests.Fixes #