use crate::account_view::AccountView;
use crate::error::ProgramError;
use crate::ProgramResult;
#[derive(Clone, Copy, Debug)]
pub struct LamportSnapshot {
source_before: u64,
destination_before: u64,
}
impl LamportSnapshot {
#[inline(always)]
pub fn capture(source: &AccountView, destination: &AccountView) -> Self {
Self {
source_before: source.lamports(),
destination_before: destination.lamports(),
}
}
#[inline]
pub fn verify_transfer(
&self,
source: &AccountView,
destination: &AccountView,
amount: u64,
) -> ProgramResult {
let source_after = source.lamports();
let dest_after = destination.lamports();
let source_delta = self
.source_before
.checked_sub(source_after)
.ok_or(ProgramError::ArithmeticOverflow)?;
if source_delta != amount {
return Err(ProgramError::InvalidAccountData);
}
let dest_delta = dest_after
.checked_sub(self.destination_before)
.ok_or(ProgramError::ArithmeticOverflow)?;
if dest_delta != amount {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline]
pub fn verify_deduction(&self, source: &AccountView, amount: u64) -> ProgramResult {
let delta = self
.source_before
.checked_sub(source.lamports())
.ok_or(ProgramError::ArithmeticOverflow)?;
if delta != amount {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline]
pub fn verify_unchanged(
&self,
source: &AccountView,
destination: &AccountView,
) -> ProgramResult {
if source.lamports() != self.source_before
|| destination.lamports() != self.destination_before
{
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn source_before(&self) -> u64 {
self.source_before
}
#[inline(always)]
pub fn destination_before(&self) -> u64 {
self.destination_before
}
}
#[derive(Clone, Copy, Debug)]
pub struct BalanceSnapshot {
before: u64,
}
impl BalanceSnapshot {
#[inline(always)]
pub fn capture(account: &AccountView) -> Self {
Self {
before: account.lamports(),
}
}
#[inline]
pub fn verify_increased_by(&self, account: &AccountView, min_increase: u64) -> ProgramResult {
let current = account.lamports();
let delta = current
.checked_sub(self.before)
.ok_or(ProgramError::ArithmeticOverflow)?;
if delta < min_increase {
return Err(ProgramError::InsufficientFunds);
}
Ok(())
}
#[inline]
pub fn verify_decreased_by_at_most(
&self,
account: &AccountView,
max_decrease: u64,
) -> ProgramResult {
let current = account.lamports();
let delta = self
.before
.checked_sub(current)
.ok_or(ProgramError::ArithmeticOverflow)?;
if delta > max_decrease {
return Err(ProgramError::InsufficientFunds);
}
Ok(())
}
#[inline]
pub fn verify_unchanged(&self, account: &AccountView) -> ProgramResult {
if account.lamports() != self.before {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn before(&self) -> u64 {
self.before
}
#[inline(always)]
pub fn net_change(&self, account: &AccountView) -> i128 {
account.lamports() as i128 - self.before as i128
}
}
#[derive(Clone, Copy, Debug)]
pub struct DataFingerprint {
hash: u64,
data_len: usize,
}
impl DataFingerprint {
#[inline]
pub fn capture(account: &AccountView, len: usize) -> Self {
let data_len = account.data_len().min(len);
let data_ptr = account.data_ptr_unchecked();
let mut hash: u64 = 0xcbf29ce484222325;
let mut i = 0;
while i < data_len {
let byte = unsafe { *data_ptr.add(i) };
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
i += 1;
}
Self { hash, data_len }
}
#[inline]
pub fn verify_unchanged(&self, account: &AccountView) -> ProgramResult {
let current = Self::capture(account, self.data_len);
if current.hash != self.hash || current.data_len != self.data_len {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn hash(&self) -> u64 {
self.hash
}
}