Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
47ca6f5
AC initial
a1denvalu3 Aug 11, 2025
198864d
fix
a1denvalu3 Aug 11, 2025
e5ad221
identifiers
a1denvalu3 Aug 12, 2025
58a4a26
keys
a1denvalu3 Aug 12, 2025
3f78e34
remove unnecessary example
a1denvalu3 Aug 12, 2025
4fc1d0d
fix
a1denvalu3 Aug 12, 2025
2ecf06e
keysets + corrections
a1denvalu3 Aug 13, 2025
09bd766
prettier
a1denvalu3 Aug 13, 2025
ec35b31
swap request
a1denvalu3 Aug 13, 2025
63f797e
fix output commitments
a1denvalu3 Aug 13, 2025
b570992
finalize swap transaction
a1denvalu3 Aug 13, 2025
63964bb
fix typo
a1denvalu3 Aug 13, 2025
9e950e9
better readability.
a1denvalu3 Aug 13, 2025
02e7ce7
update
a1denvalu3 Aug 18, 2025
735459a
add AC-04 for minting
a1denvalu3 Aug 18, 2025
11c870c
prettier
a1denvalu3 Aug 18, 2025
a4b78d4
add nut-25 to ac04
a1denvalu3 Sep 11, 2025
67ad007
melt ac05
a1denvalu3 Sep 20, 2025
77df722
small fixes
a1denvalu3 Sep 20, 2025
67fb772
minor change
a1denvalu3 Sep 20, 2025
3c33070
AC06 bootstrap
a1denvalu3 Sep 21, 2025
bb06d56
AC-00/01/02/03: Restructure to mirror AC-06 style
a1denvalu3 Sep 22, 2025
e363526
AC-04/05: Restructure to mirror AC-06 style
a1denvalu3 Sep 22, 2025
970f840
AC-03/04/05: Enforce exactly 2 inputs and 2 outputs per request
a1denvalu3 Sep 22, 2025
c9bb999
fix a couple of errors
a1denvalu3 Sep 22, 2025
1719923
fix error
a1denvalu3 Sep 22, 2025
2a8bf55
AC-07: Deterministic derivation for tag and KVAC attribute blinding f…
a1denvalu3 Sep 22, 2025
501db98
AC-07: Switch to versioned derivation (align with NUT-13 v1/v2)
a1denvalu3 Sep 22, 2025
63ed873
AC-07: Update v2 KDF to HMAC-SHA256 (keep full 32-byte digest)
a1denvalu3 Sep 22, 2025
b644112
AC-07: Correct v2 KDF inputs per latest NUT-13 changes
a1denvalu3 Sep 22, 2025
0737d01
fixes
a1denvalu3 Sep 22, 2025
024eeb0
refinements
a1denvalu3 Sep 29, 2025
394634c
fixes
a1denvalu3 Sep 29, 2025
31758e7
fixed input fee
a1denvalu3 Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions protocol-extensions/anonymous-credentials/AC00.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# AC-00: Introduction and Core Models

`mandatory`

---

```
Status: Stable draft
Scope: Shared data models used by AC-01/02/03/04/05/06
Dependencies: cashu-kvac (KVAC), NUT-01/02
```

This document defines the core data models and normative constraints used by the Anonymous Credentials (AC) extensions.

## 1. Overview

[cashu-kvac][KVAC] is treated as a black-box implementation of the cryptographic operations for Anonymous Credentials. This specification does not reproduce cryptographic details; instead it defines how objects are constructed, serialized, and validated between wallet and Mint.

Terminology differences from the main specs:
- proof → note: the word “proof” refers to zero-knowledge proofs in this spec, so we use “note” for value-bearing objects.
- Mint (capital M) refers to the server component.
- JSON examples may embed symbolic types like `cashu_kvac::{Struct}` to indicate json-encoded KVAC types.

## 2. Keys

### 2.1 Mint Private Key

Rust (illustrative only; use deterministic derivation in production):

```rust
let scalars = (0..6).map(|_| cashu_kvac::secp::Scalar::random()).collect();
let mint_privkey = cashu_kvac::models::MintPrivateKey::from_scalars(&scalars).unwrap();
```

### 2.2 Mint Public Key

```rust
let mint_pubkey: cashu_kvac::models::MintPublicKey = mint_privkey.public_key.clone();
```

Constraints:
- Public keys MUST be compressed Secp256k1 format (33-byte, hex-encoded for wire) per AC-01/NUT-02.
- A keyset contains exactly one MintPublicKey (internally composed of two points, e.g., `Cw` and `I`).

## 3. Data Models

All hex strings are lowercase, no `0x` prefix, unless otherwise noted.

### 3.1 UnsignedNote

A client-created note not yet signed by the Mint.

```json
{
"keyset_id": "<hex>",
"amount": 1337,
"script": "<bytes-as-hex|null>",
"unit": "sat",
"tag": "<64-hex>",
"attributes": [
"<cashu_kvac::models::AmountAttribute>",
"<cashu_kvac::models::ScriptAttribute>"
]
}
```

Constraints:
- keyset_id: hex-encoded keyset identifier (see AC-02/NUT-02). **MUST** correspond to an active keyset when used to request Mint signatures.
- amount: non-negative integer. For zero-amount bootstrap notes, see [AC-06](AC06).
- script: optional [NUT-10](10) script, serialized as bytes and hex-encoded.
- unit: currency unit string (e.g., "sat", "msat"). All Inputs/Outputs within one request MUST share the same unit.
- tag: 32-byte random scalar serialized as 64-hex. MUST be unique per request and SHOULD be globally unique.
- attributes: tuple of AmountAttribute and ScriptAttribute constructed by the client.

Example attribute creation:

```rust
let amount_attr = cashu_kvac::models::AmountAttribute::new(1337, Some("deadbeefdeadbeefdeadbeefdeadbeef"));
let script_attr = cashu_kvac::models::ScriptAttribute::new(b"", Some("baadc0debaadc0debaadc0debaadc0de"));
```

### 3.2 Output

The Mint-visible view of an UnsignedNote.

```json
{
"id": "<keyset_id>",
"tag": "<64-hex>",
"commitments": ["<66-hex>", "<66-hex>"]
}
```

Constraints:
- id **MUST** equal `unsigned_note.keyset_id`.
- commitments[0] is the amount commitment; commitments[1] is the script commitment.
- The pair (id, tag) **MUST** be unique.

### 3.3 MAC

A Mint signature over Output commitments and tag.

```rust
let mac = MAC::generate(
&mint_privkey,
&output.commitments.0,
Some(&output.commitments.1),
Some(output.tag),
);
```

### 3.4 Note (signed)

```json
{
"keyset_id": "<hex>",
"amount": 1337,
"script": "<hex|null>",
"unit": "sat",
"tag": "<64-hex>",
"mac": "<66-hex>",
"attributes": [
"<cashu_kvac::AmountAttribute>",
"<cashu_kvac::ScriptAttribute>"
],
"issuance_proof": "<cashu_kvac::ZKP>"
}
```

Constraints:
- Fields mirror UnsignedNote.
- `issuance_proof` **MUST** verify against the MintPublicKey for `keyset_id`.

### 3.5 Input

Derived from a Note to spend it.

```json
{
"id": "<keyset_id>",
"script": "<hex|null>",
"randomized_commitments": "<cashu_kvac::RandomizedCommitments>",
"witness": "<hex|null>"
}
```

Construction:

```rust
let randomized_commitments = cashu_kvac::models::RandomizedCommitments::from_attributes_and_mac(
&note.attributes.0,
Some(&note.attributes.1),
note.tag,
note.mac,
true,
);
```

Constraints:
- id MUST equal `note.keyset_id`.
- witness/script handling per NUT-10/11/14.

## 4. Identifiers

- Mint identifies Inputs by `input.randomized_commitments.Cv` (nullifier base) and Outputs by `output.tag`.
- Clients identify Notes/UnsignedNotes by `tag`.

## 5. Validation and Interop Rules

- All amounts within a single request **MUST** be expressed in the same `unit`.
- Keyset ids used for Outputs **MUST** refer to active keysets (see [AC-01](AC01)/[AC-02](AC02)) at the time of issuance.
- Tags **MUST** be unique per request to avoid ambiguity and replay within the same batch.

## 6. Security and Policy Notes

- For deterministic wallets, `tag` and attribute blinding factors **SHOULD** be derived per [AC-07](07) to allow recovery. When deterministic mode is used, clients **MUST** ensure counters are allocated per-output and not reused.
- Tags and blinding factors **MUST** be generated with cryptographically secure randomness.
- Scripts ([NUT-10](10)) may reveal spending policy to the Mint via commitments; avoid embedding sensitive metadata.
- IssuanceProof verification **MUST** use the correct MintPublicKey from the stated keyset.
- Nullifier reuse is prevented by the Mint; clients **SHOULD** treat spent Notes as invalid after a successful swap.

[AC-01]: AC01.md
[AC-02]: AC02.md
[AC-03]: AC03.md
[AC-06]: AC06.md
[AC-07]: AC07.md
[KVAC]: https://github.com/lollerfirst/cashu-kvac.git
[01]: 01.md
[10]: 10.md
[11]: 11.md
[14]: 14.md
[02]: 02.md
87 changes: 87 additions & 0 deletions protocol-extensions/anonymous-credentials/AC01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# AC-01: Mint Public Key Exchange

`mandatory`

---
```
Status: Stable draft
Scope: Retrieval of active and specific Mint keysets
Dependencies: AC-00, AC-02, NUT-02
```

This document outlines how wallets obtain the Mint’s active keysets and optionally retrieve a specific keyset by id.

## 1. Endpoints

- GET `/v1/kvac/keys` — list of active keysets
- GET `/v1/kvac/keys/{keyset_id_hex}` — a specific keyset (active or inactive)

Content-Type: application/json

## 2. Responses

### 2.1 GET /v1/kvac/keys

```json
{
"keysets": [
{
"id": "<keyset_id_hex>",
"unit": "<currency_unit_str>",
"final_expiry": "<unix_epoch_int|null>",
"keys": {
"Cw": "<compressed_pubkey_hex>",
"I": "<compressed_pubkey_hex>"
}
}
]
}
```

Constraints:
- Returned keysets MUST be active (the Mint signs Outputs for them).
- Public keys MUST be compressed Secp256k1 format (33 bytes, hex).
- `id` MUST be recomputable from `keys` (see AC-02/NUT-02).
- `unit` defines the keyset’s unit (e.g., "sat"); clients MUST respect unit consistency across requests (see AC-00/AC-03).
- `final_expiry` MAY be provided; after expiry, notes from the keyset MAY be invalidated by Mint policy (see AC-02).

### 2.2 GET /v1/kvac/keys/{keyset_id_hex}

Returns the keyset regardless of active status:

```json
{
"keysets": [
{
"id": "<keyset_id_hex>",
"unit": "<currency_unit_str>",
"final_expiry": "<unix_epoch_int|null>",
"keys": {
"Cw": "<compressed_pubkey_hex>",
"I": "<compressed_pubkey_hex>"
}
}
]
}
```

## 3. Client Behavior

- SHOULD verify that `id` recomputes from the provided public keys (NUT-02).
- SHOULD fetch active keysets before issuing requests that involve Outputs (AC-03/AC-06).
- SHOULD cache and refresh active keysets periodically to handle rotation.

## 4. Errors

- 400 Bad Request — malformed `keyset_id`
- 404 Not Found — unknown `keyset_id`
- 5xx — server-side failures

## 5. Security and Policy Notes

- Key rotation: wallets SHOULD refresh the active set before constructing Outputs.
- Mint MAY rotate keys without prior notice; clients SHOULD handle changed active sets.

[AC-00]: AC00.md
[AC-02]: AC02.md
[02]: 02.md
62 changes: 62 additions & 0 deletions protocol-extensions/anonymous-credentials/AC02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# AC-02: Keysets and Fees

`mandatory`

---
```
Status: Stable draft
Scope: Listing all keysets and their fee policy
Dependencies: AC-00, AC-01, NUT-02
```

KVAC keysets largely follow NUT-02. This document specifies the endpoint and wire format for retrieving all keysets and fee parameters used by Anonymous Credentials extensions.

## 1. Endpoint

- GET `/v1/kvac/keysets`
- Content-Type: application/json

## 2. Response

```json
{
"keysets": [
{
"id": "<hex>",
"unit": "<str>",
"active": true,
"final_expiry": "<int|null>",
"input_fee_fixed": "<int|null>"
}
]
}
```

Field semantics:
- `id`: keyset identifier recomputable from public keys (see NUT-02).
- `unit`: currency unit string for this keyset.
- `active`: whether the Mint will sign new Outputs for this keyset.
- `final_expiry`: optional Unix epoch after which the keyset may be permanently deleted.
- `input_fee_fixed`: optional fixed fee applied to the inputs in swap flows. If omitted, treated as 0.

Constraints:
- Clients **MUST** treat `active=false` keysets as valid for Inputs but NOT for new Outputs.

## 3. Client Behavior

- SHOULD fetch `/v1/kvac/keysets` periodically to handle rotation and fee changes.
- MUST ensure Outputs reference active keysets only.
- SHOULD align units within a single request (see AC-00/AC-03).

## 4. Errors

- 5xx — server-side failures

## 5. Security and Policy Notes

- Keyset rotation and expiries can invalidate issuance; wallets SHOULD re-fetch before constructing requests that include Outputs.
- Fee policy changes are Mint-defined; clients SHOULD not assume stability without refresh.

[AC-00]: AC00.md
[AC-03]: AC03.md
[02]: 02.md
Loading
Loading