use serde::{Deserialize, Serialize};
use steel::*;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
pub struct DailyParticipantData {
pub participant_id: [u8; 32],
pub first_payment_timestamp: i64,
pub last_payment_timestamp: i64,
pub first_payment_tx_sig_short: [u8; 8],
pub last_payment_tx_sig_short: [u8; 8],
pub payment_count: u32,
pub participant_type: u8,
pub _padding: [u8; 3],
}
impl DailyParticipantData {
pub fn new(
participant_id: [u8; 32],
first_payment_timestamp: i64,
last_payment_timestamp: i64,
first_payment_tx_sig_short: [u8; 8],
last_payment_tx_sig_short: [u8; 8],
payment_count: u32,
participant_type: u8,
) -> Self {
Self {
participant_id,
first_payment_timestamp,
last_payment_timestamp,
first_payment_tx_sig_short,
last_payment_tx_sig_short,
payment_count,
participant_type,
_padding: [0u8; 3],
}
}
pub fn compute_leaf_hash(&self) -> [u8; 32] {
use solana_program::hash::hashv;
let mut data = Vec::new();
data.extend_from_slice(&self.participant_id);
data.extend_from_slice(&self.first_payment_timestamp.to_le_bytes());
data.extend_from_slice(&self.last_payment_timestamp.to_le_bytes());
data.extend_from_slice(&self.first_payment_tx_sig_short);
data.extend_from_slice(&self.last_payment_tx_sig_short);
data.extend_from_slice(&self.payment_count.to_le_bytes());
data.push(self.participant_type);
hashv(&[&data]).to_bytes()
}
pub fn validate(&self) -> Result<(), &'static str> {
if self.participant_type > 1 {
return Err("Invalid participant type");
}
if self.payment_count == 0 {
return Err("Payment count must be greater than 0");
}
if self.first_payment_timestamp > self.last_payment_timestamp {
return Err("First timestamp must be before or equal to last timestamp");
}
if self.first_payment_tx_sig_short == [0u8; 8] {
return Err("First payment transaction signature short cannot be all zeros");
}
if self.last_payment_tx_sig_short == [0u8; 8] {
return Err("Last payment transaction signature short cannot be all zeros");
}
Ok(())
}
pub fn is_customer(&self) -> bool {
self.participant_type == 0
}
pub fn is_merchant(&self) -> bool {
self.participant_type == 1
}
pub fn from_bytes(data: &[u8]) -> Result<Self, &'static str> {
let expected_size = std::mem::size_of::<Self>();
if data.len() < expected_size {
return Err("Insufficient data for DailyParticipantData");
}
let participant_id: [u8; 32] = data[0..32]
.try_into()
.map_err(|_| "Invalid participant_id")?;
let first_payment_timestamp = i64::from_le_bytes(
data[32..40]
.try_into()
.map_err(|_| "Invalid first_payment_timestamp")?,
);
let last_payment_timestamp = i64::from_le_bytes(
data[40..48]
.try_into()
.map_err(|_| "Invalid last_payment_timestamp")?,
);
let first_payment_tx_sig_short: [u8; 8] = data[48..56]
.try_into()
.map_err(|_| "Invalid first_payment_tx_sig_short")?;
let last_payment_tx_sig_short: [u8; 8] = data[56..64]
.try_into()
.map_err(|_| "Invalid last_payment_tx_sig_short")?;
let payment_count = u32::from_le_bytes(
data[64..68]
.try_into()
.map_err(|_| "Invalid payment_count")?,
);
let participant_type = data[68];
Ok(Self {
participant_id,
first_payment_timestamp,
last_payment_timestamp,
first_payment_tx_sig_short,
last_payment_tx_sig_short,
payment_count,
participant_type,
_padding: [0u8; 3],
})
}
}