@coordination-network/revocation-registry 0.0.2 → 0.1.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 +2 -3
- package/dist/index.cjs +10 -2
- package/dist/index.js +10 -2
- package/package.json +1 -1
- package/src/revocation-registry.ts +11 -2
- package/types/index.d.ts +3 -2
package/README.md
CHANGED
|
@@ -93,9 +93,9 @@ const rebuilt = RevocationRegistry.fromLeaves(slots)
|
|
|
93
93
|
|
|
94
94
|
### Snapshots
|
|
95
95
|
|
|
96
|
-
Replaying every revocation since genesis gets slow. `export()` serializes the whole tree, hashes included, and `import()` rebuilds it without
|
|
96
|
+
Replaying every revocation since genesis gets slow. `export()` serializes the whole tree, hashes included, and `import()` rebuilds it without recomputing every node, so a service can publish periodic snapshots and a holder can sync from the latest one plus the few revocations after it.
|
|
97
97
|
|
|
98
|
-
`import()` validates by default
|
|
98
|
+
`import()` validates by default: it rehashes the leaves against the serialized hashes and requires every leaf value to be a field element, which rejects a tampered snapshot. That is not proof of authenticity. Import a served snapshot with validation on, and compare `root` against the authority's.
|
|
99
99
|
|
|
100
100
|
A snapshot carries the node hashes, so it is larger than the leaf slots and lets a publisher hand you hashes you did not derive. `fromLeaves` is the leaner and more suspicious option; `import` is the faster one when you produced the snapshot yourself.
|
|
101
101
|
|
|
@@ -141,7 +141,6 @@ An index is only valid against the tree state the write applies to. If another r
|
|
|
141
141
|
## Notes
|
|
142
142
|
|
|
143
143
|
- Commitments must be non-zero and below the BN254 scalar field order. Zero is reserved for the sentinel leaf and for tombstones. Above the field order, two distinct values can reduce to the same field element, which would let one revocation silently cover an unrelated credential and would break the sorted ordering a non-membership proof relies on.
|
|
144
|
-
- An empty registry cannot produce proofs. Nothing has been revoked, so a verifier should treat that as "no revocations", not as a failure.
|
|
145
144
|
- Revocation is normally permanent. `unrevoke` exists because the structure supports it, but an un-revoked credential becomes valid again for every verifier still accepting an older root, and it leaves a tombstone that rules out rebuilding from `commitments` alone.
|
|
146
145
|
- `indexOf` returns a physical slot, never a position in `commitments`. The two always differ by at least one, because slot 0 is the sentinel.
|
|
147
146
|
- `revocationHashes` is the Poseidon pair every implementation of a registry must agree on: 3-input for leaves, 2-input for internal nodes. `tests/cross-language.test.ts` pins the resulting roots against the Solidity implementation.
|
package/dist/index.cjs
CHANGED
|
@@ -387,12 +387,20 @@ class RevocationRegistry {
|
|
|
387
387
|
*
|
|
388
388
|
* @param snapshot The serialized tree.
|
|
389
389
|
* @param validate Recompute every commitment and check it against the
|
|
390
|
-
* serialized hashes
|
|
391
|
-
* `false` only for
|
|
390
|
+
* serialized hashes and require every leaf value to be a field element,
|
|
391
|
+
* rejecting a tampered snapshot. Defaults to `true`; pass `false` only for
|
|
392
|
+
* data you produced yourself.
|
|
392
393
|
*/
|
|
393
394
|
static import(snapshot, validate = true) {
|
|
394
395
|
const tree = new RevocationRegistry();
|
|
395
396
|
tree.leanIMTPlus = leanImtPlus.LeanIMTPlus.import(revocationHashes, snapshot, { validate });
|
|
397
|
+
// Hashing reduces mod p, so a leaf holding `c + p` matches the honest root while `isRevoked(c)` is false.
|
|
398
|
+
if (validate) {
|
|
399
|
+
for (const { value, nextValue } of tree.leafSlots) {
|
|
400
|
+
toFieldElement(value);
|
|
401
|
+
toFieldElement(nextValue);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
396
404
|
return tree;
|
|
397
405
|
}
|
|
398
406
|
/**
|
package/dist/index.js
CHANGED
|
@@ -385,12 +385,20 @@ class RevocationRegistry {
|
|
|
385
385
|
*
|
|
386
386
|
* @param snapshot The serialized tree.
|
|
387
387
|
* @param validate Recompute every commitment and check it against the
|
|
388
|
-
* serialized hashes
|
|
389
|
-
* `false` only for
|
|
388
|
+
* serialized hashes and require every leaf value to be a field element,
|
|
389
|
+
* rejecting a tampered snapshot. Defaults to `true`; pass `false` only for
|
|
390
|
+
* data you produced yourself.
|
|
390
391
|
*/
|
|
391
392
|
static import(snapshot, validate = true) {
|
|
392
393
|
const tree = new RevocationRegistry();
|
|
393
394
|
tree.leanIMTPlus = LeanIMTPlus.import(revocationHashes, snapshot, { validate });
|
|
395
|
+
// Hashing reduces mod p, so a leaf holding `c + p` matches the honest root while `isRevoked(c)` is false.
|
|
396
|
+
if (validate) {
|
|
397
|
+
for (const { value, nextValue } of tree.leafSlots) {
|
|
398
|
+
toFieldElement(value);
|
|
399
|
+
toFieldElement(nextValue);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
394
402
|
return tree;
|
|
395
403
|
}
|
|
396
404
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coordination-network/revocation-registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "LeanIMT+ revocation registry in TypeScript: revoke credential commitments and generate membership and non-membership proofs, as the registry itself or as a mirror of one held elsewhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -318,14 +318,23 @@ export class RevocationRegistry {
|
|
|
318
318
|
*
|
|
319
319
|
* @param snapshot The serialized tree.
|
|
320
320
|
* @param validate Recompute every commitment and check it against the
|
|
321
|
-
* serialized hashes
|
|
322
|
-
* `false` only for
|
|
321
|
+
* serialized hashes and require every leaf value to be a field element,
|
|
322
|
+
* rejecting a tampered snapshot. Defaults to `true`; pass `false` only for
|
|
323
|
+
* data you produced yourself.
|
|
323
324
|
*/
|
|
324
325
|
public static import(snapshot: string, validate = true): RevocationRegistry {
|
|
325
326
|
const tree = new RevocationRegistry()
|
|
326
327
|
|
|
327
328
|
tree.leanIMTPlus = LeanIMTPlus.import(revocationHashes, snapshot, { validate })
|
|
328
329
|
|
|
330
|
+
// Hashing reduces mod p, so a leaf holding `c + p` matches the honest root while `isRevoked(c)` is false.
|
|
331
|
+
if (validate) {
|
|
332
|
+
for (const { value, nextValue } of tree.leafSlots) {
|
|
333
|
+
toFieldElement(value)
|
|
334
|
+
toFieldElement(nextValue)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
329
338
|
return tree
|
|
330
339
|
}
|
|
331
340
|
|
package/types/index.d.ts
CHANGED
|
@@ -282,8 +282,9 @@ declare class RevocationRegistry {
|
|
|
282
282
|
*
|
|
283
283
|
* @param snapshot The serialized tree.
|
|
284
284
|
* @param validate Recompute every commitment and check it against the
|
|
285
|
-
* serialized hashes
|
|
286
|
-
* `false` only for
|
|
285
|
+
* serialized hashes and require every leaf value to be a field element,
|
|
286
|
+
* rejecting a tampered snapshot. Defaults to `true`; pass `false` only for
|
|
287
|
+
* data you produced yourself.
|
|
287
288
|
*/
|
|
288
289
|
static import(snapshot: string, validate?: boolean): RevocationRegistry;
|
|
289
290
|
/**
|