cow_settlement_interface/data/
state.rs1use core::mem::size_of;
8
9use arrayref::{array_refs, mut_array_refs};
10use derive_more::Deref;
11use solana_program_error::ProgramError;
12use solana_pubkey::Pubkey;
13
14use crate::SettlementAccount;
15
16#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct StateAccount {
19 pub reclaim_authority: Pubkey,
22}
23
24#[derive(Clone, Debug, Deref, Eq, PartialEq)]
35pub struct EncodedStateAccount([u8; Self::SIZE]);
36
37impl EncodedStateAccount {
38 const W_DISCRIMINATOR: usize = size_of::<u8>();
40 const W_RECLAIM_AUTHORITY: usize = size_of::<Pubkey>();
41
42 pub const SIZE: usize = 33;
43
44 pub const DISCRIMINATOR: u8 = SettlementAccount::SettlementState.discriminator();
46}
47
48pub fn write_account(buffer: &mut [u8; EncodedStateAccount::SIZE], reclaim_authority: &Pubkey) {
51 let (discriminator_slot, reclaim_authority_slot) = mut_array_refs![
52 buffer,
53 EncodedStateAccount::W_DISCRIMINATOR,
54 EncodedStateAccount::W_RECLAIM_AUTHORITY
55 ];
56 *discriminator_slot = [EncodedStateAccount::DISCRIMINATOR];
57 *reclaim_authority_slot = reclaim_authority.to_bytes();
58}
59
60impl From<EncodedStateAccount> for [u8; EncodedStateAccount::SIZE] {
61 fn from(encoded: EncodedStateAccount) -> Self {
62 encoded.0
63 }
64}
65
66impl From<StateAccount> for EncodedStateAccount {
67 fn from(account: StateAccount) -> Self {
68 let mut out = [0u8; Self::SIZE];
69 write_account(&mut out, &account.reclaim_authority);
70 Self(out)
71 }
72}
73
74impl TryFrom<[u8; EncodedStateAccount::SIZE]> for StateAccount {
75 type Error = ProgramError;
76
77 fn try_from(bytes: [u8; EncodedStateAccount::SIZE]) -> Result<Self, Self::Error> {
78 let (discriminator, reclaim_authority) = array_refs![
79 &bytes,
80 EncodedStateAccount::W_DISCRIMINATOR,
81 EncodedStateAccount::W_RECLAIM_AUTHORITY
82 ];
83
84 if *discriminator != [EncodedStateAccount::DISCRIMINATOR] {
85 return Err(ProgramError::InvalidAccountData);
86 }
87
88 Ok(StateAccount {
89 reclaim_authority: Pubkey::new_from_array(*reclaim_authority),
90 })
91 }
92}
93
94impl TryFrom<EncodedStateAccount> for StateAccount {
95 type Error = ProgramError;
96
97 fn try_from(encoded: EncodedStateAccount) -> Result<Self, Self::Error> {
98 StateAccount::try_from(encoded.0)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 fn sample_account() -> StateAccount {
107 StateAccount {
108 reclaim_authority: Pubkey::new_from_array([0x42; 32]),
109 }
110 }
111
112 #[test]
113 fn decode_rejects_wrong_discriminator() {
114 let mut bytes: [u8; EncodedStateAccount::SIZE] =
115 EncodedStateAccount::from(sample_account()).into();
116 bytes[0] ^= 0xff;
117 let err = StateAccount::try_from(bytes).expect_err("wrong discriminator must be rejected");
118 assert_eq!(err, ProgramError::InvalidAccountData);
119 }
120
121 #[test]
122 fn direct_write_account_matches_state_account_encoding() {
123 let account = sample_account();
124 let mut buffer = [0u8; EncodedStateAccount::SIZE];
125 write_account(&mut buffer, &account.reclaim_authority);
126 let direct = EncodedStateAccount(buffer);
127 let via_state_account = EncodedStateAccount::from(account);
128 assert_eq!(direct, via_state_account);
129 }
130
131 mod proptest {
132 use ::proptest::prelude::*;
133
134 use super::*;
135
136 proptest! {
137 #[test]
138 fn account_encode_roundtrip(bytes in any::<[u8; 32]>()) {
139 let account = StateAccount { reclaim_authority: Pubkey::new_from_array(bytes) };
140 let encoded = EncodedStateAccount::from(account.clone());
141 let decoded = StateAccount::try_from(encoded).expect("should decode after encoding");
142 prop_assert_eq!(decoded, account);
143 }
144
145 #[test]
146 fn account_decode_roundtrip(bytes in any::<[u8; 32]>()) {
147 let mut encoded = [0u8; EncodedStateAccount::SIZE];
148 encoded[0] = EncodedStateAccount::DISCRIMINATOR;
149 encoded[1..].copy_from_slice(&bytes);
150 let encoded = EncodedStateAccount(encoded);
151
152 let decoded = StateAccount::try_from(encoded.clone()).expect("should decode from valid bytes");
153 let re_encoded = EncodedStateAccount::from(decoded);
154
155 prop_assert_eq!(re_encoded, encoded);
156 }
157 }
158 }
159}