Skip to content

Commit 1f63b1d

Browse files
authored
pinocchio: Add pubkey_eq helper (#248)
* Add pubkey_eq helper * Fix typo * Update pubkey comparison * Add proptest * Add unlikely * Replace proptest
1 parent 74a7b51 commit 1f63b1d

File tree

7 files changed

+64
-14
lines changed

7 files changed

+64
-14
lines changed

sdk/pinocchio/src/account_info.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ use core::{
1010
#[cfg(target_os = "solana")]
1111
use crate::syscalls::sol_memset_;
1212

13-
use crate::{program_error::ProgramError, pubkey::Pubkey, ProgramResult};
13+
use crate::{
14+
program_error::ProgramError,
15+
pubkey::{pubkey_eq, Pubkey},
16+
ProgramResult,
17+
};
1418

1519
/// Maximum number of bytes a program may add to an account during a
1620
/// single top-level instruction.
@@ -186,7 +190,7 @@ impl AccountInfo {
186190
/// Checks if the account is owned by the given program.
187191
#[inline(always)]
188192
pub fn is_owned_by(&self, program: &Pubkey) -> bool {
189-
self.owner() == program
193+
pubkey_eq(self.owner(), program)
190194
}
191195

192196
/// Changes the owner of the account.

sdk/pinocchio/src/cpi.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ use core::{mem::MaybeUninit, ops::Deref, slice::from_raw_parts};
44

55
use crate::{
66
account_info::{AccountInfo, BorrowState},
7+
hint::unlikely,
78
instruction::{Account, Instruction, Signer},
89
program_error::ProgramError,
9-
pubkey::Pubkey,
10+
pubkey::{pubkey_eq, Pubkey},
1011
ProgramResult,
1112
};
1213

@@ -299,7 +300,7 @@ unsafe fn inner_invoke_signed_with_bounds<const MAX_ACCOUNTS: usize>(
299300
// In order to check whether the borrow state is compatible
300301
// with the invocation, we need to check that we have the
301302
// correct account info and meta pair.
302-
if account_info.key() != account_meta.pubkey {
303+
if unlikely(!pubkey_eq(account_info.key(), account_meta.pubkey)) {
303304
return Err(ProgramError::InvalidArgument);
304305
}
305306

sdk/pinocchio/src/pubkey.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Public key type and functions.
22
3+
use core::ptr::read_unaligned;
4+
35
use crate::program_error::ProgramError;
46

57
/// Number of bytes in a pubkey.
@@ -33,6 +35,24 @@ pub fn log(pubkey: &Pubkey) {
3335
core::hint::black_box(pubkey);
3436
}
3537

38+
/// Compare two `Pubkey`s for equality.
39+
///
40+
/// The implementation of this function is currently more efficient
41+
/// than `p1 == p2` since it compares 8 bytes at a time instead of
42+
/// byte-by-byte.
43+
#[inline(always)]
44+
pub const fn pubkey_eq(p1: &Pubkey, p2: &Pubkey) -> bool {
45+
let p1_ptr = p1.as_ptr() as *const u64;
46+
let p2_ptr = p2.as_ptr() as *const u64;
47+
48+
unsafe {
49+
read_unaligned(p1_ptr) == read_unaligned(p2_ptr)
50+
&& read_unaligned(p1_ptr.add(1)) == read_unaligned(p2_ptr.add(1))
51+
&& read_unaligned(p1_ptr.add(2)) == read_unaligned(p2_ptr.add(2))
52+
&& read_unaligned(p1_ptr.add(3)) == read_unaligned(p2_ptr.add(3))
53+
}
54+
}
55+
3656
/// Find a valid [program derived address][pda] and its corresponding bump seed.
3757
///
3858
/// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
@@ -277,3 +297,25 @@ pub fn create_with_seed(
277297
panic!("create_with_seed is only available on target `solana`")
278298
}
279299
}
300+
301+
#[cfg(test)]
302+
mod tests {
303+
use crate::pubkey::{pubkey_eq, Pubkey, PUBKEY_BYTES};
304+
305+
#[test]
306+
fn test_pubkey_eq_matches_default_eq() {
307+
for i in 0..u8::MAX {
308+
let p1: Pubkey = [i; PUBKEY_BYTES];
309+
let p2: Pubkey = [i; PUBKEY_BYTES];
310+
311+
assert_eq!(pubkey_eq(&p1, &p2), p1 == p2);
312+
}
313+
314+
for i in 0..u8::MAX {
315+
let p1: Pubkey = [i; PUBKEY_BYTES];
316+
let p2: Pubkey = [u8::MAX - i; PUBKEY_BYTES];
317+
318+
assert_eq!(!pubkey_eq(&p1, &p2), p1 != p2);
319+
}
320+
}
321+
}

sdk/pinocchio/src/sysvars/clock.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
use super::Sysvar;
44
use crate::{
55
account_info::{AccountInfo, Ref},
6+
hint::unlikely,
67
impl_sysvar_get,
78
program_error::ProgramError,
8-
pubkey::Pubkey,
9+
pubkey::{pubkey_eq, Pubkey},
910
};
1011

1112
/// The ID of the clock sysvar.
@@ -86,7 +87,7 @@ impl Clock {
8687
/// This method performs a check on the account info key.
8788
#[inline]
8889
pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Clock>, ProgramError> {
89-
if account_info.key() != &CLOCK_ID {
90+
if unlikely(!pubkey_eq(account_info.key(), &CLOCK_ID)) {
9091
return Err(ProgramError::InvalidArgument);
9192
}
9293
Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
@@ -107,7 +108,7 @@ impl Clock {
107108
pub unsafe fn from_account_info_unchecked(
108109
account_info: &AccountInfo,
109110
) -> Result<&Self, ProgramError> {
110-
if account_info.key() != &CLOCK_ID {
111+
if unlikely(!pubkey_eq(account_info.key(), &CLOCK_ID)) {
111112
return Err(ProgramError::InvalidArgument);
112113
}
113114
Ok(Self::from_bytes_unchecked(

sdk/pinocchio/src/sysvars/rent.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
use super::Sysvar;
66
use crate::{
77
account_info::{AccountInfo, Ref},
8+
hint::unlikely,
89
impl_sysvar_get,
910
program_error::ProgramError,
10-
pubkey::Pubkey,
11+
pubkey::{pubkey_eq, Pubkey},
1112
};
1213

1314
/// The ID of the rent sysvar.
@@ -73,7 +74,7 @@ impl Rent {
7374
/// This method performs a check on the account info key.
7475
#[inline]
7576
pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Rent>, ProgramError> {
76-
if account_info.key() != &RENT_ID {
77+
if unlikely(!pubkey_eq(account_info.key(), &RENT_ID)) {
7778
return Err(ProgramError::InvalidArgument);
7879
}
7980
Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
@@ -94,7 +95,7 @@ impl Rent {
9495
pub unsafe fn from_account_info_unchecked(
9596
account_info: &AccountInfo,
9697
) -> Result<&Self, ProgramError> {
97-
if account_info.key() != &RENT_ID {
98+
if unlikely(!pubkey_eq(account_info.key(), &RENT_ID)) {
9899
return Err(ProgramError::InvalidArgument);
99100
}
100101
Ok(Self::from_bytes_unchecked(

sdk/pinocchio/src/sysvars/slot_hashes/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ mod test_utils;
1515

1616
use crate::{
1717
account_info::{AccountInfo, Ref},
18+
hint::unlikely,
1819
program_error::ProgramError,
19-
pubkey::Pubkey,
20+
pubkey::{pubkey_eq, Pubkey},
2021
sysvars::clock::Slot,
2122
};
2223
use core::{mem, ops::Deref, slice::from_raw_parts};
@@ -277,7 +278,7 @@ impl<'a> SlotHashes<Ref<'a, [u8]>> {
277278
/// - `ProgramError::AccountBorrowFailed` if the account data is already mutably borrowed
278279
#[inline(always)]
279280
pub fn from_account_info(account_info: &'a AccountInfo) -> Result<Self, ProgramError> {
280-
if account_info.key() != &SLOTHASHES_ID {
281+
if unlikely(!pubkey_eq(account_info.key(), &SLOTHASHES_ID)) {
281282
return Err(ProgramError::InvalidArgument);
282283
}
283284

sdk/pubkey/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#[doc(hidden)]
55
// Re-export dependencies used in macros.
66
pub mod reexport {
7-
pub use pinocchio::pubkey::Pubkey;
7+
pub use pinocchio::pubkey::{pubkey_eq, Pubkey};
88
}
99

1010
use core::mem::MaybeUninit;
@@ -176,7 +176,7 @@ macro_rules! declare_id {
176176
#[doc = "Returns `true` if given pubkey is the program ID."]
177177
#[inline]
178178
pub fn check_id(id: &$crate::reexport::Pubkey) -> bool {
179-
id == &ID
179+
$crate::reexport::pubkey_eq(id, &ID)
180180
}
181181

182182
#[doc = "Returns the program ID."]

0 commit comments

Comments
 (0)