use solana_address::Address;
use solana_program_error::ProgramError;
pub mod buffer;
pub mod order;
pub mod state;
const SETTLEMENT_SEED_PREFIX: &[u8] = b"settlement v";
const SETTLEMENT_SEED_VERSION_LEN: usize = 7;
pub const SETTLEMENT_SEED_LEN: usize = SETTLEMENT_SEED_PREFIX.len() + SETTLEMENT_SEED_VERSION_LEN;
pub const SETTLEMENT_SEED: &[u8] = &build_padded_settlement_seed(concat!(
env!("CARGO_PKG_VERSION_MAJOR"),
".",
env!("CARGO_PKG_VERSION_MINOR")
));
const fn build_padded_settlement_seed(version: &str) -> [u8; SETTLEMENT_SEED_LEN] {
let version = version.as_bytes();
assert!(version.len() <= SETTLEMENT_SEED_VERSION_LEN, "{}", "the package version no longer fits the settlement seed; shorten the major/minor version number");
let mut seed = [b' '; SETTLEMENT_SEED_LEN];
write_at(&mut seed, 0, SETTLEMENT_SEED_PREFIX);
write_at(&mut seed, SETTLEMENT_SEED_PREFIX.len(), version);
seed
}
const fn write_at(dest: &mut [u8], offset: usize, src: &[u8]) {
let mut i = 0;
while i < src.len() {
let index = offset.checked_add(i).expect("index stays within the seed");
dest[index] = src[i];
i = i.checked_add(1).expect("index stays within the seed");
}
}
#[must_use]
pub fn is_pda_with_signer_seeds<const N: usize>(
account: &Address,
program_id: &Address,
signer_seeds: [&[u8]; N],
) -> bool {
Address::create_program_address(&signer_seeds, program_id)
.is_ok_and(|derived| account == &derived)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, num_enum::TryFromPrimitive)]
#[repr(u8)]
#[num_enum(error_type(
name = ProgramError,
constructor = SettlementAccount::unknown_discriminator,
))]
pub enum SettlementAccount {
OrderAccount = 128,
SettlementState = 129,
}
impl SettlementAccount {
pub const fn discriminator(self) -> u8 {
self as u8
}
fn unknown_discriminator(_: u8) -> ProgramError {
ProgramError::InvalidAccountData
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use solana_pubkey::Pubkey;
use super::{
build_padded_settlement_seed, SettlementAccount, SETTLEMENT_SEED, SETTLEMENT_SEED_LEN,
SETTLEMENT_SEED_VERSION_LEN,
};
use solana_program_error::ProgramError;
pub(crate) const SAMPLE_VERSIONS: &[&str] = &[
"0.0", "0.1", "0.2", "0.10", "0.11", "0.12", "0.13", "0.14", "0.15", "0.16", "0.17",
"0.18", "0.19", "1.0", "1.1", "10.0",
];
fn longest_fitting_version() -> String {
let major_width = SETTLEMENT_SEED_VERSION_LEN
.checked_sub(".9".len())
.expect("the version field must hold at least a one-digit major and minor");
format!("{}.9", "9".repeat(major_width))
}
#[test]
fn build_padded_settlement_seed_accepts_the_widest_fitting_version() {
let seed = build_padded_settlement_seed(&longest_fitting_version());
assert_eq!(seed.as_slice(), b"settlement v99999.9");
assert!(!seed.ends_with(b" "));
}
#[test]
#[should_panic(expected = "no longer fits the settlement seed")]
fn build_padded_settlement_seed_rejects_a_version_one_byte_too_wide() {
let version = longest_fitting_version();
let _ = build_padded_settlement_seed(&format!("{version}0"));
}
#[test]
#[should_panic(expected = "no longer fits the settlement seed")]
fn build_padded_settlement_seed_rejects_a_version_wider_than_the_whole_seed() {
let _ = build_padded_settlement_seed(&"9".repeat(SETTLEMENT_SEED_LEN.saturating_add(1)));
}
#[test]
fn current_settlement_seed_is_correct_length() {
assert_eq!(SETTLEMENT_SEED.len(), SETTLEMENT_SEED_LEN);
}
#[test]
fn current_settlement_seed_is_printable_ascii() {
assert!(
SETTLEMENT_SEED
.iter()
.all(|byte| byte.is_ascii_graphic() || *byte == b' '),
"the seed must stay readable: {:?}",
core::str::from_utf8(SETTLEMENT_SEED),
);
}
pub(crate) fn assert_canonical_bump<const SIZE: usize, F1>(find_pda: F1, seeds: [&[u8]; SIZE])
where
F1: Fn(&Pubkey) -> (solana_pubkey::Pubkey, u8),
{
let program_id = Pubkey::new_unique();
let (pda, canonical_bump) = find_pda(&program_id);
let try_create_address = |bump| {
let bump = [bump];
let mut signer_seeds = seeds.to_vec();
signer_seeds.push(&bump);
Pubkey::create_program_address(&signer_seeds, &program_id)
};
if let Some(first_invalid_bump) = canonical_bump.checked_add(1) {
for candidate in first_invalid_bump..=u8::MAX {
assert!(
try_create_address(candidate).is_err(),
"bump {candidate} above the canonical bump {canonical_bump} must be invalid",
);
}
}
let expected = try_create_address(canonical_bump)
.expect("canonical bump must produce a valid address");
assert_eq!(pda, expected);
}
#[track_caller]
pub(crate) fn assert_distinct_versions_yield_distinct_pdas(
program_id: &Pubkey,
cur_version_pda: &Pubkey,
trailing_seeds: &[&[u8]],
) {
let mut cur_version_seeds: Vec<&[u8]> = vec![&SETTLEMENT_SEED];
cur_version_seeds.extend_from_slice(trailing_seeds);
let (cur_version_derived_pda, _) =
Pubkey::find_program_address(&cur_version_seeds, program_id);
assert_eq!(cur_version_pda, &cur_version_derived_pda);
let mut seen_pdas = HashSet::new();
for other in SAMPLE_VERSIONS {
let other_seed = build_padded_settlement_seed(other);
let mut seeds: Vec<&[u8]> = vec![&other_seed];
seeds.extend_from_slice(trailing_seeds);
let (other_pda, _) = Pubkey::find_program_address(&seeds, program_id);
assert!(seen_pdas.insert(other_pda));
}
}
#[test]
fn settlement_account_try_from_partitions_all_bytes() {
for i in u8::MIN..=u8::MAX {
match SettlementAccount::try_from(i) {
Ok(account) => assert_eq!(account as u8, i),
Err(err) => assert_eq!(err, ProgramError::InvalidAccountData),
}
}
}
#[test]
fn settlement_account_discriminators_are_distinct() {
assert_ne!(
SettlementAccount::OrderAccount.discriminator(),
SettlementAccount::SettlementState.discriminator(),
);
}
}