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 manager: Pubkey,
22 pub reclaim_authority: Pubkey,
25}
26
27#[derive(Clone, Debug, Deref, Eq, PartialEq)]
38pub struct EncodedStateAccount([u8; Self::SIZE]);
39
40impl EncodedStateAccount {
41 const W_DISCRIMINATOR: usize = size_of::<u8>();
43 const W_MANAGER: usize = size_of::<Pubkey>();
44 const W_RECLAIM_AUTHORITY: usize = size_of::<Pubkey>();
45
46 pub const SIZE: usize = 65;
47
48 pub const DISCRIMINATOR: u8 = SettlementAccount::SettlementState.discriminator();
50}
51
52pub fn write_account(buffer: &mut [u8; EncodedStateAccount::SIZE], account: &StateAccount) {
55 let StateAccount {
56 manager,
57 reclaim_authority,
58 } = account;
59 let (discriminator_slot, manager_slot, reclaim_authority_slot) = mut_array_refs![
60 buffer,
61 EncodedStateAccount::W_DISCRIMINATOR,
62 EncodedStateAccount::W_MANAGER,
63 EncodedStateAccount::W_RECLAIM_AUTHORITY
64 ];
65 *discriminator_slot = [EncodedStateAccount::DISCRIMINATOR];
66 *manager_slot = manager.to_bytes();
67 *reclaim_authority_slot = reclaim_authority.to_bytes();
68}
69
70impl From<EncodedStateAccount> for [u8; EncodedStateAccount::SIZE] {
71 fn from(encoded: EncodedStateAccount) -> Self {
72 encoded.0
73 }
74}
75
76impl From<StateAccount> for EncodedStateAccount {
77 fn from(account: StateAccount) -> Self {
78 let mut out = [0u8; Self::SIZE];
79 write_account(&mut out, &account);
80 Self(out)
81 }
82}
83
84impl From<StateAccount> for [u8; EncodedStateAccount::SIZE] {
85 fn from(account: StateAccount) -> Self {
86 EncodedStateAccount::from(account).into()
87 }
88}
89
90impl TryFrom<[u8; EncodedStateAccount::SIZE]> for StateAccount {
91 type Error = ProgramError;
92
93 fn try_from(bytes: [u8; EncodedStateAccount::SIZE]) -> Result<Self, Self::Error> {
94 let (discriminator, manager, reclaim_authority) = array_refs![
95 &bytes,
96 EncodedStateAccount::W_DISCRIMINATOR,
97 EncodedStateAccount::W_MANAGER,
98 EncodedStateAccount::W_RECLAIM_AUTHORITY
99 ];
100
101 if *discriminator != [EncodedStateAccount::DISCRIMINATOR] {
102 return Err(ProgramError::InvalidAccountData);
103 }
104
105 Ok(StateAccount {
106 manager: Pubkey::new_from_array(*manager),
107 reclaim_authority: Pubkey::new_from_array(*reclaim_authority),
108 })
109 }
110}
111
112impl TryFrom<EncodedStateAccount> for StateAccount {
113 type Error = ProgramError;
114
115 fn try_from(encoded: EncodedStateAccount) -> Result<Self, Self::Error> {
116 StateAccount::try_from(encoded.0)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::tests::pubkey_from_seed;
124
125 const DISCRIMINATOR_OFFSET: usize = 0;
127
128 fn sample_account() -> StateAccount {
129 StateAccount {
130 manager: pubkey_from_seed("sample_account's manager"),
131 reclaim_authority: pubkey_from_seed("sample_account's reclaim_authority"),
132 }
133 }
134
135 #[test]
136 fn decode_rejects_wrong_discriminator() {
137 let mut bytes: [u8; EncodedStateAccount::SIZE] =
138 EncodedStateAccount::from(sample_account()).into();
139 bytes[0] ^= 0xff;
140 let err = StateAccount::try_from(bytes).expect_err("wrong discriminator must be rejected");
141 assert_eq!(err, ProgramError::InvalidAccountData);
142 }
143
144 #[test]
145 fn direct_write_account_matches_state_account_encoding() {
146 let account = sample_account();
147 let mut buffer = [0u8; EncodedStateAccount::SIZE];
148 write_account(&mut buffer, &account);
149 let direct = EncodedStateAccount(buffer);
150 let via_state_account = EncodedStateAccount::from(account);
151 assert_eq!(direct, via_state_account);
152 }
153
154 mod proptest {
155 use ::proptest::prelude::*;
156
157 use super::*;
158
159 proptest! {
160 #[test]
161 fn account_encode_roundtrip(
162 reclaim_authority in any::<[u8; 32]>(),
163 manager in any::<[u8; 32]>(),
164 ) {
165 let account = StateAccount {
166 reclaim_authority: Pubkey::new_from_array(reclaim_authority),
167 manager: Pubkey::new_from_array(manager),
168 };
169 let encoded = EncodedStateAccount::from(account.clone());
170 let decoded = StateAccount::try_from(encoded).expect("should decode after encoding");
171 prop_assert_eq!(decoded, account);
172 }
173
174 #[test]
175 fn account_decode_roundtrip(
176 mut bytes in any::<[u8; EncodedStateAccount::SIZE]>(),
177 ) {
178 bytes[DISCRIMINATOR_OFFSET] = EncodedStateAccount::DISCRIMINATOR;
179 let encoded = EncodedStateAccount(bytes);
180
181 let decoded = StateAccount::try_from(encoded.clone()).expect("should decode from valid bytes");
182 let re_encoded = EncodedStateAccount::from(decoded);
183
184 prop_assert_eq!(re_encoded, encoded);
185 }
186 }
187 }
188}