Skip to main content

worgl_sdk/
lib.rs

1//! The holder's side of [Worgl](worgl): a note store that pays, receives and
2//! refreshes notes while keeping the privacy rules.
3//!
4//! The mint sees every note's lineage but not who holds what. That stays
5//! true only if holders follow a few rules, and this crate follows them so
6//! applications need not know them:
7//!
8//! - **A fresh key for every note.** Two notes under one key tell the mint
9//!   they share an owner.
10//! - **One note per transfer.** A transfer that spends two notes tells the
11//!   mint they had one owner. Change goes back to fresh keys.
12//! - **Spread in time.** When a payment needs several notes, their
13//!   transfers are submitted at random gaps; refreshes are spread across the
14//!   rotation period. Transfers made together can be linked by timing alone.
15//! - **No order to read.** Outputs are shuffled, so the payee's notes are
16//!   not always first.
17//!
18//! The crate does no networking and keeps no files, so it runs anywhere,
19//! WebAssembly included. A client submits the transfers it makes and hands
20//! back the mint's answers.
21//!
22//! # A payment
23//!
24//! 1. The payee calls [`Holder::request`] and sends the [`PaymentRequest`]
25//!    to the payer: an amount as notes, each to a fresh key of the payee.
26//! 2. The payer calls [`Holder::pay`] and returns the [`Payment`]: signed
27//!    transfers, each spending one of its notes, with a time to submit each.
28//! 3. The payee checks it with [`PaymentRequest::check`], submits each
29//!    transfer at its time, takes the new notes in with [`Holder::absorb`],
30//!    and hands the rest of the answers, [`Holder::theirs`], back to the
31//!    payer: its change.
32//! 4. The payer checks their signatures and takes them in with
33//!    [`Holder::take_answers`], which settles the transfers they answer.
34//!    Without them, it resends its outgoing transfers at the times
35//!    [`Holder::resend`] gives and records each answer with
36//!    [`Holder::settle`], which works while the mint keeps its answers, and
37//!    keeps the payee's notes from it in [`Holder::payments`], in case the
38//!    payee has not submitted.
39//!
40//! ```
41//! use worgl::{Issuer, IssuerKey, LedgerConfig, Policy};
42//! use worgl_sdk::{Holder, MintInfo, MintView, Spacing};
43//!
44//! // A mint in memory, standing in for the server.
45//! let ledger = LedgerConfig { layers: 4, layer_size: 1 << 16 };
46//! let mut mint = Issuer::new(IssuerKey::generate(), ledger, Policy::cash_like())?;
47//! let view = |mint: &Issuer| {
48//!     MintView::new(&MintInfo {
49//!         issuer: *mint.public_key(),
50//!         layers: mint.layers(),
51//!         policy: mint.policy().clone(),
52//!         rotation: None,
53//!         paused: false,
54//!     })
55//! };
56//!
57//! // Alice is minted 8 in one note.
58//! let (mut alice, mut bob) = (Holder::new(), Holder::new());
59//! let funding = alice.request(&view(&mint)?, 8)?;
60//! let note = mint.mint(8, funding.outputs[0].holder)?;
61//! alice.absorb(&[note]);
62//!
63//! // Bob asks for 5, Alice pays, Bob checks and submits.
64//! let request = bob.request(&view(&mint)?, 5)?;
65//! let payment = alice.pay(&view(&mint)?, &request, Spacing::default())?;
66//! request.check(&payment)?;
67//! for transfer in &payment.transfers {
68//!     let answer = mint.transfer(&transfer.request)?;
69//!     bob.absorb(&answer);
70//!     // Bob hands Alice the rest, her change; she checks it and keeps it.
71//!     let change = bob.theirs(&answer);
72//!     for note in &change {
73//!         note.verify(mint.keys())?;
74//!     }
75//!     alice.take_answers(&change);
76//! }
77//!
78//! // Bob's 5 is frozen until the next rotation; Alice has 3 in change.
79//! assert_eq!(bob.balance(&view(&mint)?).frozen, 5);
80//! assert_eq!(alice.balance(&view(&mint)?).frozen, 3);
81//! assert_eq!(alice.outgoing().count(), 0);
82//! # Ok::<(), Box<dyn std::error::Error>>(())
83//! ```
84//!
85//! A mint's info, read into a [`MintView`], tells the store which notes can
86//! move now: a note that just arrived sits in the last layer and is frozen
87//! until the next rotation. The view holds the info's layers, which no clock
88//! moves, so read the info again after a rotation. [`Holder::refresh`] keeps
89//! notes alive by paying the ones about to expire to the holder itself.
90
91mod holder;
92mod mint;
93mod payment;
94mod random;
95
96pub use holder::{
97    Balance, Holder, LoadError, MAX_REQUESTED_NOTES, MadePayment, PAID_KEYS_KEPT, PayError,
98    REFRESH_MARGIN_SECS, RequestError,
99};
100pub use mint::{MintInfo, MintView, NoLayers, NoteState, Schedule};
101pub use payment::{CheckError, Payment, PaymentRequest, ScheduledTransfer, Spacing};