Skip to main content

Crate worgl_sdk

Crate worgl_sdk 

Source
Expand description

The holder’s side of Worgl: a note store that pays, receives and refreshes notes while keeping the privacy rules.

The mint sees every note’s lineage but not who holds what. That stays true only if holders follow a few rules, and this crate follows them so applications need not know them:

  • A fresh key for every note. Two notes under one key tell the mint they share an owner.
  • One note per transfer. A transfer that spends two notes tells the mint they had one owner. Change goes back to fresh keys.
  • Spread in time. When a payment needs several notes, their transfers are submitted at random gaps; refreshes are spread across the rotation period. Transfers made together can be linked by timing alone.
  • No order to read. Outputs are shuffled, so the payee’s notes are not always first.

The crate does no networking and keeps no files, so it runs anywhere, WebAssembly included. A client submits the transfers it makes and hands back the mint’s answers.

§A payment

  1. The payee calls Holder::request and sends the PaymentRequest to the payer: an amount as notes, each to a fresh key of the payee.
  2. The payer calls Holder::pay and returns the Payment: signed transfers, each spending one of its notes, with a time to submit each.
  3. The payee checks it with PaymentRequest::check, submits each transfer at its time, takes the new notes in with Holder::absorb, and hands the rest of the answers, Holder::theirs, back to the payer: its change.
  4. The payer checks their signatures and takes them in with Holder::take_answers, which settles the transfers they answer. Without them, it resends its outgoing transfers at the times Holder::resend gives and records each answer with Holder::settle, which works while the mint keeps its answers, and keeps the payee’s notes from it in Holder::payments, in case the payee has not submitted.
use worgl::{Issuer, IssuerKey, LedgerConfig, Policy};
use worgl_sdk::{Holder, MintInfo, MintView, Spacing};

// A mint in memory, standing in for the server.
let ledger = LedgerConfig { layers: 4, layer_size: 1 << 16 };
let mut mint = Issuer::new(IssuerKey::generate(), ledger, Policy::cash_like())?;
let view = |mint: &Issuer| {
    MintView::new(&MintInfo {
        issuer: *mint.public_key(),
        layers: mint.layers(),
        policy: mint.policy().clone(),
        rotation: None,
        paused: false,
    })
};

// Alice is minted 8 in one note.
let (mut alice, mut bob) = (Holder::new(), Holder::new());
let funding = alice.request(&view(&mint)?, 8)?;
let note = mint.mint(8, funding.outputs[0].holder)?;
alice.absorb(&[note]);

// Bob asks for 5, Alice pays, Bob checks and submits.
let request = bob.request(&view(&mint)?, 5)?;
let payment = alice.pay(&view(&mint)?, &request, Spacing::default())?;
request.check(&payment)?;
for transfer in &payment.transfers {
    let answer = mint.transfer(&transfer.request)?;
    bob.absorb(&answer);
    // Bob hands Alice the rest, her change; she checks it and keeps it.
    let change = bob.theirs(&answer);
    for note in &change {
        note.verify(mint.keys())?;
    }
    alice.take_answers(&change);
}

// Bob's 5 is frozen until the next rotation; Alice has 3 in change.
assert_eq!(bob.balance(&view(&mint)?).frozen, 5);
assert_eq!(alice.balance(&view(&mint)?).frozen, 3);
assert_eq!(alice.outgoing().count(), 0);

A mint’s info, read into a MintView, tells the store which notes can move now: a note that just arrived sits in the last layer and is frozen until the next rotation. The view holds the info’s layers, which no clock moves, so read the info again after a rotation. Holder::refresh keeps notes alive by paying the ones about to expire to the holder itself.

Structs§

Balance
Value a holder has at one mint, by what it can do now.
Holder
The notes one holder owns, with the one-time key of each, plus the keys it has handed out, the transfers it has signed but not yet seen land, and the payments it made until it is done with them.
MadePayment
A payment this holder made, kept while one of its transfers is outgoing or the holder keeps notes owed to the payee for it.
MintInfo
The parts of a mint’s GET /v1/mints/{mint}/info answer a holder needs. Other fields in the answer are ignored.
MintView
What a holder knows about one mint: enough to tell, without asking it, whether a note can move now and when it dies.
NoLayers
A mint’s info has no layers.
Payment
The payer’s answer to a PaymentRequest: signed transfers that, submitted to the mint, create the payee’s notes.
PaymentRequest
What a payee asks to be paid: one fresh key of its own for each note it expects, so the mint never sees two of its notes under one key.
Schedule
A mint’s rotation schedule, as its info publishes it.
ScheduledTransfer
A signed transfer and when to submit it.
Spacing
How far apart to submit the transfers of one payment when it takes more than one, in seconds. Each gap is drawn at random from this range.

Enums§

CheckError
A payment does not match its request.
LoadError
A saved store cannot be read.
NoteState
Where a note stands, as far as a MintView can tell.
PayError
A payment cannot be made.
RequestError
A payment request cannot be made.

Constants§

MAX_REQUESTED_NOTES
Most notes one payment request may ask for.
PAID_KEYS_KEPT
How many of the keys it paid last a holder remembers, so as to refuse paying one of them again: the notes of four requests of the most notes a request may ask for, or of hundreds of usual ones, for about 67 KiB of the saved store. A key paid by a transfer still outgoing, or by a payment the holder still keeps, is refused however long ago it was paid.
REFRESH_MARGIN_SECS
Seconds before a scheduled rotation by which the transfers that Holder::refresh and Holder::due_resends plan are due, or half the time left when that is shorter. A plan made at or past the rotation’s time, which the mint has not made yet as far as its info says, has them all due at once. A payer should settle a payment this long before the rotation that drops a note it spends.