1use solana_address::Address;
2use solana_instruction::{AccountMeta, Instruction};
3use winterwallet_common::{SIGNATURE_LEN, WINTERNITZ_SCALARS};
4use winterwallet_core::WinternitzKeypair;
5
6use crate::{
7 AdvancePlan, Error, WinterWalletAccount, find_wallet_address,
8 transaction::{DEFAULT_ADVANCE_COMPUTE_UNIT_LIMIT, with_compute_budget},
9};
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct SigningPosition {
14 wallet: u32,
15 parent: u32,
16 child: u32,
17}
18
19impl SigningPosition {
20 pub const fn new(wallet: u32, parent: u32, child: u32) -> Self {
22 Self {
23 wallet,
24 parent,
25 child,
26 }
27 }
28
29 pub fn from_keypair(keypair: &WinternitzKeypair) -> Self {
31 Self::new(keypair.wallet(), keypair.parent(), keypair.child())
32 }
33
34 pub const fn wallet(&self) -> u32 {
36 self.wallet
37 }
38
39 pub const fn parent(&self) -> u32 {
41 self.parent
42 }
43
44 pub const fn child(&self) -> u32 {
46 self.child
47 }
48
49 pub fn next(&self) -> Result<Self, Error> {
51 match self.child.checked_add(1) {
52 Some(child) => Ok(Self::new(self.wallet, self.parent, child)),
53 None => Ok(Self::new(
54 self.wallet,
55 self.parent.checked_add(1).ok_or(Error::PositionOverflow)?,
56 0,
57 )),
58 }
59 }
60
61 fn tuple(&self) -> (u32, u32, u32) {
62 (self.wallet, self.parent, self.child)
63 }
64
65 fn ensure_can_advance(&self) -> Result<(), Error> {
66 self.next().map(|_| ())
67 }
68}
69
70pub struct WinterWallet {
72 id: [u8; 32],
73 pda: Address,
74 current_root: [u8; 32],
75 position: SigningPosition,
76}
77
78impl WinterWallet {
79 pub fn new(id: [u8; 32], current_root: [u8; 32], position: SigningPosition) -> Self {
81 let (pda, _bump) = find_wallet_address(&id);
82 Self {
83 id,
84 pda,
85 current_root,
86 position,
87 }
88 }
89
90 pub fn from_account(account: &WinterWalletAccount, position: SigningPosition) -> Self {
92 Self::new(account.id, *account.root.as_bytes(), position)
93 }
94
95 pub fn id(&self) -> &[u8; 32] {
97 &self.id
98 }
99
100 pub fn pda(&self) -> &Address {
102 &self.pda
103 }
104
105 pub fn current_root(&self) -> &[u8; 32] {
107 &self.current_root
108 }
109
110 pub fn position(&self) -> SigningPosition {
112 self.position
113 }
114
115 pub fn advance_plan(
117 &self,
118 new_root: &[u8; 32],
119 inner_instructions: &[Instruction],
120 ) -> Result<UnsignedAdvance, Error> {
121 let plan = AdvancePlan::new(&self.pda, new_root, inner_instructions)?;
122 Ok(UnsignedAdvance {
123 wallet_id: self.id,
124 current_root: self.current_root,
125 position: self.position,
126 plan,
127 })
128 }
129
130 pub fn withdraw_plan(
132 &self,
133 receiver: &Address,
134 lamports: u64,
135 new_root: &[u8; 32],
136 ) -> Result<UnsignedAdvance, Error> {
137 let plan = AdvancePlan::withdraw(&self.pda, receiver, lamports, new_root)?;
138 Ok(UnsignedAdvance {
139 wallet_id: self.id,
140 current_root: self.current_root,
141 position: self.position,
142 plan,
143 })
144 }
145
146 pub fn close_plan(
149 &self,
150 receiver: &Address,
151 new_root: &[u8; 32],
152 ) -> Result<UnsignedAdvance, Error> {
153 let plan = AdvancePlan::close(&self.pda, receiver, new_root)?;
154 Ok(UnsignedAdvance {
155 wallet_id: self.id,
156 current_root: self.current_root,
157 position: self.position,
158 plan,
159 })
160 }
161
162 pub fn transfer_plan(
164 &self,
165 source_token_account: &Address,
166 destination_token_account: &Address,
167 token_program: &Address,
168 amount: u64,
169 new_root: &[u8; 32],
170 ) -> Result<UnsignedAdvance, Error> {
171 self.advance_plan(
172 new_root,
173 &[token_transfer(
174 source_token_account,
175 destination_token_account,
176 &self.pda,
177 amount,
178 token_program,
179 )],
180 )
181 }
182}
183
184pub struct UnsignedAdvance {
186 wallet_id: [u8; 32],
187 current_root: [u8; 32],
188 position: SigningPosition,
189 plan: AdvancePlan,
190}
191
192impl UnsignedAdvance {
193 pub fn plan(&self) -> &AdvancePlan {
195 &self.plan
196 }
197
198 pub fn signing_position(&self) -> SigningPosition {
200 self.position
201 }
202
203 pub fn preimage(&self) -> Vec<&[u8]> {
205 self.plan.preimage(&self.wallet_id, &self.current_root)
206 }
207
208 pub fn sign(self, keypair: &mut WinternitzKeypair) -> Result<SignedAdvance, Error> {
213 let actual = SigningPosition::from_keypair(keypair);
214 if actual != self.position {
215 return Err(Error::SignerPositionMismatch {
216 expected: self.position.tuple(),
217 actual: actual.tuple(),
218 });
219 }
220 actual.ensure_can_advance()?;
221
222 let derived_root = keypair
223 .derive::<WINTERNITZ_SCALARS>()
224 .to_pubkey()
225 .merklize();
226 if derived_root.as_bytes() != &self.current_root {
227 return Err(Error::RootMismatch);
228 }
229
230 let signature = {
231 let preimage = self.preimage();
232 let sig = keypair.sign_and_increment::<WINTERNITZ_SCALARS>(&preimage);
233 let mut bytes = [0u8; SIGNATURE_LEN];
234 bytes.copy_from_slice(sig.as_bytes());
235 bytes
236 };
237 let next_position = SigningPosition::from_keypair(keypair);
238
239 Ok(SignedAdvance {
240 wallet_id: self.wallet_id,
241 signing_position: self.position,
242 next_position,
243 signature,
244 plan: self.plan,
245 })
246 }
247}
248
249pub struct SignedAdvance {
251 wallet_id: [u8; 32],
252 signing_position: SigningPosition,
253 next_position: SigningPosition,
254 signature: [u8; SIGNATURE_LEN],
255 plan: AdvancePlan,
256}
257
258impl SignedAdvance {
259 pub fn wallet_id(&self) -> &[u8; 32] {
261 &self.wallet_id
262 }
263
264 pub fn wallet_pda(&self) -> &Address {
266 self.plan.wallet_pda()
267 }
268
269 pub fn signing_position(&self) -> SigningPosition {
271 self.signing_position
272 }
273
274 pub fn next_position(&self) -> SigningPosition {
276 self.next_position
277 }
278
279 pub fn signature_bytes(&self) -> &[u8; SIGNATURE_LEN] {
281 &self.signature
282 }
283
284 pub fn persist<P>(self, persistence: &mut P) -> Result<PersistedAdvance, P::Error>
286 where
287 P: AdvancePersistence,
288 {
289 persistence.persist_signed_advance(&self)?;
290 Ok(PersistedAdvance { signed: self })
291 }
292}
293
294pub trait AdvancePersistence {
296 type Error;
298
299 fn persist_signed_advance(&mut self, advance: &SignedAdvance) -> Result<(), Self::Error>;
301}
302
303pub struct PersistedAdvance {
305 signed: SignedAdvance,
306}
307
308impl PersistedAdvance {
309 pub fn signed(&self) -> &SignedAdvance {
311 &self.signed
312 }
313
314 pub fn advance_instruction(&self) -> Instruction {
316 self.signed.plan.instruction(&self.signed.signature)
317 }
318
319 pub fn transaction_instructions(
321 &self,
322 unit_limit: u32,
323 unit_price_micro_lamports: u64,
324 ) -> Vec<Instruction> {
325 with_compute_budget(
326 &[self.advance_instruction()],
327 unit_limit,
328 unit_price_micro_lamports,
329 )
330 }
331
332 pub fn default_transaction_instructions(
334 &self,
335 unit_price_micro_lamports: u64,
336 ) -> Vec<Instruction> {
337 self.transaction_instructions(
338 DEFAULT_ADVANCE_COMPUTE_UNIT_LIMIT,
339 unit_price_micro_lamports,
340 )
341 }
342
343 pub fn send<S>(&self, sender: &mut S) -> Result<String, S::Error>
345 where
346 S: AdvanceSender,
347 {
348 sender.send_persisted_advance(self)
349 }
350}
351
352pub trait AdvanceSender {
354 type Error;
356
357 fn send_persisted_advance(&mut self, advance: &PersistedAdvance)
359 -> Result<String, Self::Error>;
360}
361
362pub fn token_transfer(
364 source: &Address,
365 destination: &Address,
366 authority: &Address,
367 amount: u64,
368 token_program: &Address,
369) -> Instruction {
370 let mut data = Vec::with_capacity(9);
371 data.push(3);
372 data.extend_from_slice(&amount.to_le_bytes());
373
374 Instruction {
375 program_id: *token_program,
376 accounts: vec![
377 AccountMeta::new(*source, false),
378 AccountMeta::new(*destination, false),
379 AccountMeta::new_readonly(*authority, false),
380 ],
381 data,
382 }
383}