pub const ADDRESS_BYTES: usize = 32;
pub const MAX_SEED_LEN: usize = 32;
pub const MAX_SEEDS: usize = 16;
pub const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
#[repr(transparent)]
#[derive(Clone, Copy, Default, PartialOrd, Ord)]
pub struct Address(pub(crate) [u8; 32]);
unsafe impl crate::pod::Zeroable for Address {}
unsafe impl crate::pod::Pod for Address {}
unsafe impl crate::zerocopy::__sealed::HopperZeroCopySealed for Address {}
impl Address {
#[inline(always)]
pub const fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
#[inline(always)]
pub const fn new_from_array(bytes: [u8; 32]) -> Self {
Self(bytes)
}
#[inline(always)]
pub const fn to_bytes(&self) -> [u8; 32] {
self.0
}
#[inline(always)]
pub const fn as_array(&self) -> &[u8; 32] {
&self.0
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
#[cfg(target_os = "solana")]
pub fn find_program_address(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
crate::native_boundary::find_program_address(seeds, program_id)
}
#[cfg(target_os = "solana")]
pub fn create_program_address(
seeds: &[&[u8]],
program_id: &Address,
) -> Result<Address, crate::ProgramError> {
crate::native_boundary::create_program_address(seeds, program_id)
}
}
impl From<[u8; 32]> for Address {
#[inline(always)]
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
impl From<Address> for [u8; 32] {
#[inline(always)]
fn from(addr: Address) -> [u8; 32] {
addr.0
}
}
impl TryFrom<&[u8]> for Address {
type Error = core::array::TryFromSliceError;
#[inline]
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let arr: [u8; 32] = slice.try_into()?;
Ok(Self(arr))
}
}
impl AsRef<[u8]> for Address {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for Address {
#[inline(always)]
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl AsRef<[u8; 32]> for Address {
#[inline(always)]
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl PartialEq for Address {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
address_eq(self, other)
}
}
impl Eq for Address {}
impl core::hash::Hash for Address {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl core::fmt::Debug for Address {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Address({:?})", &self.0[..4])
}
}
impl core::fmt::Display for Address {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for byte in &self.0[..4] {
write!(f, "{byte:02x}")?;
}
write!(f, "..")
}
}
#[inline(always)]
pub fn address_eq(a: &Address, b: &Address) -> bool {
keys_eq(&a.0, &b.0)
}
#[inline(always)]
pub fn keys_eq(a: &[u8; 32], b: &[u8; 32]) -> bool {
let a_ptr = a.as_ptr() as *const u64;
let b_ptr = b.as_ptr() as *const u64;
unsafe {
core::ptr::read_unaligned(a_ptr) == core::ptr::read_unaligned(b_ptr)
&& core::ptr::read_unaligned(a_ptr.add(1)) == core::ptr::read_unaligned(b_ptr.add(1))
&& core::ptr::read_unaligned(a_ptr.add(2)) == core::ptr::read_unaligned(b_ptr.add(2))
&& core::ptr::read_unaligned(a_ptr.add(3)) == core::ptr::read_unaligned(b_ptr.add(3))
}
}
#[inline(always)]
pub fn keys_eq_bytes(a: &[u8], b: &[u8; 32]) -> bool {
if a.len() != 32 {
return false;
}
let a_ptr = a.as_ptr() as *const u64;
let b_ptr = b.as_ptr() as *const u64;
unsafe {
core::ptr::read_unaligned(a_ptr) == core::ptr::read_unaligned(b_ptr)
&& core::ptr::read_unaligned(a_ptr.add(1)) == core::ptr::read_unaligned(b_ptr.add(1))
&& core::ptr::read_unaligned(a_ptr.add(2)) == core::ptr::read_unaligned(b_ptr.add(2))
&& core::ptr::read_unaligned(a_ptr.add(3)) == core::ptr::read_unaligned(b_ptr.add(3))
}
}
#[inline(always)]
pub fn address_is_zero(a: &Address) -> bool {
let ptr = a.0.as_ptr() as *const u64;
unsafe {
(core::ptr::read_unaligned(ptr)
| core::ptr::read_unaligned(ptr.add(1))
| core::ptr::read_unaligned(ptr.add(2))
| core::ptr::read_unaligned(ptr.add(3)))
== 0
}
}
#[cfg(test)]
mod tests {
use super::*;
fn edge_patterns() -> [[u8; 32]; 6] {
let mut ramp = [0u8; 32];
for (i, b) in ramp.iter_mut().enumerate() {
*b = i as u8;
}
let mut last_hi = [0u8; 32];
last_hi[31] = 0xFF;
let mut first_hi = [0u8; 32];
first_hi[0] = 0xFF;
[
[0u8; 32],
[0xFFu8; 32],
ramp,
last_hi,
first_hi,
[0xA5u8; 32],
]
}
#[test]
fn keys_eq_matches_bytewise_on_equal_arrays() {
for pat in edge_patterns() {
let copy = pat;
assert!(keys_eq(&pat, ©));
assert!(address_eq(&Address::new(pat), &Address::new(pat)));
assert_eq!(Address::new(pat), Address::new(pat));
}
}
#[test]
fn keys_eq_detects_single_byte_difference_at_every_index() {
for base in edge_patterns() {
for idx in 0..32 {
let mut other = base;
other[idx] ^= 0x01;
assert!(!keys_eq(&base, &other), "missed diff at byte {idx}");
assert!(!address_eq(&Address::new(base), &Address::new(other)));
assert_ne!(Address::new(base), Address::new(other));
assert_eq!(keys_eq(&base, &other), base == other);
}
}
}
#[test]
fn keys_eq_differs_only_in_last_byte() {
let a = [7u8; 32];
let mut b = a;
b[31] = 8;
assert!(!keys_eq(&a, &b));
assert_ne!(Address::new(a), Address::new(b));
}
#[test]
fn keys_eq_bytes_matches_slice_semantics() {
for pat in edge_patterns() {
assert!(keys_eq_bytes(&pat[..], &pat));
for idx in 0..32 {
let mut other = pat;
other[idx] ^= 0x80;
assert_eq!(keys_eq_bytes(&other[..], &pat), other == pat);
}
}
let key = [0u8; 32];
assert!(!keys_eq_bytes(&[], &key));
assert!(!keys_eq_bytes(&key[..31], &key));
let long = [0u8; 33];
assert!(!keys_eq_bytes(&long[..], &key));
}
#[test]
fn eq_is_consistent_with_derived_ord() {
use core::cmp::Ordering;
let patterns = edge_patterns();
for a in patterns {
for b in patterns {
let (aa, ab) = (Address::new(a), Address::new(b));
assert_eq!(aa == ab, aa.cmp(&ab) == Ordering::Equal);
assert_eq!(aa == ab, a == b);
for idx in 0..32 {
let mut c = a;
c[idx] = c[idx].wrapping_add(1);
let ac = Address::new(c);
assert_eq!(aa == ac, aa.cmp(&ac) == Ordering::Equal);
assert_eq!(aa == ac, a == c);
}
}
}
}
#[test]
fn address_is_zero_or_fold() {
assert!(address_is_zero(&Address::new([0u8; 32])));
assert!(address_is_zero(&Address::default()));
for idx in 0..32 {
let mut bytes = [0u8; 32];
bytes[idx] = 1;
assert!(!address_is_zero(&Address::new(bytes)));
}
assert!(!address_is_zero(&Address::new([0xFFu8; 32])));
}
}