use crate::{
error::RuleSetError,
payload::ProofInfo,
state::{
RuleSetHeader, RuleSetRevisionMapV1, RULE_SET_REV_MAP_VERSION,
RULE_SET_SERIALIZED_HEADER_LEN,
},
};
use borsh::BorshDeserialize;
use solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed},
program_error::ProgramError,
program_memory::sol_memcmp,
pubkey::{Pubkey, PUBKEY_BYTES},
rent::Rent,
system_instruction,
sysvar::Sysvar,
};
#[inline(always)]
pub fn create_or_allocate_account_raw<'a>(
program_id: Pubkey,
new_account_info: &AccountInfo<'a>,
system_program_info: &AccountInfo<'a>,
payer_info: &AccountInfo<'a>,
size: usize,
signer_seeds: &[&[u8]],
) -> ProgramResult {
let rent = &Rent::get()?;
let required_lamports = rent
.minimum_balance(size)
.max(1)
.saturating_sub(new_account_info.lamports());
if required_lamports > 0 {
msg!("Transfer {} lamports to the new account", required_lamports);
invoke(
&system_instruction::transfer(payer_info.key, new_account_info.key, required_lamports),
&[
payer_info.clone(),
new_account_info.clone(),
system_program_info.clone(),
],
)?;
}
let accounts = &[new_account_info.clone(), system_program_info.clone()];
msg!("Allocate space for the account");
invoke_signed(
&system_instruction::allocate(new_account_info.key, size.try_into().unwrap()),
accounts,
&[signer_seeds],
)?;
msg!("Assign the account to the owning program");
invoke_signed(
&system_instruction::assign(new_account_info.key, &program_id),
accounts,
&[signer_seeds],
)?;
Ok(())
}
#[inline(always)]
pub fn resize_or_reallocate_account_raw<'a>(
target_account: &AccountInfo<'a>,
funding_account: &AccountInfo<'a>,
system_program: &AccountInfo<'a>,
new_size: usize,
) -> ProgramResult {
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(new_size);
let lamports_diff = new_minimum_balance.saturating_sub(target_account.lamports());
invoke(
&system_instruction::transfer(funding_account.key, target_account.key, lamports_diff),
&[
funding_account.clone(),
target_account.clone(),
system_program.clone(),
],
)?;
target_account.realloc(new_size, false)?;
Ok(())
}
pub fn assert_derivation(
program_id: &Pubkey,
account: &Pubkey,
path: &[&[u8]],
) -> Result<u8, ProgramError> {
let (key, bump) = Pubkey::find_program_address(path, program_id);
if key != *account {
return Err(RuleSetError::DerivedKeyInvalid.into());
}
Ok(bump)
}
pub fn assert_owned_by(account: &AccountInfo, owner: &Pubkey) -> ProgramResult {
if account.owner != owner {
Err(RuleSetError::IncorrectOwner.into())
} else {
Ok(())
}
}
pub fn cmp_pubkeys(a: &Pubkey, b: &Pubkey) -> bool {
sol_memcmp(a.as_ref(), b.as_ref(), PUBKEY_BYTES) == 0
}
pub fn compute_merkle_root(leaf: &Pubkey, merkle_proof: &ProofInfo) -> [u8; 32] {
let mut computed_hash = leaf.to_bytes();
for proof_element in merkle_proof.proof.iter() {
if computed_hash <= *proof_element {
computed_hash =
solana_program::keccak::hashv(&[&[0x01], &computed_hash, proof_element]).0;
} else {
computed_hash =
solana_program::keccak::hashv(&[&[0x01], proof_element, &computed_hash]).0;
}
}
computed_hash
}
pub fn get_existing_revision_map(
rule_set_pda_info: &AccountInfo,
) -> Result<(RuleSetRevisionMapV1, usize), ProgramError> {
let data = rule_set_pda_info
.data
.try_borrow()
.map_err(|_| ProgramError::AccountBorrowFailed)?;
let header = if data.len() >= RULE_SET_SERIALIZED_HEADER_LEN {
RuleSetHeader::try_from_slice(&data[..RULE_SET_SERIALIZED_HEADER_LEN])?
} else {
return Err(RuleSetError::DataTypeMismatch.into());
};
match data.get(header.rev_map_version_location) {
Some(&RULE_SET_REV_MAP_VERSION) => {
let start = header
.rev_map_version_location
.checked_add(1)
.ok_or(RuleSetError::NumericalOverflow)?;
if start < data.len() {
let mut location = &data[start..];
let revision_map = RuleSetRevisionMapV1::deserialize(&mut location)?;
Ok((revision_map, header.rev_map_version_location))
} else {
Err(RuleSetError::DataTypeMismatch.into())
}
}
Some(_) => Err(RuleSetError::UnsupportedRuleSetRevMapVersion.into()),
None => Err(RuleSetError::DataTypeMismatch.into()),
}
}
pub fn get_latest_revision(rule_set_pda_info: &AccountInfo) -> Result<Option<usize>, ProgramError> {
let (revision_map, _) = get_existing_revision_map(rule_set_pda_info)?;
match revision_map.rule_set_revisions.len() {
0 => Err(RuleSetError::RuleSetRevisionNotAvailable.into()),
length => Ok(Some(length - 1)),
}
}
pub fn is_on_curve(pubkey: &Pubkey) -> bool {
let _point = pubkey.to_bytes();
let mut _validate_result = 0u8;
false
}
pub fn is_zeroed(buf: &[u8]) -> bool {
const ZEROS_LEN: usize = 1024;
const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
let mut chunks = buf.chunks_exact(ZEROS_LEN);
#[allow(clippy::indexing_slicing)]
{
chunks.all(|chunk| chunk == &ZEROS[..])
&& chunks.remainder() == &ZEROS[..chunks.remainder().len()]
}
}