@openzeppelin/miden-multisig-client 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -21
- package/dist/client.d.ts +39 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +77 -0
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/lookupAuth.d.ts.map +1 -1
- package/dist/lookupAuth.js +2 -20
- package/dist/lookupAuth.js.map +1 -1
- package/dist/multisig/authArgErrors.d.ts +27 -1
- package/dist/multisig/authArgErrors.d.ts.map +1 -1
- package/dist/multisig/authArgErrors.js +33 -0
- package/dist/multisig/authArgErrors.js.map +1 -1
- package/dist/multisig.d.ts +77 -11
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +247 -181
- package/dist/multisig.js.map +1 -1
- package/dist/proposal/metadata.d.ts.map +1 -1
- package/dist/proposal/metadata.js +2 -0
- package/dist/proposal/metadata.js.map +1 -1
- package/dist/recovery/transportDrain.d.ts +38 -30
- package/dist/recovery/transportDrain.d.ts.map +1 -1
- package/dist/recovery/transportDrain.js +113 -113
- package/dist/recovery/transportDrain.js.map +1 -1
- package/dist/session/grant.d.ts +40 -0
- package/dist/session/grant.d.ts.map +1 -0
- package/dist/session/grant.js +125 -0
- package/dist/session/grant.js.map +1 -0
- package/dist/session/key.d.ts +103 -0
- package/dist/session/key.d.ts.map +1 -0
- package/dist/session/key.js +160 -0
- package/dist/session/key.js.map +1 -0
- package/dist/session/manager.d.ts +76 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +187 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/signers/ecdsa.d.ts +5 -1
- package/dist/signers/ecdsa.d.ts.map +1 -1
- package/dist/signers/ecdsa.js +9 -0
- package/dist/signers/ecdsa.js.map +1 -1
- package/dist/signers/falcon.d.ts +5 -1
- package/dist/signers/falcon.d.ts.map +1 -1
- package/dist/signers/falcon.js +9 -0
- package/dist/signers/falcon.js.map +1 -1
- package/dist/signers/ledger.d.ts +9 -1
- package/dist/signers/ledger.d.ts.map +1 -1
- package/dist/signers/ledger.js +13 -1
- package/dist/signers/ledger.js.map +1 -1
- package/dist/signers/miden-wallet.d.ts +13 -1
- package/dist/signers/miden-wallet.d.ts.map +1 -1
- package/dist/signers/miden-wallet.js +25 -0
- package/dist/signers/miden-wallet.js.map +1 -1
- package/dist/signers/para.d.ts +5 -1
- package/dist/signers/para.d.ts.map +1 -1
- package/dist/signers/para.js +9 -0
- package/dist/signers/para.js.map +1 -1
- package/dist/transaction/authArgs.d.ts +2 -2
- package/dist/transaction/authArgs.js +2 -2
- package/dist/transaction/options.d.ts +2 -2
- package/dist/transaction/summary.d.ts +17 -61
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js +49 -95
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction.d.ts +1 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +1 -1
- package/dist/transaction.js.map +1 -1
- package/dist/types/proposal.d.ts +11 -5
- package/dist/types/proposal.d.ts.map +1 -1
- package/dist/types/proposal.js.map +1 -1
- package/dist/utils/eip712.d.ts +26 -69
- package/dist/utils/eip712.d.ts.map +1 -1
- package/dist/utils/eip712.js +101 -8
- package/dist/utils/eip712.js.map +1 -1
- package/dist/utils/felt.d.ts +7 -1
- package/dist/utils/felt.d.ts.map +1 -1
- package/dist/utils/felt.js +18 -1
- package/dist/utils/felt.js.map +1 -1
- package/package.json +4 -4
- package/src/client.ts +95 -0
- package/src/index.ts +27 -6
- package/src/lookupAuth.ts +3 -22
- package/src/multisig/authArgErrors.ts +42 -1
- package/src/multisig.ts +318 -207
- package/src/proposal/metadata.ts +2 -0
- package/src/recovery/transportDrain.ts +118 -124
- package/src/session/grant.ts +143 -0
- package/src/session/key.ts +215 -0
- package/src/session/manager.ts +273 -0
- package/src/signers/ecdsa.ts +12 -1
- package/src/signers/falcon.ts +12 -1
- package/src/signers/ledger.ts +19 -2
- package/src/signers/miden-wallet.ts +29 -1
- package/src/signers/para.ts +12 -1
- package/src/transaction/authArgs.ts +2 -2
- package/src/transaction/options.ts +2 -2
- package/src/transaction/summary.ts +62 -110
- package/src/transaction.ts +1 -6
- package/src/types/proposal.ts +11 -5
- package/src/utils/eip712.ts +130 -10
- package/src/utils/felt.ts +19 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recovery primitives for restoring note state after device loss.
|
|
3
3
|
*
|
|
4
|
-
* After recovery on a fresh device
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* After recovery on a fresh device, or after adding a recovered account to a
|
|
5
|
+
* shared store, the account's note tag has no note-transport cursor yet.
|
|
6
|
+
* Since miden-client 0.17.1 the transport keeps one cursor per tag and scans
|
|
7
|
+
* a tag without one from the first retained note, but each sync fetches a
|
|
8
|
+
* bounded number of pages. The drain here keeps fetching until a fetch moves
|
|
9
|
+
* no tag's cursor, so a recovery flow does not have to wait for later
|
|
10
|
+
* background syncs.
|
|
8
11
|
*/
|
|
9
12
|
import { errorMessage, isLikelyNetworkError } from '../connectivity.js';
|
|
10
13
|
import { isTransientRpcError } from '../rpc/errors.js';
|
|
@@ -42,18 +45,60 @@ export const NODE_RPC_FRAGMENT = 'grpc request failed';
|
|
|
42
45
|
*/
|
|
43
46
|
export const NODE_CONNECT_FRAGMENT = 'failed to connect to the Miden node';
|
|
44
47
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
48
|
+
* Mirror of the Rust SDK's `MAX_DRAIN_PASSES`: upper bound on transport
|
|
49
|
+
* fetches one drain runs. Each fetch reads up to 32 pages per request group
|
|
50
|
+
* of up to 128 tags; a backlog whose cursors still move after this many
|
|
51
|
+
* fetches is reported as a retryable failure, and the stored per-tag cursors
|
|
52
|
+
* let the next drain or sync continue from there.
|
|
53
|
+
*/
|
|
54
|
+
const MAX_DRAIN_PASSES = 64;
|
|
55
|
+
/**
|
|
56
|
+
* miden-client's `NOTE_TRANSPORT_CURSORS_KEY`, a setting in its client-owned
|
|
57
|
+
* scope (`SettingScope::Client` = 0), which the SDK's `settings` API does not
|
|
58
|
+
* reach. Exported for the drift-guard test, which pins the key and the
|
|
59
|
+
* store layout below against the shipped SDK.
|
|
49
60
|
*/
|
|
50
|
-
export const
|
|
61
|
+
export const NOTE_TRANSPORT_CURSORS_KEY = 'note_transport_cursors';
|
|
62
|
+
const CLIENT_SETTING_SCOPE = 0;
|
|
63
|
+
/** The SDK's IndexedDB table for settings, keyed by `[scope, key]`. */
|
|
64
|
+
const SETTINGS_TABLE = 'settings';
|
|
51
65
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
66
|
+
* The stored per-tag note-transport cursors, as the raw bytes miden-client
|
|
67
|
+
* keeps, read from the client's IndexedDB store. Compared, never decoded: a
|
|
68
|
+
* fetch that leaves them unchanged found every tracked tag's history
|
|
69
|
+
* exhausted. Throws when the store cannot be read, which is a local-store
|
|
70
|
+
* failure the caller must see.
|
|
55
71
|
*/
|
|
56
|
-
|
|
72
|
+
export async function readTransportCursors(midenClient) {
|
|
73
|
+
const name = await midenClient.storeIdentifier();
|
|
74
|
+
const db = await new Promise((resolve, reject) => {
|
|
75
|
+
const request = indexedDB.open(name);
|
|
76
|
+
request.onsuccess = () => resolve(request.result);
|
|
77
|
+
request.onerror = () => reject(request.error);
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
if (!db.objectStoreNames.contains(SETTINGS_TABLE)) {
|
|
81
|
+
throw new Error(`storage error: the store ${name} has no ${SETTINGS_TABLE} table`);
|
|
82
|
+
}
|
|
83
|
+
const row = await new Promise((resolve, reject) => {
|
|
84
|
+
const request = db
|
|
85
|
+
.transaction(SETTINGS_TABLE, 'readonly')
|
|
86
|
+
.objectStore(SETTINGS_TABLE)
|
|
87
|
+
.get([CLIENT_SETTING_SCOPE, NOTE_TRANSPORT_CURSORS_KEY]);
|
|
88
|
+
request.onsuccess = () => resolve(request.result);
|
|
89
|
+
request.onerror = () => reject(request.error);
|
|
90
|
+
});
|
|
91
|
+
return row?.value ?? null;
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
db.close();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function sameBytes(a, b) {
|
|
98
|
+
if (a === null || b === null)
|
|
99
|
+
return a === b;
|
|
100
|
+
return a.length === b.length && a.every((byte, index) => byte === b[index]);
|
|
101
|
+
}
|
|
57
102
|
/**
|
|
58
103
|
* Mirror of the Rust SDK's `CONNECT_PERMANENT_SIGNALS`: connection-failure
|
|
59
104
|
* wording a retry cannot fix (misconfigured endpoint, TLS/certificate
|
|
@@ -106,10 +151,8 @@ function classifyDrainFailure(err) {
|
|
|
106
151
|
if (lower.includes(TRANSPORT_DISABLED_FRAGMENT)) {
|
|
107
152
|
return { status: 'unavailable', retryable: false, reason, scanned: false };
|
|
108
153
|
}
|
|
109
|
-
// The upstream convergence guard
|
|
110
|
-
//
|
|
111
|
-
// honest backlog. Retryable in the sense that a rerun is safe (imports are
|
|
112
|
-
// idempotent) and succeeds once the server recovers.
|
|
154
|
+
// The upstream convergence guard of miden-client before 0.17.1, which no
|
|
155
|
+
// longer raises it. A rerun is safe (imports are idempotent).
|
|
113
156
|
if (lower.includes(PAGINATION_GUARD_FRAGMENT)) {
|
|
114
157
|
return { status: 'failed', retryable: true, reason, scanned: true };
|
|
115
158
|
}
|
|
@@ -143,42 +186,21 @@ function classifyDrainFailure(err) {
|
|
|
143
186
|
return { status: 'failed', retryable: false, reason, scanned: true };
|
|
144
187
|
}
|
|
145
188
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* attempt already handled, which is idempotent. A failed restoring write
|
|
153
|
-
* throws: the store is left with its bookkeeping cleared and subsequent
|
|
154
|
-
* normal syncs may re-drain (and re-fail on) old history — a local-store
|
|
155
|
-
* environment failure the caller must not fold into a transport report.
|
|
156
|
-
*/
|
|
157
|
-
async function restoreCoveredTags(midenClient, snapshot, drainReason) {
|
|
158
|
-
if (snapshot === null || snapshot === undefined)
|
|
159
|
-
return;
|
|
160
|
-
try {
|
|
161
|
-
await midenClient.settings.set(NOTE_TRANSPORT_COVERED_TAGS_KEY, snapshot);
|
|
162
|
-
}
|
|
163
|
-
catch (restoreError) {
|
|
164
|
-
throw new Error(`the transport drain failed (${drainReason}) and restoring the covered-tags bookkeeping also failed (${errorMessage(restoreError)}); subsequent syncs may re-drain old transport history`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Rescans the full private-note transport backlog for every tracked note tag
|
|
169
|
-
* and imports what it finds, regardless of the stored transport
|
|
170
|
-
* cursor. Counterpart of `MultisigClient::drain_private_note_backlog`
|
|
171
|
-
* in the Rust SDK; the WASM boundary exposes only error message text, so
|
|
172
|
-
* failure classification here is message-based, keyed on the upstream
|
|
173
|
-
* Display prefixes pinned by the drift-guard test.
|
|
189
|
+
* Fetches the private-note transport backlog for every tracked note tag and
|
|
190
|
+
* imports what it finds. Counterpart of
|
|
191
|
+
* `MultisigClient::drain_private_note_backlog` in the Rust SDK; the WASM
|
|
192
|
+
* boundary exposes only error message text, so failure classification here
|
|
193
|
+
* is message-based, keyed on the upstream Display prefixes pinned by the
|
|
194
|
+
* drift-guard test.
|
|
174
195
|
*
|
|
175
196
|
* Use after account recovery, passing the **same** `MidenClient` instance
|
|
176
|
-
* that was injected into `MultisigClient
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
197
|
+
* that was injected into `MultisigClient`. The recovered account must already
|
|
198
|
+
* be in the store so its note tag is tracked (`MultisigClient.load` does
|
|
199
|
+
* this). Its tag has no transport cursor yet, so the transport serves it from
|
|
200
|
+
* the first retained note. The drain runs transport fetches until one moves
|
|
201
|
+
* no tag's cursor, which only an exhausted backlog leaves unchanged: a fetch
|
|
202
|
+
* whose pages were all duplicates or screened out still advances it. The
|
|
203
|
+
* drain is idempotent: imports dedupe, and per-tag cursors only move forward.
|
|
182
204
|
*
|
|
183
205
|
* Transport recovery is bounded by the transport service's retention:
|
|
184
206
|
* senders may bypass the transport entirely and relayed blobs are pruned
|
|
@@ -186,77 +208,55 @@ async function restoreCoveredTags(midenClient, snapshot, drainReason) {
|
|
|
186
208
|
* backup. Transport-disabled and transport-unreachable outcomes are reported
|
|
187
209
|
* in the {@link TransportRecoveryReport} rather than thrown; this function
|
|
188
210
|
* only throws when the local store itself fails.
|
|
189
|
-
*
|
|
190
|
-
* The rescan runs as many transport syncs as the upstream per-sync tag
|
|
191
|
-
* backfill cap requires to cover every tracked tag, and a failed drain
|
|
192
|
-
* restores the pre-drain covered-tags bookkeeping so normal sync keeps
|
|
193
|
-
* working exactly as it did before the attempt.
|
|
194
211
|
*/
|
|
195
212
|
export async function drainPrivateNoteBacklog(midenClient) {
|
|
196
|
-
const
|
|
213
|
+
const noteCount = async () => (await midenClient.notes.list()).length;
|
|
214
|
+
const before = await noteCount();
|
|
197
215
|
// Records are never removed by a drain, so the length delta is the count
|
|
198
216
|
// of newly imported records. Caveat: it is a store delta — notes imported
|
|
199
217
|
// by concurrent activity on the same store (a background sync, another
|
|
200
218
|
// tab) during the drain are attributed to it.
|
|
201
|
-
const importedSince = async () => Math.max((await
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
// The incremental fetch doubles as the transport probe: miden-sdk 0.16's
|
|
212
|
-
// syncNoteTransport silently no-ops when the transport is disabled, but
|
|
213
|
-
// this drain must report `unavailable`, and fetchPrivate still throws
|
|
214
|
-
// the upstream disabled error.
|
|
215
|
-
await midenClient.notes.fetchPrivate();
|
|
216
|
-
// miden-sdk 0.16 replaced the explicit full drain with covered-tag
|
|
217
|
-
// bookkeeping inside syncNoteTransport: every tag not yet marked covered
|
|
218
|
-
// is drained from the start with a local cursor (the global cursor is
|
|
219
|
-
// never regressed), then the steady-state fetch runs. Clearing the
|
|
220
|
-
// covered-tags marker first forces that full per-tag re-drain — exactly
|
|
221
|
-
// the recovery semantic this primitive promises. Imports dedupe, so
|
|
222
|
-
// re-draining already-seen history is harmless.
|
|
223
|
-
await midenClient.settings.remove(NOTE_TRANSPORT_COVERED_TAGS_KEY);
|
|
224
|
-
cleared = true;
|
|
225
|
-
// Upstream backfills at most `MAX_BACKFILL_TAGS_PER_SYNC` uncovered tags
|
|
226
|
-
// per sync, so run enough passes to cover every tracked tag before
|
|
227
|
-
// reporting the backlog fully scanned.
|
|
228
|
-
const tagCount = (await midenClient.tags.list()).length;
|
|
229
|
-
const passes = Math.max(1, Math.ceil(tagCount / MAX_BACKFILL_TAGS_PER_SYNC));
|
|
230
|
-
for (let pass = 0; pass < passes; pass += 1) {
|
|
231
|
-
await midenClient.syncNoteTransport();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
catch (err) {
|
|
235
|
-
// A broken local store is an environment failure, not a transport
|
|
236
|
-
// outcome: the whole recovery flow needs to know, so it propagates
|
|
237
|
-
// (matching the Rust `StoreError` branch) instead of being folded into
|
|
238
|
-
// the report.
|
|
239
|
-
if (isLocalStoreError(err)) {
|
|
240
|
-
throw err;
|
|
219
|
+
const importedSince = async () => Math.max((await noteCount()) - before, 0);
|
|
220
|
+
let last = await readTransportCursors(midenClient);
|
|
221
|
+
for (let pass = 0; pass < MAX_DRAIN_PASSES; pass += 1) {
|
|
222
|
+
try {
|
|
223
|
+
// fetchPrivate throws the upstream disabled error when no transport is
|
|
224
|
+
// configured, where syncNoteTransport silently no-ops; this drain must
|
|
225
|
+
// report `unavailable`. A failed fetch still imports the pages fetched
|
|
226
|
+
// before it.
|
|
227
|
+
await midenClient.notes.fetchPrivate();
|
|
241
228
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
229
|
+
catch (err) {
|
|
230
|
+
// A broken local store is an environment failure, not a transport
|
|
231
|
+
// outcome: the whole recovery flow needs to know, so it propagates
|
|
232
|
+
// (matching the Rust `StoreError` branch) instead of being folded into
|
|
233
|
+
// the report.
|
|
234
|
+
if (isLocalStoreError(err)) {
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
const { status, retryable, reason, scanned } = classifyDrainFailure(err);
|
|
238
|
+
// A disabled transport throws before fetching anything, so skip the
|
|
239
|
+
// re-count entirely.
|
|
240
|
+
const imported = scanned ? await importedSince() : 0;
|
|
241
|
+
if (status === 'unavailable' && imported > 0) {
|
|
242
|
+
// `unavailable` promises "nothing was imported"; pages imported before
|
|
243
|
+
// the failure make it an interrupted drain, so report it as a failure,
|
|
244
|
+
// keeping the classified retryability, like the Rust twin.
|
|
245
|
+
return { status: 'failed', imported, retryable, reason };
|
|
246
|
+
}
|
|
247
|
+
return { status, imported, retryable, reason };
|
|
245
248
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// re-count entirely.
|
|
250
|
-
const imported = scanned ? await importedSince() : 0;
|
|
251
|
-
if (status === 'unavailable' && imported > 0) {
|
|
252
|
-
// `unavailable` promises "nothing was imported"; a connection lost
|
|
253
|
-
// mid-drain after partial progress is an interrupted drain, so report
|
|
254
|
-
// it as a failure — keeping the classified retryability, like the
|
|
255
|
-
// Rust twin.
|
|
256
|
-
return { status: 'failed', imported, retryable, reason };
|
|
249
|
+
const cursors = await readTransportCursors(midenClient);
|
|
250
|
+
if (sameBytes(cursors, last)) {
|
|
251
|
+
return { status: 'completed', imported: await importedSince(), retryable: false };
|
|
257
252
|
}
|
|
258
|
-
|
|
253
|
+
last = cursors;
|
|
259
254
|
}
|
|
260
|
-
return {
|
|
255
|
+
return {
|
|
256
|
+
status: 'failed',
|
|
257
|
+
imported: await importedSince(),
|
|
258
|
+
retryable: true,
|
|
259
|
+
reason: `the transport backlog was not exhausted after ${MAX_DRAIN_PASSES} fetches; rerun the drain to continue from the stored cursors`,
|
|
260
|
+
};
|
|
261
261
|
}
|
|
262
262
|
//# sourceMappingURL=transportDrain.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transportDrain.js","sourceRoot":"","sources":["../../src/recovery/transportDrain.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"transportDrain.js","sourceRoot":"","sources":["../../src/recovery/transportDrain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SessionGrantFields } from '@openzeppelin/guardian-client';
|
|
2
|
+
import { Word } from '@miden-sdk/miden-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Session grant, logout and revoke-all digests (issue #219). MUST produce
|
|
5
|
+
* byte-identical digests to `crates/shared/src/session_grant.rs`; parity is
|
|
6
|
+
* verified against `crates/shared/tests/fixtures/session_grant_vectors.json`.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The scope every v1 grant states, shown by the wallet: the delegated signer
|
|
10
|
+
* acts for every account its wallet key cosigns on this Guardian, including
|
|
11
|
+
* accounts the key is added to later, until the grant expires.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SESSION_GRANT_SCOPE = "Every account this signer cosigns on this Guardian, now or later, until this grant expires";
|
|
14
|
+
export declare function sessionDomainTag(): Word;
|
|
15
|
+
export declare function sessionLogoutDomainTag(): Word;
|
|
16
|
+
export declare function sessionRevokeAllDomainTag(): Word;
|
|
17
|
+
/** The canonical readable expiry, `YYYY-MM-DD HH:MM:SS UTC`, derived from `expiresAt`. */
|
|
18
|
+
export declare function formatUtcSeconds(unixSeconds: number): string;
|
|
19
|
+
/**
|
|
20
|
+
* Digest signed by Falcon and raw ECDSA wallets: the domain tag followed by
|
|
21
|
+
* every grant field in the clear, in the order the wallet displays them.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sessionGrantDigest(grant: SessionGrantFields): Word;
|
|
24
|
+
/**
|
|
25
|
+
* The grant as the label/value pairs a wallet shows, in signing order. Raw
|
|
26
|
+
* (Falcon or raw ECDSA) wallets display only a hash, so show these to the
|
|
27
|
+
* user before asking such a wallet to sign.
|
|
28
|
+
*/
|
|
29
|
+
export declare function describeSessionGrant(grant: SessionGrantFields): Array<{
|
|
30
|
+
label: string;
|
|
31
|
+
value: string;
|
|
32
|
+
}>;
|
|
33
|
+
/** Digest of the account-less, session-key-signed `POST /session/logout`. */
|
|
34
|
+
export declare function sessionLogoutDigest(sessionPublicKey: string, timestampMs: number): Word;
|
|
35
|
+
/**
|
|
36
|
+
* Digest of the account-less, wallet-signed `POST /session/revoke-all`, which
|
|
37
|
+
* ends every session of `signerCommitment` on the Guardian.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sessionRevokeAllDigest(signerCommitment: string, timestampMs: number): Word;
|
|
40
|
+
//# sourceMappingURL=grant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../../src/session/grant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAA2B,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAIrE;;;;GAIG;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,+FAC8D,CAAC;AAU/F,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAuBD,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG5D;AAUD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAkBlE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,kBAAkB,GACxB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAWzC;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAOvF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAO1F"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Felt, FeltArray, Rpo256, Word } from '@miden-sdk/miden-sdk';
|
|
2
|
+
import { domainTagWord, feltFromU64Reduced } from '../utils/felt.js';
|
|
3
|
+
import { hexToBytes } from '../utils/encoding.js';
|
|
4
|
+
/**
|
|
5
|
+
* Session grant, logout and revoke-all digests (issue #219). MUST produce
|
|
6
|
+
* byte-identical digests to `crates/shared/src/session_grant.rs`; parity is
|
|
7
|
+
* verified against `crates/shared/tests/fixtures/session_grant_vectors.json`.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The scope every v1 grant states, shown by the wallet: the delegated signer
|
|
11
|
+
* acts for every account its wallet key cosigns on this Guardian, including
|
|
12
|
+
* accounts the key is added to later, until the grant expires.
|
|
13
|
+
*/
|
|
14
|
+
export const SESSION_GRANT_SCOPE = 'Every account this signer cosigns on this Guardian, now or later, until this grant expires';
|
|
15
|
+
const DOMAIN_TAG_BYTES = new TextEncoder().encode('guardian.session.v1');
|
|
16
|
+
const LOGOUT_DOMAIN_TAG_BYTES = new TextEncoder().encode('guardian.session.logout.v1');
|
|
17
|
+
const REVOKE_ALL_DOMAIN_TAG_BYTES = new TextEncoder().encode('guardian.session.revoke_all.v1');
|
|
18
|
+
let cachedDomainTag = null;
|
|
19
|
+
let cachedLogoutDomainTag = null;
|
|
20
|
+
let cachedRevokeAllDomainTag = null;
|
|
21
|
+
export function sessionDomainTag() {
|
|
22
|
+
cachedDomainTag ??= domainTagWord(DOMAIN_TAG_BYTES);
|
|
23
|
+
return cachedDomainTag;
|
|
24
|
+
}
|
|
25
|
+
export function sessionLogoutDomainTag() {
|
|
26
|
+
cachedLogoutDomainTag ??= domainTagWord(LOGOUT_DOMAIN_TAG_BYTES);
|
|
27
|
+
return cachedLogoutDomainTag;
|
|
28
|
+
}
|
|
29
|
+
export function sessionRevokeAllDomainTag() {
|
|
30
|
+
cachedRevokeAllDomainTag ??= domainTagWord(REVOKE_ALL_DOMAIN_TAG_BYTES);
|
|
31
|
+
return cachedRevokeAllDomainTag;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `[len, u32_le_chunk...]` felts: injective for arbitrary bytes, including
|
|
35
|
+
* public keys. Mirrors `felts_from_bytes` in the Rust crate.
|
|
36
|
+
*/
|
|
37
|
+
function feltsFromBytes(bytes) {
|
|
38
|
+
const felts = [new Felt(BigInt(bytes.length))];
|
|
39
|
+
for (let offset = 0; offset < bytes.length; offset += 4) {
|
|
40
|
+
let value = 0;
|
|
41
|
+
for (let i = 0; i < 4; i += 1) {
|
|
42
|
+
const byte = offset + i < bytes.length ? bytes[offset + i] : 0;
|
|
43
|
+
value += byte * 2 ** (8 * i);
|
|
44
|
+
}
|
|
45
|
+
felts.push(new Felt(BigInt(value)));
|
|
46
|
+
}
|
|
47
|
+
return felts;
|
|
48
|
+
}
|
|
49
|
+
function hashBytes(bytes) {
|
|
50
|
+
return Rpo256.hashElements(new FeltArray(feltsFromBytes(bytes)));
|
|
51
|
+
}
|
|
52
|
+
/** The canonical readable expiry, `YYYY-MM-DD HH:MM:SS UTC`, derived from `expiresAt`. */
|
|
53
|
+
export function formatUtcSeconds(unixSeconds) {
|
|
54
|
+
const iso = new Date(unixSeconds * 1000).toISOString();
|
|
55
|
+
return `${iso.slice(0, 10)} ${iso.slice(11, 19)} UTC`;
|
|
56
|
+
}
|
|
57
|
+
function sessionPublicKeyBytes(hex) {
|
|
58
|
+
const bytes = hexToBytes(hex);
|
|
59
|
+
if (bytes.length !== 33 || (bytes[0] !== 0x02 && bytes[0] !== 0x03)) {
|
|
60
|
+
throw new Error('Session public key must be a 33-byte SEC1 compressed P-256 point');
|
|
61
|
+
}
|
|
62
|
+
return bytes;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Digest signed by Falcon and raw ECDSA wallets: the domain tag followed by
|
|
66
|
+
* every grant field in the clear, in the order the wallet displays them.
|
|
67
|
+
*/
|
|
68
|
+
export function sessionGrantDigest(grant) {
|
|
69
|
+
if (grant.expiresAt <= grant.issuedAt) {
|
|
70
|
+
throw new Error('Session grant expiresAt must be after issuedAt');
|
|
71
|
+
}
|
|
72
|
+
const encoder = new TextEncoder();
|
|
73
|
+
const felts = [
|
|
74
|
+
...sessionDomainTag().toFelts(),
|
|
75
|
+
...Word.fromHex(grant.signerCommitment).toFelts(),
|
|
76
|
+
...feltsFromBytes(sessionPublicKeyBytes(grant.sessionPublicKey)),
|
|
77
|
+
...hashBytes(encoder.encode(grant.origin)).toFelts(),
|
|
78
|
+
feltFromU64Reduced(BigInt(grant.issuedAt)),
|
|
79
|
+
feltFromU64Reduced(BigInt(grant.expiresAt)),
|
|
80
|
+
...hashBytes(encoder.encode(formatUtcSeconds(grant.expiresAt))).toFelts(),
|
|
81
|
+
...hashBytes(encoder.encode(SESSION_GRANT_SCOPE)).toFelts(),
|
|
82
|
+
...Word.fromHex(grant.guardianCommitment).toFelts(),
|
|
83
|
+
...hashBytes(encoder.encode(grant.network)).toFelts(),
|
|
84
|
+
];
|
|
85
|
+
return Rpo256.hashElements(new FeltArray(felts));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The grant as the label/value pairs a wallet shows, in signing order. Raw
|
|
89
|
+
* (Falcon or raw ECDSA) wallets display only a hash, so show these to the
|
|
90
|
+
* user before asking such a wallet to sign.
|
|
91
|
+
*/
|
|
92
|
+
export function describeSessionGrant(grant) {
|
|
93
|
+
return [
|
|
94
|
+
{ label: 'Signer', value: grant.signerCommitment },
|
|
95
|
+
{ label: 'Session key', value: grant.sessionPublicKey },
|
|
96
|
+
{ label: 'Website', value: grant.origin || '(none: outside a browser)' },
|
|
97
|
+
{ label: 'Valid from', value: formatUtcSeconds(grant.issuedAt) },
|
|
98
|
+
{ label: 'Expires', value: formatUtcSeconds(grant.expiresAt) },
|
|
99
|
+
{ label: 'Scope', value: SESSION_GRANT_SCOPE },
|
|
100
|
+
{ label: 'Guardian key', value: grant.guardianCommitment },
|
|
101
|
+
{ label: 'Network', value: grant.network },
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
/** Digest of the account-less, session-key-signed `POST /session/logout`. */
|
|
105
|
+
export function sessionLogoutDigest(sessionPublicKey, timestampMs) {
|
|
106
|
+
const felts = [
|
|
107
|
+
...sessionLogoutDomainTag().toFelts(),
|
|
108
|
+
feltFromU64Reduced(BigInt(timestampMs)),
|
|
109
|
+
...feltsFromBytes(sessionPublicKeyBytes(sessionPublicKey)),
|
|
110
|
+
];
|
|
111
|
+
return Rpo256.hashElements(new FeltArray(felts));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Digest of the account-less, wallet-signed `POST /session/revoke-all`, which
|
|
115
|
+
* ends every session of `signerCommitment` on the Guardian.
|
|
116
|
+
*/
|
|
117
|
+
export function sessionRevokeAllDigest(signerCommitment, timestampMs) {
|
|
118
|
+
const felts = [
|
|
119
|
+
...sessionRevokeAllDomainTag().toFelts(),
|
|
120
|
+
feltFromU64Reduced(BigInt(timestampMs)),
|
|
121
|
+
...Word.fromHex(signerCommitment).toFelts(),
|
|
122
|
+
];
|
|
123
|
+
return Rpo256.hashElements(new FeltArray(felts));
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=grant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant.js","sourceRoot":"","sources":["../../src/session/grant.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { RequestAuthPayload, SessionEndReason, SessionRequestSigner } from '@openzeppelin/guardian-client';
|
|
2
|
+
import type { Word } from '@miden-sdk/miden-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* A P-256 session key whose private half is a non-extractable WebCrypto key:
|
|
5
|
+
* page scripts can use it to sign while it is loaded, but can never read or
|
|
6
|
+
* export it.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WebCryptoSessionKey {
|
|
9
|
+
readonly privateKey: CryptoKey;
|
|
10
|
+
/** Hex SEC1-compressed public key. */
|
|
11
|
+
readonly publicKey: string;
|
|
12
|
+
constructor(privateKey: CryptoKey,
|
|
13
|
+
/** Hex SEC1-compressed public key. */
|
|
14
|
+
publicKey: string);
|
|
15
|
+
static generate(): Promise<WebCryptoSessionKey>;
|
|
16
|
+
/** Sign the 32 bytes of a word: ECDSA P-256 over SHA-256, `r || s` hex. */
|
|
17
|
+
signWord(word: Word): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
export interface GuardianSessionOptions {
|
|
20
|
+
/** The store keeping this session's key; its record is removed when the session ends. */
|
|
21
|
+
store?: SessionKeyStore;
|
|
22
|
+
/**
|
|
23
|
+
* Called once when the Guardian client stops using the session. Requests
|
|
24
|
+
* are then signed by the wallet again. On `expired`, `revoked` or
|
|
25
|
+
* `rejected`, start a new session to stop the prompts; `logout` means the
|
|
26
|
+
* app ended it itself (logout, revoke-all or a replacement session).
|
|
27
|
+
*/
|
|
28
|
+
onEnded?: (reason: SessionEndReason, session: GuardianSession) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Signs Guardian requests for a wallet after its session grant was
|
|
32
|
+
* registered. Plug into `GuardianHttpClient.setSession`.
|
|
33
|
+
*/
|
|
34
|
+
export declare class GuardianSession implements SessionRequestSigner {
|
|
35
|
+
private readonly key;
|
|
36
|
+
/** Commitment of the wallet that signed the grant. */
|
|
37
|
+
readonly signerCommitment: string;
|
|
38
|
+
/** Unix seconds. */
|
|
39
|
+
readonly expiresAt: number;
|
|
40
|
+
/** Where a `SessionKeyStore` keeps this session's key. */
|
|
41
|
+
readonly storeId: string;
|
|
42
|
+
private readonly options;
|
|
43
|
+
constructor(key: WebCryptoSessionKey,
|
|
44
|
+
/** Commitment of the wallet that signed the grant. */
|
|
45
|
+
signerCommitment: string,
|
|
46
|
+
/** Unix seconds. */
|
|
47
|
+
expiresAt: number,
|
|
48
|
+
/** Where a `SessionKeyStore` keeps this session's key. */
|
|
49
|
+
storeId: string, options?: GuardianSessionOptions);
|
|
50
|
+
get publicKey(): string;
|
|
51
|
+
/**
|
|
52
|
+
* Forgets the stored key (best effort: a dead key left behind fails its
|
|
53
|
+
* first request after a reload and ends then) and tells the app.
|
|
54
|
+
*/
|
|
55
|
+
onEnded(reason: SessionEndReason): void;
|
|
56
|
+
signRequest(accountId: string, timestamp: number, requestPayload: RequestAuthPayload): Promise<string>;
|
|
57
|
+
signLogout(timestampMs: number): Promise<string>;
|
|
58
|
+
}
|
|
59
|
+
/** A session key persisted across page loads. */
|
|
60
|
+
export interface StoredSessionKey {
|
|
61
|
+
/** `<guardian key commitment>:<signer commitment>`. */
|
|
62
|
+
id: string;
|
|
63
|
+
privateKey: CryptoKey;
|
|
64
|
+
publicKey: string;
|
|
65
|
+
/** Unix seconds. */
|
|
66
|
+
expiresAt: number;
|
|
67
|
+
}
|
|
68
|
+
export interface SessionKeyStore {
|
|
69
|
+
load(id: string): Promise<StoredSessionKey | null>;
|
|
70
|
+
save(record: StoredSessionKey): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Remove the record `id` only if it still holds `publicKey`, as one atomic
|
|
73
|
+
* step, so a newer key another tab saved for the same wallet and Guardian is
|
|
74
|
+
* never deleted.
|
|
75
|
+
*/
|
|
76
|
+
remove(id: string, publicKey: string): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Remove the stored key of `session`, unless the record already holds the key
|
|
80
|
+
* of a newer session for the same wallet and Guardian.
|
|
81
|
+
*/
|
|
82
|
+
export declare function forgetSessionKey(store: SessionKeyStore, session: {
|
|
83
|
+
storeId: string;
|
|
84
|
+
publicKey: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Keeps session keys in IndexedDB so a reload does not need another wallet
|
|
88
|
+
* signature. The browser stores the `CryptoKey` object itself; it stays
|
|
89
|
+
* non-extractable.
|
|
90
|
+
*/
|
|
91
|
+
export declare class IndexedDbSessionKeyStore implements SessionKeyStore {
|
|
92
|
+
private readonly databaseName;
|
|
93
|
+
private static readonly STORE;
|
|
94
|
+
private database;
|
|
95
|
+
constructor(databaseName?: string);
|
|
96
|
+
load(id: string): Promise<StoredSessionKey | null>;
|
|
97
|
+
save(record: StoredSessionKey): Promise<void>;
|
|
98
|
+
remove(id: string, publicKey: string): Promise<void>;
|
|
99
|
+
private request;
|
|
100
|
+
/** Opens the database once; a failed open is retried on the next call. */
|
|
101
|
+
private open;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../src/session/key.ts"],"names":[],"mappings":"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"}
|