worgl_sdk/holder.rs
1//! A holder's notes, the keys it has handed out, and the transfers it has
2//! signed but not seen land.
3
4use std::cmp::Reverse;
5use std::collections::{HashSet, VecDeque};
6
7use serde::{Deserialize, Serialize};
8use worgl::{Denominations, HolderKey, Note, Output, PolicyError, PublicKey, TransferRequest};
9
10use crate::mint::{MintView, NoteState};
11use crate::payment::{Payment, PaymentRequest, ScheduledTransfer, Spacing};
12use crate::random;
13
14/// Most notes one payment request may ask for.
15pub const MAX_REQUESTED_NOTES: usize = 256;
16
17/// How many of the keys it paid last a holder remembers, so as to refuse
18/// paying one of them again: the notes of four requests of the most notes a
19/// request may ask for, or of hundreds of usual ones, for about 67 KiB of
20/// the saved store. A key paid by a transfer still outgoing, or by a payment
21/// the holder still keeps, is refused however long ago it was paid.
22pub const PAID_KEYS_KEPT: usize = 4 * MAX_REQUESTED_NOTES;
23
24/// Version of the format [`Holder::to_json`] writes for a store that holds
25/// unconfirmed change or notes owed to payees. A reader of format 1 skips
26/// fields it does not know, so it would drop those unseen and lose their
27/// value. A store without them is still written as format 1, which such a
28/// reader opens, missing only the payments kept and the keys paid last.
29const FORMAT: u32 = 2;
30
31/// Seconds before a scheduled rotation by which the transfers that
32/// [`Holder::refresh`] and [`Holder::due_resends`] plan are due, or half the
33/// time left when that is shorter. A plan made at or past the rotation's
34/// time, which the mint has not made yet as far as its info says, has them
35/// all due at once. A payer should settle a payment this long before the
36/// rotation that drops a note it spends.
37pub const REFRESH_MARGIN_SECS: u64 = 300;
38
39/// Seconds over which refreshes are spread when the mint has no schedule.
40const UNSCHEDULED_REFRESH_SECS: u64 = 60;
41
42/// The notes one holder owns, with the one-time key of each, plus the keys
43/// it has handed out, the transfers it has signed but not yet seen land, and
44/// the payments it made until it is done with them.
45///
46/// It keeps the privacy rules without being told: a fresh key for every
47/// note it expects, never two notes in one transfer, transfers of one
48/// payment and refreshes spread out in time, and outputs in random order.
49/// It does no networking: submit the transfers it makes to the mint, and
50/// give it the mint's answers.
51///
52/// Notes are bearer: whoever holds this store holds the money. Save it with
53/// [`to_json`](Self::to_json), encrypted at rest, after every change.
54#[derive(Debug, Clone, Default)]
55pub struct Holder {
56 notes: Vec<Held>,
57 expected: Vec<Expected>,
58 outgoing: Vec<Outgoing>,
59 unconfirmed: Vec<Unconfirmed>,
60 payments: Vec<MadePayment>,
61 /// The keys this holder paid last, oldest first.
62 paid: VecDeque<PublicKey>,
63}
64
65/// A note and the key that spends it.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67struct Held {
68 note: Note,
69 #[serde(with = "secret")]
70 key: HolderKey,
71}
72
73/// A key handed out for a note of `amount` from `mint`, not yet received.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75struct Expected {
76 mint: PublicKey,
77 amount: u64,
78 #[serde(with = "secret")]
79 key: HolderKey,
80}
81
82/// A transfer this holder signed and has not yet seen answered, with the
83/// note it spends.
84#[derive(Debug, Clone, Serialize, Deserialize)]
85struct Outgoing {
86 request: TransferRequest,
87 spends: Held,
88}
89
90/// The key for a note of change from a transfer this holder dropped or
91/// cancelled. The note comes only if the counterparty hands it over, and it
92/// would sit in `generation`, so it is worthless once that is dropped.
93#[derive(Debug, Clone, Serialize, Deserialize)]
94struct Unconfirmed {
95 change: Expected,
96 generation: u64,
97}
98
99/// A payment this holder made, kept while one of its transfers is
100/// [outgoing](Holder::outgoing) or the holder keeps notes owed to the payee
101/// for it.
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103pub struct MadePayment {
104 /// The request it answers.
105 pub request: PaymentRequest,
106 /// The payment as [`Holder::pay`] returned it, to send again to a payee
107 /// who lost it. A transfer [cancelled](Holder::cancel) or
108 /// [forgotten](Holder::forget) leaves it, so it is never handed out
109 /// again; the payment then no longer pays the whole request, which
110 /// [`PaymentRequest::check`] tells, and the payee would refuse it.
111 pub payment: Payment,
112 /// The payee's notes from the mint's answers this holder got itself:
113 /// each time it [settles](Holder::settle) one of the payment's
114 /// transfers, whether a resend or its own submission of the payment got
115 /// the answer. It cannot tell whether the payee submitted the payment
116 /// and has them already, so it keeps them for the payee until they are
117 /// handed over ([`forget_owed`](Holder::forget_owed)) or their layer is
118 /// dropped ([`prune`](Holder::prune)).
119 pub owed: Vec<Note>,
120}
121
122/// Value a holder has at one mint, by what it can do now.
123#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
124pub struct Balance {
125 /// Can be spent now.
126 pub spendable: u128,
127 /// In the last layer: spendable after the next rotation.
128 pub frozen: u128,
129 /// Part of the above that dies at the next rotation unless refreshed.
130 pub expiring: u128,
131 /// In notes the view cannot place: read the mint's info again.
132 pub unknown: u128,
133 /// In notes spent by transfers not yet answered.
134 pub outgoing: u128,
135 /// Expected from keys handed out and not yet paid.
136 pub expected: u128,
137 /// Change of transfers dropped or cancelled, which comes only if the
138 /// counterparty hands it over: see [`Holder::forget`].
139 #[serde(default)]
140 pub unconfirmed: u128,
141}
142
143/// A payment request cannot be made.
144#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
145pub enum RequestError {
146 /// The amount is zero.
147 #[error("the amount must be positive")]
148 Zero,
149 /// The amount cannot be split into the mint's denominations.
150 #[error("{0} cannot be split into the mint's denominations")]
151 Denominations(u64),
152 /// It would take more than [`MAX_REQUESTED_NOTES`] notes.
153 #[error("the request would need more than {MAX_REQUESTED_NOTES} notes")]
154 TooManyNotes,
155}
156
157/// A payment cannot be made.
158#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
159pub enum PayError {
160 /// The request is for another mint than the view.
161 #[error("the request is for another mint")]
162 WrongMint,
163 /// The request asks for nothing.
164 #[error("the request asks for nothing")]
165 Empty,
166 /// The request asks for a note of an amount that is not one of the
167 /// mint's denominations, such as 0. The mint would refuse the transfer.
168 #[error("the request asks for a note of {0}, which is not one of the mint's denominations")]
169 Denomination(u64),
170 /// The request asks for a note to a key this holder has paid already: by
171 /// a transfer still outgoing, by a payment it keeps, among the last
172 /// [`PAID_KEYS_KEPT`] keys it paid, or twice in the request. The payee
173 /// would keep only one of two notes to one key, and the mint would see
174 /// the key twice. Paying again takes a new request.
175 #[error("the request asks for a note to {0}, which this holder has paid already")]
176 Paid(PublicKey),
177 /// Not enough value is spendable now. Frozen notes become spendable at
178 /// the next rotation.
179 #[error("{spendable} is spendable now and {needed} is needed")]
180 Unaffordable {
181 /// Value spendable now.
182 spendable: u128,
183 /// Value asked for.
184 needed: u128,
185 },
186 /// Enough value is spendable, but in notes too small for the notes the
187 /// request asks for. Ask the payee for a request with smaller notes; see
188 /// [`Holder::request_at_most`].
189 #[error(
190 "the requested notes do not fit the spendable notes; the largest spendable note is {largest}"
191 )]
192 Fragmented {
193 /// The largest spendable note.
194 largest: u64,
195 },
196 /// The change would take more notes than one transfer may create.
197 #[error("the change does not fit in {max_outputs} notes per transfer")]
198 TooManyOutputs {
199 /// The mint's limit.
200 max_outputs: u8,
201 },
202 /// A planned transfer breaks the mint's policy. Plans follow the policy,
203 /// so this means a bug.
204 #[error("a planned transfer breaks the mint's policy: {0}")]
205 Policy(PolicyError),
206 /// Signing failed. The library refuses only malformed inputs, so this
207 /// means the store is corrupt.
208 #[error(transparent)]
209 Sign(#[from] worgl::Error),
210}
211
212/// A saved store cannot be read.
213#[derive(Debug, thiserror::Error)]
214pub enum LoadError {
215 /// It is not a saved store.
216 #[error("not a saved note store: {0}")]
217 Json(#[from] serde_json::Error),
218 /// It was written by a newer SDK.
219 #[error("saved in format {0}, which this SDK does not read")]
220 Format(u32),
221}
222
223#[derive(Serialize, Deserialize)]
224struct Saved {
225 format: u32,
226 notes: Vec<Held>,
227 expected: Vec<Expected>,
228 outgoing: Vec<Outgoing>,
229 // Missing from stores of format 1 written before format 2 existed.
230 #[serde(default)]
231 unconfirmed: Vec<Unconfirmed>,
232 #[serde(default)]
233 payments: Vec<MadePayment>,
234 #[serde(default)]
235 paid: VecDeque<PublicKey>,
236}
237
238impl Holder {
239 /// An empty store.
240 pub fn new() -> Self {
241 Self::default()
242 }
243
244 /// Asks for `amount` from `mint`, as the fewest notes its denominations
245 /// allow, each to a fresh key.
246 pub fn request(
247 &mut self,
248 mint: &MintView,
249 amount: u64,
250 ) -> Result<PaymentRequest, RequestError> {
251 self.request_notes(mint, amount, None)
252 }
253
254 /// Asks for `amount` from `mint` in notes of at most `largest` each. A
255 /// payer whose notes are small answers [`PayError::Fragmented`] to a
256 /// plain [`request`](Self::request); this request it can pay.
257 pub fn request_at_most(
258 &mut self,
259 mint: &MintView,
260 amount: u64,
261 largest: u64,
262 ) -> Result<PaymentRequest, RequestError> {
263 self.request_notes(mint, amount, Some(largest))
264 }
265
266 fn request_notes(
267 &mut self,
268 mint: &MintView,
269 amount: u64,
270 largest: Option<u64>,
271 ) -> Result<PaymentRequest, RequestError> {
272 if amount == 0 {
273 return Err(RequestError::Zero);
274 }
275 let amounts = match split(
276 &mint.policy.denominations,
277 amount,
278 largest,
279 MAX_REQUESTED_NOTES,
280 ) {
281 Ok(amounts) => amounts,
282 Err(Split::Inexact) => return Err(RequestError::Denominations(amount)),
283 Err(Split::TooMany) => return Err(RequestError::TooManyNotes),
284 };
285 let mut outputs = Vec::with_capacity(amounts.len());
286 for amount in amounts {
287 let key = HolderKey::generate();
288 outputs.push(Output {
289 amount,
290 holder: key.public_key(),
291 });
292 self.expected.push(Expected {
293 mint: mint.issuer,
294 amount,
295 key,
296 });
297 }
298 random::shuffle(&mut outputs);
299 Ok(PaymentRequest {
300 mint: mint.issuer,
301 outputs,
302 })
303 }
304
305 /// Stops expecting the notes a request asked for, for a request that
306 /// will not be paid.
307 pub fn withdraw(&mut self, request: &PaymentRequest) {
308 self.expected.retain(|e| {
309 !request
310 .outputs
311 .iter()
312 .any(|o| o.holder == e.key.public_key())
313 });
314 }
315
316 /// Answers a payment request with signed transfers.
317 ///
318 /// It prefers one note that covers everything, paid in one transfer
319 /// with the change back to fresh keys. Otherwise it spends several
320 /// notes, one per transfer, largest first, and spaces the transfers by
321 /// `spacing`. Only notes [spendable](NoteState::Spendable) under `mint`
322 /// are used. The notes spent leave the store and wait among the
323 /// [outgoing](Self::outgoing) transfers until [`settle`](Self::settle)
324 /// or [`take_answers`](Self::take_answers) sees them answered, and the
325 /// payment is kept among the [payments](Self::payments).
326 ///
327 /// Before signing anything, it refuses a request for a note the mint
328 /// would not make, of an amount outside its denominations
329 /// ([`PayError::Denomination`]), and a request for a note to a key it
330 /// has paid already ([`PayError::Paid`]), such as the same request
331 /// twice. Each planned transfer is checked against the mint's policy as
332 /// well.
333 pub fn pay(
334 &mut self,
335 mint: &MintView,
336 request: &PaymentRequest,
337 spacing: Spacing,
338 ) -> Result<Payment, PayError> {
339 if request.mint != mint.issuer {
340 return Err(PayError::WrongMint);
341 }
342 if request.outputs.is_empty() {
343 return Err(PayError::Empty);
344 }
345 let mut asked = HashSet::new();
346 for output in &request.outputs {
347 if !mint.policy.denominations.contains(output.amount) {
348 return Err(PayError::Denomination(output.amount));
349 }
350 if !asked.insert(output.holder) || self.has_paid(&output.holder) {
351 return Err(PayError::Paid(output.holder));
352 }
353 }
354 let plan = self.plan(mint, request)?;
355 for part in &plan {
356 let outputs: Vec<u64> = part
357 .paid
358 .iter()
359 .map(|o| o.amount)
360 .chain(part.change.iter().copied())
361 .collect();
362 mint.policy
363 .check_transfer(&[self.notes[part.note].note.amount()], &outputs)
364 .map_err(PayError::Policy)?;
365 }
366 let mut transfers = Vec::with_capacity(plan.len());
367 let mut expected = Vec::new();
368 let mut after_secs = 0u64;
369 for (n, part) in plan.iter().enumerate() {
370 let held = &self.notes[part.note];
371 let mut outputs = part.paid.clone();
372 for &amount in &part.change {
373 let key = HolderKey::generate();
374 outputs.push(Output {
375 amount,
376 holder: key.public_key(),
377 });
378 expected.push(Expected {
379 mint: mint.issuer,
380 amount,
381 key,
382 });
383 }
384 random::shuffle(&mut outputs);
385 let request = TransferRequest::sign(&[(held.note, &held.key)], outputs)?;
386 if n > 0 {
387 after_secs = after_secs.saturating_add(spacing.gap());
388 }
389 transfers.push(ScheduledTransfer {
390 after_secs,
391 request,
392 });
393 }
394 // Nothing failed: move the spent notes out.
395 for (part, scheduled) in plan.iter().zip(&transfers) {
396 self.outgoing.push(Outgoing {
397 request: scheduled.request.clone(),
398 spends: self.notes[part.note].clone(),
399 });
400 }
401 let mut used: Vec<usize> = plan.iter().map(|part| part.note).collect();
402 used.sort_unstable_by_key(|&i| Reverse(i));
403 for i in used {
404 self.notes.remove(i);
405 }
406 self.expected.extend(expected);
407 let payment = Payment {
408 mint: mint.issuer,
409 transfers,
410 };
411 self.payments.push(MadePayment {
412 request: request.clone(),
413 payment: payment.clone(),
414 owed: Vec::new(),
415 });
416 self.remember_paid(request.outputs.iter().map(|o| o.holder));
417 Ok(payment)
418 }
419
420 /// Whether this holder has paid a note to `key`: by an outgoing
421 /// transfer, by a payment it keeps, or among the last keys it paid.
422 fn has_paid(&self, key: &PublicKey) -> bool {
423 self.paid.contains(key)
424 || self
425 .outgoing
426 .iter()
427 .any(|o| o.request.outputs.iter().any(|out| out.holder == *key))
428 || self
429 .payments
430 .iter()
431 .any(|p| p.request.outputs.iter().any(|out| out.holder == *key))
432 }
433
434 /// Adds `keys` to the keys paid last, forgetting the oldest beyond
435 /// [`PAID_KEYS_KEPT`].
436 fn remember_paid(&mut self, keys: impl IntoIterator<Item = PublicKey>) {
437 self.paid.extend(keys);
438 let excess = self.paid.len().saturating_sub(PAID_KEYS_KEPT);
439 self.paid.drain(..excess);
440 }
441
442 /// Chooses which note pays which requested outputs.
443 fn plan(&self, mint: &MintView, request: &PaymentRequest) -> Result<Vec<Part>, PayError> {
444 let max_outputs = usize::from(mint.policy.max_outputs.max(1));
445 let denominations = &mint.policy.denominations;
446 let change_of = |room: u64| split(denominations, room, None, max_outputs).ok();
447 let needed = request.amount();
448 let mut spendable: Vec<usize> = (0..self.notes.len())
449 .filter(|&i| matches!(mint.state(&self.notes[i].note), NoteState::Spendable { .. }))
450 .collect();
451 spendable.sort_by_key(|&i| self.notes[i].note.amount());
452 let amount = |i: usize| self.notes[i].note.amount();
453 let total: u128 = spendable.iter().map(|&i| u128::from(amount(i))).sum();
454 if total < needed {
455 return Err(PayError::Unaffordable {
456 spendable: total,
457 needed,
458 });
459 }
460
461 // The smallest single note that covers everything, change included.
462 for &i in &spendable {
463 if u128::from(amount(i)) < needed {
464 continue;
465 }
466 let room = (u128::from(amount(i)) - needed) as u64;
467 if let Some(change) = change_of(room)
468 && request.outputs.len() + change.len() <= max_outputs
469 {
470 return Ok(vec![Part {
471 note: i,
472 paid: request.outputs.clone(),
473 change,
474 }]);
475 }
476 }
477
478 // Otherwise the largest notes first, each paying the largest
479 // requested outputs it can hold.
480 let mut wanted = request.outputs.clone();
481 wanted.sort_by_key(|o| Reverse(o.amount));
482 let mut notes = spendable.clone();
483 let mut plan = Vec::new();
484 let mut change_limited = false;
485 while !wanted.is_empty() {
486 let Some(i) = notes.pop() else { break };
487 let mut room = amount(i);
488 let mut paid = Vec::new();
489 let mut k = 0;
490 while k < wanted.len() && paid.len() < max_outputs {
491 if wanted[k].amount <= room {
492 room -= wanted[k].amount;
493 paid.push(wanted.remove(k));
494 } else {
495 k += 1;
496 }
497 }
498 // Give back the smallest outputs until the change fits.
499 let change = loop {
500 match change_of(room) {
501 Some(change) if paid.len() + change.len() <= max_outputs => break Some(change),
502 _ if paid.is_empty() => break None,
503 _ => {
504 change_limited = true;
505 let out: Output = paid.pop().expect("paid is not empty");
506 room += out.amount;
507 wanted.push(out);
508 }
509 }
510 };
511 wanted.sort_by_key(|o| Reverse(o.amount));
512 if let Some(change) = change
513 && !paid.is_empty()
514 {
515 plan.push(Part {
516 note: i,
517 paid,
518 change,
519 });
520 }
521 }
522 if !wanted.is_empty() {
523 let max_outputs = mint.policy.max_outputs;
524 let largest = spendable.last().map_or(0, |&i| amount(i));
525 return Err(if change_limited {
526 PayError::TooManyOutputs { max_outputs }
527 } else {
528 PayError::Fragmented { largest }
529 });
530 }
531 Ok(plan)
532 }
533
534 /// Takes in the notes of a mint's answer that were made for this
535 /// holder's keys: from a payment it received, its own change, unconfirmed
536 /// change included, or a refresh. Notes for other keys are ignored, and a
537 /// note already held is not taken twice. Returns the value taken in.
538 ///
539 /// Give it notes from the mint's own answer, or check them first with
540 /// [`Note::verify`]: the store does not check signatures.
541 pub fn absorb(&mut self, notes: &[Note]) -> u128 {
542 let mut taken = 0;
543 for note in notes {
544 if self.notes.iter().any(|h| h.note == *note) {
545 continue;
546 }
547 let pays = |e: &Expected| {
548 e.mint == *note.issuer()
549 && e.amount == note.amount()
550 && e.key.public_key() == *note.holder()
551 };
552 let key = if let Some(i) = self.expected.iter().position(pays) {
553 self.expected.swap_remove(i).key
554 } else if let Some(i) = self.unconfirmed.iter().position(|u| pays(&u.change)) {
555 self.unconfirmed.swap_remove(i).change.key
556 } else {
557 continue;
558 };
559 self.notes.push(Held { note: *note, key });
560 taken += u128::from(note.amount());
561 }
562 taken
563 }
564
565 /// The notes of a mint's answers made for neither a note this holder
566 /// holds nor a key it expects, unconfirmed change included: the
567 /// counterparty's. A payee hands them back to the payer, whose change
568 /// they are, and a payer that submitted a payment hands them to the
569 /// payee. They tell the counterparty nothing about this holder's notes:
570 /// they are bound to the counterparty's own keys.
571 pub fn theirs(&self, notes: &[Note]) -> Vec<Note> {
572 notes
573 .iter()
574 .filter(|note| {
575 let holder = note.holder();
576 !self.notes.iter().any(|h| h.note.holder() == holder)
577 && !self.expected.iter().any(|e| e.key.public_key() == *holder)
578 && !self
579 .unconfirmed
580 .iter()
581 .any(|u| u.change.key.public_key() == *holder)
582 })
583 .copied()
584 .collect()
585 }
586
587 /// Records the mint's answer to one of this holder's transfers: the
588 /// transfer stops being outgoing and its notes for this holder are taken
589 /// in. Returns the value taken in.
590 ///
591 /// When the transfer was outgoing and is part of a payment this holder
592 /// made, the answer's notes for the payee are kept for it, among the
593 /// payment's [owed](MadePayment::owed) notes: a resend may be what
594 /// carried out the payment.
595 pub fn settle(&mut self, request: &TransferRequest, answer: &[Note]) -> u128 {
596 let before = self.outgoing.len();
597 self.outgoing.retain(|o| o.request != *request);
598 if self.outgoing.len() < before
599 && let Some(made) = self
600 .payments
601 .iter_mut()
602 .find(|p| p.payment.transfers.iter().any(|t| t.request == *request))
603 {
604 for note in answer {
605 let payee = note.issuer() == &made.payment.mint
606 && made
607 .request
608 .outputs
609 .iter()
610 .any(|o| o.holder == *note.holder() && o.amount == note.amount());
611 if payee && !made.owed.contains(note) {
612 made.owed.push(*note);
613 }
614 }
615 }
616 let taken = self.absorb(answer);
617 self.tidy();
618 taken
619 }
620
621 /// Takes in notes the counterparty handed over from the mint's answers,
622 /// as [`absorb`](Self::absorb) does, then settles each outgoing transfer
623 /// that pays this holder notes, once it holds every one of them with the
624 /// transfer's mint, amount and key. Returns the value taken in.
625 ///
626 /// A transfer that pays this holder nothing, such as a payment that
627 /// needs no change, stays outgoing: only [resending](Self::resend) it
628 /// shows it was applied.
629 ///
630 /// Give it notes from the mint's own answer, or check them first with
631 /// [`Note::verify`]: the store does not check signatures, and a forged
632 /// note would settle a transfer as surely as a real one.
633 ///
634 /// A signature shows that the mint issued a note, not which transfer it
635 /// answers. A counterparty holding the transfer can pay the same keys
636 /// from notes of its own and leave the transfer unsubmitted: it then
637 /// stops being outgoing though the mint never applied it, and nothing
638 /// resends it before its note dies. A hand-back settles a transfer only
639 /// as surely as the counterparty submitted it; to be sure, ask the mint
640 /// (`POST /v1/verify`) whether the note the transfer spends is spent
641 /// before taking the notes in.
642 pub fn take_answers(&mut self, notes: &[Note]) -> u128 {
643 let taken = self.absorb(notes);
644 let (held, expected) = (&self.notes, &self.expected);
645 self.outgoing.retain(|o| {
646 let mint = o.spends.note.issuer();
647 let mut own = o
648 .request
649 .outputs
650 .iter()
651 .filter(|out| {
652 held.iter().any(|h| h.note.holder() == &out.holder)
653 || expected.iter().any(|e| e.key.public_key() == out.holder)
654 })
655 .peekable();
656 let answered = own.peek().is_some()
657 && own.all(|out| {
658 held.iter().any(|h| {
659 h.note.issuer() == mint
660 && h.note.amount() == out.amount
661 && h.note.holder() == &out.holder
662 })
663 });
664 !answered
665 });
666 self.tidy();
667 taken
668 }
669
670 /// Drops an outgoing transfer whose note is gone, such as one the mint
671 /// refused because its note was already spent or had expired: had the
672 /// mint applied this transfer and still kept its answer, it would have
673 /// answered with the notes (see [`resend`](Self::resend)). Returns
674 /// whether the transfer was outgoing.
675 ///
676 /// The mint may have applied it all the same, so the keys of its change
677 /// stay, as unconfirmed change: change the counterparty hands over later
678 /// is still taken in by [`take_answers`](Self::take_answers) or
679 /// [`absorb`](Self::absorb), until the layer it would sit in is dropped
680 /// ([`prune`](Self::prune)). The transfer leaves its
681 /// [payment](MadePayment::payment).
682 pub fn forget(&mut self, request: &TransferRequest) -> bool {
683 self.end(request).is_some()
684 }
685
686 /// Takes back the note of an outgoing transfer, for one the mint never
687 /// applied, such as one nobody else holds that will never be submitted.
688 /// The keys of its change stay, as unconfirmed change, and the transfer
689 /// leaves its payment, as for [`forget`](Self::forget). Returns whether
690 /// the transfer was outgoing.
691 ///
692 /// A refusal does not always show that the mint never applied it: the
693 /// mint checks its policy before its record of answers, so a transfer it
694 /// applied under a looser policy is refused as `policy` too. Once a payee
695 /// holds the transfer, they can still submit it. If the mint applies it,
696 /// or already had, the payee's notes and the note taken back claim the
697 /// same value, and the mint refuses whichever is spent second.
698 pub fn cancel(&mut self, request: &TransferRequest) -> bool {
699 let Some(spends) = self.end(request) else {
700 return false;
701 };
702 self.notes.push(spends);
703 true
704 }
705
706 /// Ends an outgoing transfer and returns the note it spends. The keys of
707 /// its change move from the keys expected to the unconfirmed change, and
708 /// it leaves the [payment](MadePayment::payment) that holds it, so that
709 /// payment is never handed out again with it.
710 fn end(&mut self, request: &TransferRequest) -> Option<Held> {
711 let i = self.outgoing.iter().position(|o| o.request == *request)?;
712 let outgoing = self.outgoing.remove(i);
713 // A transfer's notes sit one layer past the note it spends.
714 let generation = outgoing.spends.note.serial().generation.saturating_add(1);
715 let mut i = 0;
716 while i < self.expected.len() {
717 let key = self.expected[i].key.public_key();
718 if request.outputs.iter().any(|o| o.holder == key) {
719 let change = self.expected.swap_remove(i);
720 self.unconfirmed.push(Unconfirmed { change, generation });
721 } else {
722 i += 1;
723 }
724 }
725 for made in &mut self.payments {
726 made.payment.transfers.retain(|t| t.request != *request);
727 }
728 self.tidy();
729 Some(outgoing.spends)
730 }
731
732 /// The payments this holder made and keeps: each while one of its
733 /// transfers is [outgoing](Self::outgoing), or while it keeps notes
734 /// [owed](MadePayment::owed) to the payee.
735 pub fn payments(&self) -> impl Iterator<Item = &MadePayment> {
736 self.payments.iter()
737 }
738
739 /// Stops keeping `notes` for payees, once they are handed over: they
740 /// leave the [owed](MadePayment::owed) notes of the payments that keep
741 /// them, and a payment with nothing else kept leaves the
742 /// [payments](Self::payments). Returns the value it stopped keeping.
743 ///
744 /// The payee checks their signatures and takes them in, as notes from
745 /// the mint's answers: with [`take_answers`](Self::take_answers), or
746 /// [`absorb`](Self::absorb).
747 pub fn forget_owed(&mut self, notes: &[Note]) -> u128 {
748 let mut value = 0;
749 for made in &mut self.payments {
750 made.owed.retain(|note| {
751 let handed = notes.contains(note);
752 if handed {
753 value += u128::from(note.amount());
754 }
755 !handed
756 });
757 }
758 self.tidy();
759 value
760 }
761
762 /// Drops the payments with no transfer outgoing and no notes owed.
763 fn tidy(&mut self) {
764 let outgoing = &self.outgoing;
765 self.payments.retain(|p| {
766 !p.owed.is_empty()
767 || p.payment
768 .transfers
769 .iter()
770 .any(|t| outgoing.iter().any(|o| o.request == t.request))
771 });
772 }
773
774 /// Transfers not yet answered. Resend them after a crash or a timeout,
775 /// with [`resend`](Self::resend): the mint answers an identical request
776 /// with the identical notes, before or after a rotation, for as long as
777 /// it remembers the transfer.
778 pub fn outgoing(&self) -> impl Iterator<Item = &TransferRequest> {
779 self.outgoing.iter().map(|o| &o.request)
780 }
781
782 /// What this holder may lose on `request` if the mint applied it and its
783 /// answer is lost: the value it pays to keys this holder still expects,
784 /// unconfirmed change included. `Some` when the transfer is outgoing or
785 /// pays such a key, even if the value is 0; `None` when nothing of this
786 /// holder's rides on it.
787 ///
788 /// A transfer refused as spent or expired may have been applied after
789 /// all, by the counterparty or by an earlier try whose answer was lost.
790 /// A counterparty that submitted it holds the answer and can hand the
791 /// notes over, for [`take_answers`](Self::take_answers).
792 pub fn at_stake(&self, request: &TransferRequest) -> Option<u128> {
793 let mint = request.inputs.first().map(|spend| spend.note.issuer());
794 let expected: u128 = request
795 .outputs
796 .iter()
797 .filter(|o| {
798 let pays = |e: &Expected| {
799 Some(&e.mint) == mint && e.amount == o.amount && e.key.public_key() == o.holder
800 };
801 self.expected.iter().any(pays) || self.unconfirmed.iter().any(|u| pays(&u.change))
802 })
803 .map(|o| u128::from(o.amount))
804 .sum();
805 let outgoing = self.outgoing.iter().any(|o| o.request == *request);
806 (outgoing || expected > 0).then_some(expected)
807 }
808
809 /// The [outgoing](Self::outgoing) transfers in random order, each with a
810 /// time to resend it, spaced by `spacing`. Resent together, they would
811 /// tell the mint their notes had one owner.
812 ///
813 /// The mint answers a resent transfer with the same notes only while it
814 /// keeps its answer, and at the latest until the notes it issued expire
815 /// (see [Error codes]). After that, a transfer already applied is
816 /// refused as spent or expired, and its change can only come from the
817 /// counterparty, for [`take_answers`](Self::take_answers).
818 /// [`due_resends`](Self::due_resends) gives the transfers to resend
819 /// before a rotation ends their chance.
820 ///
821 /// [Error codes]: https://github.com/zhangheqi/worgl/blob/main/site/src/content/docs/docs/error-codes.md#sending-a-request-again
822 pub fn resend(&self, spacing: Spacing) -> Vec<ScheduledTransfer> {
823 let mut requests: Vec<TransferRequest> = self.outgoing().cloned().collect();
824 random::shuffle(&mut requests);
825 let mut after_secs = 0u64;
826 requests
827 .into_iter()
828 .enumerate()
829 .map(|(n, request)| {
830 if n > 0 {
831 after_secs = after_secs.saturating_add(spacing.gap());
832 }
833 ScheduledTransfer {
834 after_secs,
835 request,
836 }
837 })
838 .collect()
839 }
840
841 /// The [outgoing](Self::outgoing) transfers at `mint` to resend before
842 /// its next rotation, each at its own random moment before it, as
843 /// [`refresh`](Self::refresh) spreads its transfers from the same `now`.
844 /// Nothing in the store changes.
845 ///
846 /// A transfer is due when the note it spends dies at that rotation:
847 /// after it, a transfer the mint never applied can no longer be
848 /// applied. It is still due when that note was dropped at the last
849 /// rotation, since the mint may keep an applied transfer's answer until
850 /// the notes it issued die, at the next. Call it before
851 /// [`refresh`](Self::refresh), whose transfers it would list too, submit
852 /// them with the refreshes and [`settle`](Self::settle) the answers.
853 /// Resending a payment the payee has not submitted yet carries it out;
854 /// the payee still gets the same notes by submitting it while the mint
855 /// keeps its answer, and settling the answer keeps them for the payee
856 /// too (see [`MadePayment::owed`]).
857 pub fn due_resends(&self, mint: &MintView, now: u64) -> Vec<ScheduledTransfer> {
858 let window = refresh_window(mint, now);
859 let mut transfers: Vec<ScheduledTransfer> = self
860 .outgoing
861 .iter()
862 .filter(|o| {
863 let note = &o.spends.note;
864 let dropped_last = note.issuer() == &mint.issuer
865 && note.serial().generation.checked_add(1) == Some(mint.oldest);
866 dropped_last
867 || matches!(
868 mint.state(note),
869 NoteState::Spendable {
870 expires_after_rotations: 1
871 } | NoteState::Frozen {
872 expires_after_rotations: 1
873 }
874 )
875 })
876 .map(|o| ScheduledTransfer {
877 after_secs: random::between(0, window),
878 request: o.request.clone(),
879 })
880 .collect();
881 transfers.sort_by_key(|t| t.after_secs);
882 transfers
883 }
884
885 /// Pays each of `mint`'s notes that dies at the next rotation to a fresh
886 /// key of this holder, each at its own random moment before the
887 /// rotation, so the mint cannot group one holder's notes by when they
888 /// moved. The refreshed note lands one layer later and lives as long as
889 /// a received note.
890 ///
891 /// Call it once per rotation period, after reading the mint's info.
892 /// Submit each transfer at its time and [`settle`](Self::settle) the
893 /// answer.
894 ///
895 /// `mint` alone says which notes are due: those in its first layer.
896 /// `now`, the Unix time by the mint's clock, says only when: the
897 /// transfers are spread over the time left before the next rotation,
898 /// less [`REFRESH_MARGIN_SECS`], and are all due at once when `now` is at
899 /// or past it, since the mint has not made it yet as far as `mint` knows.
900 /// A clock behind the mint's by more than that margin can spread them
901 /// past the rotation, where they fail, so take `now` from the mint, such
902 /// as from the `Date` of its answer.
903 pub fn refresh(&mut self, mint: &MintView, now: u64) -> Vec<ScheduledTransfer> {
904 let window = refresh_window(mint, now);
905 let mut transfers = Vec::new();
906 let mut i = 0;
907 while i < self.notes.len() {
908 let due = matches!(
909 mint.state(&self.notes[i].note),
910 NoteState::Spendable {
911 expires_after_rotations: 1
912 }
913 );
914 if !due {
915 i += 1;
916 continue;
917 }
918 let held = self.notes.remove(i);
919 let key = HolderKey::generate();
920 let output = Output {
921 amount: held.note.amount(),
922 holder: key.public_key(),
923 };
924 let request = TransferRequest::single(held.note, &held.key, output)
925 .expect("a held note is signed with its own key");
926 self.expected.push(Expected {
927 mint: mint.issuer,
928 amount: output.amount,
929 key,
930 });
931 self.outgoing.push(Outgoing {
932 request: request.clone(),
933 spends: held,
934 });
935 transfers.push(ScheduledTransfer {
936 after_secs: random::between(0, window),
937 request,
938 });
939 }
940 transfers.sort_by_key(|t| t.after_secs);
941 transfers
942 }
943
944 /// Removes `mint`'s notes whose layer was dropped and returns the value
945 /// lost. It also stops waiting for unconfirmed change of `mint` that
946 /// would sit in a dropped layer, and stops keeping notes owed to payees
947 /// once their layer is dropped; neither counts as lost.
948 ///
949 /// It goes by `mint` alone, with no clock: with a view of the mint's
950 /// info, it removes a note, and its key for good, only once the mint has
951 /// dropped the note's layer.
952 pub fn prune(&mut self, mint: &MintView) -> u128 {
953 let mut lost = 0;
954 self.notes.retain(|h| {
955 let expired = mint.state(&h.note) == NoteState::Expired;
956 if expired {
957 lost += u128::from(h.note.amount());
958 }
959 !expired
960 });
961 self.unconfirmed
962 .retain(|u| u.change.mint != mint.issuer || u.generation >= mint.oldest);
963 for made in &mut self.payments {
964 made.owed
965 .retain(|note| mint.state(note) != NoteState::Expired);
966 }
967 self.tidy();
968 lost
969 }
970
971 /// Value held at `mint`, by what it can do now.
972 pub fn balance(&self, mint: &MintView) -> Balance {
973 let mut balance = Balance::default();
974 for held in &self.notes {
975 let value = u128::from(held.note.amount());
976 match mint.state(&held.note) {
977 NoteState::Spendable {
978 expires_after_rotations,
979 } => {
980 balance.spendable += value;
981 if expires_after_rotations == 1 {
982 balance.expiring += value;
983 }
984 }
985 NoteState::Frozen {
986 expires_after_rotations,
987 } => {
988 balance.frozen += value;
989 if expires_after_rotations == 1 {
990 balance.expiring += value;
991 }
992 }
993 NoteState::Unknown => balance.unknown += value,
994 NoteState::Expired | NoteState::OtherMint => {}
995 }
996 }
997 for outgoing in &self.outgoing {
998 if outgoing.spends.note.issuer() == &mint.issuer {
999 balance.outgoing += u128::from(outgoing.spends.note.amount());
1000 }
1001 }
1002 for expected in &self.expected {
1003 if expected.mint == mint.issuer {
1004 balance.expected += u128::from(expected.amount);
1005 }
1006 }
1007 for unconfirmed in &self.unconfirmed {
1008 if unconfirmed.change.mint == mint.issuer {
1009 balance.unconfirmed += u128::from(unconfirmed.change.amount);
1010 }
1011 }
1012 balance
1013 }
1014
1015 /// The notes held, of every mint.
1016 pub fn notes(&self) -> impl Iterator<Item = &Note> {
1017 self.notes.iter().map(|h| &h.note)
1018 }
1019
1020 /// The whole store as JSON, secret keys included.
1021 ///
1022 /// A store with unconfirmed change or notes owed to payees is written in
1023 /// format 2, which an SDK that reads only format 1 refuses rather than
1024 /// open without them; any other store is written in format 1.
1025 pub fn to_json(&self) -> String {
1026 let plain = self.unconfirmed.is_empty() && self.payments.iter().all(|p| p.owed.is_empty());
1027 serde_json::to_string(&Saved {
1028 format: if plain { 1 } else { FORMAT },
1029 notes: self.notes.clone(),
1030 expected: self.expected.clone(),
1031 outgoing: self.outgoing.clone(),
1032 unconfirmed: self.unconfirmed.clone(),
1033 payments: self.payments.clone(),
1034 paid: self.paid.clone(),
1035 })
1036 .expect("a store always serializes")
1037 }
1038
1039 /// Reads a store written by [`to_json`](Self::to_json), by this SDK or
1040 /// an earlier one.
1041 pub fn from_json(json: &str) -> Result<Self, LoadError> {
1042 let saved: Saved = serde_json::from_str(json)?;
1043 if !(1..=FORMAT).contains(&saved.format) {
1044 return Err(LoadError::Format(saved.format));
1045 }
1046 Ok(Self {
1047 notes: saved.notes,
1048 expected: saved.expected,
1049 outgoing: saved.outgoing,
1050 unconfirmed: saved.unconfirmed,
1051 payments: saved.payments,
1052 paid: saved.paid,
1053 })
1054 }
1055}
1056
1057/// Seconds from `now` over which to spread transfers that must land before
1058/// `mint`'s next rotation: none once that rotation is due.
1059fn refresh_window(mint: &MintView, now: u64) -> u64 {
1060 match mint.until_rotation(now) {
1061 Some(left) => left.saturating_sub(REFRESH_MARGIN_SECS.min(left / 2)),
1062 None => UNSCHEDULED_REFRESH_SECS,
1063 }
1064}
1065
1066/// One transfer of a payment: the note it spends, the requested outputs it
1067/// pays and the change it returns.
1068struct Part {
1069 note: usize,
1070 paid: Vec<Output>,
1071 change: Vec<u64>,
1072}
1073
1074enum Split {
1075 Inexact,
1076 TooMany,
1077}
1078
1079/// Splits `amount` into allowed denominations no larger than `largest`,
1080/// largest first, as at most `limit` notes.
1081fn split(
1082 denominations: &Denominations,
1083 amount: u64,
1084 largest: Option<u64>,
1085 limit: usize,
1086) -> Result<Vec<u64>, Split> {
1087 if amount == 0 {
1088 return Ok(Vec::new());
1089 }
1090 let pieces = match (denominations, largest) {
1091 (_, Some(0)) => return Err(Split::Inexact),
1092 (denominations, None) => denominations.decompose(amount),
1093 (Denominations::Any, Some(cap)) => {
1094 let mut pieces = vec![(cap, amount / cap)];
1095 if !amount.is_multiple_of(cap) {
1096 pieces.push((amount % cap, 1));
1097 }
1098 Some(pieces)
1099 }
1100 (Denominations::PowersOfTwo, Some(cap)) => {
1101 let top = 1u64 << (63 - cap.leading_zeros());
1102 let mut pieces = vec![(top, amount / top)];
1103 pieces.extend(
1104 Denominations::PowersOfTwo
1105 .decompose(amount % top)
1106 .unwrap_or_default(),
1107 );
1108 Some(pieces)
1109 }
1110 (Denominations::List(values), Some(cap)) => {
1111 let allowed = values.iter().copied().filter(|&v| v <= cap);
1112 Denominations::list(allowed)
1113 .ok()
1114 .and_then(|d| d.decompose(amount))
1115 }
1116 };
1117 let pieces = pieces.ok_or(Split::Inexact)?;
1118 let count: u128 = pieces.iter().map(|&(_, n)| u128::from(n)).sum();
1119 if count > limit as u128 {
1120 return Err(Split::TooMany);
1121 }
1122 Ok(pieces
1123 .into_iter()
1124 .filter(|&(_, n)| n > 0)
1125 .flat_map(|(value, n)| std::iter::repeat_n(value, n as usize))
1126 .collect())
1127}
1128
1129/// Holder keys saved as their 32-byte secrets in hex.
1130mod secret {
1131 use serde::{Deserialize, Deserializer, Serializer, de};
1132 use worgl::HolderKey;
1133
1134 pub(super) fn serialize<S: Serializer>(key: &HolderKey, s: S) -> Result<S::Ok, S::Error> {
1135 s.serialize_str(&hex::encode(key.to_secret_bytes()))
1136 }
1137
1138 pub(super) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<HolderKey, D::Error> {
1139 let text = String::deserialize(d)?;
1140 let mut secret = [0u8; 32];
1141 hex::decode_to_slice(text, &mut secret).map_err(de::Error::custom)?;
1142 Ok(HolderKey::from_secret_bytes(&secret))
1143 }
1144}
1145
1146#[cfg(test)]
1147mod tests {
1148 use super::*;
1149
1150 #[test]
1151 fn splits_follow_the_denominations_and_the_cap() {
1152 let p2 = Denominations::PowersOfTwo;
1153 assert_eq!(split(&p2, 13, None, 16).ok(), Some(vec![8, 4, 1]));
1154 assert_eq!(split(&p2, 13, Some(4), 16).ok(), Some(vec![4, 4, 4, 1]));
1155 assert_eq!(split(&p2, 13, Some(5), 16).ok(), Some(vec![4, 4, 4, 1]));
1156 assert!(matches!(
1157 split(&p2, 1 << 20, Some(1), 256),
1158 Err(Split::TooMany)
1159 ));
1160 assert_eq!(split(&p2, 0, None, 0).ok(), Some(vec![]));
1161
1162 let any = Denominations::Any;
1163 assert_eq!(split(&any, 7, None, 16).ok(), Some(vec![7]));
1164 assert_eq!(split(&any, 7, Some(3), 16).ok(), Some(vec![3, 3, 1]));
1165
1166 let list = Denominations::list([1, 2, 5]).unwrap();
1167 assert_eq!(split(&list, 9, None, 16).ok(), Some(vec![5, 2, 2]));
1168 assert_eq!(split(&list, 9, Some(2), 16).ok(), Some(vec![2, 2, 2, 2, 1]));
1169 let fives = Denominations::list([5]).unwrap();
1170 assert!(matches!(split(&fives, 7, None, 16), Err(Split::Inexact)));
1171 assert!(matches!(split(&fives, 5, Some(4), 16), Err(Split::Inexact)));
1172 }
1173
1174 #[test]
1175 fn the_keys_paid_last_are_kept_up_to_the_bound_and_saved() {
1176 let mut holder = Holder::new();
1177 let keys: Vec<PublicKey> = (0..=PAID_KEYS_KEPT)
1178 .map(|_| HolderKey::generate().public_key())
1179 .collect();
1180 holder.remember_paid(keys.iter().copied());
1181 assert_eq!(holder.paid.len(), PAID_KEYS_KEPT);
1182 let holder = Holder::from_json(&holder.to_json()).unwrap();
1183 assert!(!holder.has_paid(&keys[0]), "the oldest is forgotten");
1184 assert!(keys[1..].iter().all(|key| holder.has_paid(key)));
1185 }
1186}