keri-ts 2.0.0 → 2.1.0
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/esm/keri/npm/src/acdc/chaining.js +223 -89
- package/esm/keri/npm/src/acdc/observing.js +2 -2
- package/esm/keri/npm/src/acdc/regbasing.js +20 -29
- package/esm/keri/npm/src/acdc/regengine.js +4 -6
- package/esm/keri/npm/src/acdc/registering.js +13 -10
- package/esm/keri/npm/src/acdc/registraring.js +4 -9
- package/esm/keri/npm/src/acdc/salting.js +127 -25
- package/esm/keri/npm/src/acdc/unblinding.js +2 -4
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +1 -1
- package/esm/keri/npm/src/app/credentials/root-replicating.js +123 -0
- package/esm/keri/npm/src/app/credentials/verifying.js +21 -3
- package/esm/keri/npm/src/app/delivery/forwarding.js +49 -7
- package/esm/keri/npm/src/app/exchanges/exchanging.js +11 -12
- package/esm/keri/npm/src/app/identity/configing.js +17 -43
- package/esm/keri/npm/src/app/identity/habbing.js +54 -35
- package/esm/keri/npm/src/app/identity/keeping.js +1 -1
- package/esm/keri/npm/src/app/identity/lmdb-habery.js +12 -0
- package/esm/keri/npm/src/app/ipex/ipex-graph.js +7 -70
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +8 -5
- package/esm/keri/npm/src/app/ipex/ipex-v2.js +21 -5
- package/esm/keri/npm/src/app/ipex/ipexing.js +1 -1
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +5 -9
- package/esm/keri/npm/src/app/multisig/group-members.js +14 -0
- package/esm/keri/npm/src/app/multisig/grouping.js +4 -3
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +7 -2
- package/esm/keri/npm/src/app/notifications/notifying.js +5 -9
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +35 -3
- package/esm/keri/npm/src/app/queries/witness-inquisitor.js +2 -2
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +7 -10
- package/esm/keri/npm/src/app/transport/byte-source.js +6 -42
- package/esm/keri/npm/src/app/transport/cesr-egress.js +77 -13
- package/esm/keri/npm/src/app/transport/file-bytes.js +41 -0
- package/esm/keri/npm/src/app/transport/http-messenger.js +14 -9
- package/esm/keri/npm/src/app/transport/message-source.js +1 -1
- package/esm/keri/npm/src/app/transport/messengering.js +3 -4
- package/esm/keri/npm/src/app/transport/node-http-client.js +21 -0
- package/esm/keri/npm/src/app/transport/tcp-dialer.js +14 -0
- package/esm/keri/npm/src/app/witness/receipt-client.js +2 -2
- package/esm/keri/npm/src/cli/common/context.js +2 -2
- package/esm/keri/npm/src/cli/common/existing.js +3 -5
- package/esm/keri/npm/src/cli/credentials/ipex.js +2 -1
- package/esm/keri/npm/src/cli/endpoints/oobi-clean.js +4 -3
- package/esm/keri/npm/src/cli/endpoints/oobi.js +2 -2
- package/esm/keri/npm/src/cli/identity/challenge.js +2 -2
- package/esm/keri/npm/src/cli/identity/incept.js +2 -2
- package/esm/keri/npm/src/cli/identity/init.js +4 -3
- package/esm/keri/npm/src/cli/identity/interact.js +2 -2
- package/esm/keri/npm/src/cli/identity/rotate.js +2 -2
- package/esm/keri/npm/src/cli/messaging/exchange.js +2 -2
- package/esm/keri/npm/src/cli/messaging/multisig.js +61 -3
- package/esm/keri/npm/src/cli/tools/annotate.js +2 -1
- package/esm/keri/npm/src/cli/tools/benchmark.js +2 -1
- package/esm/keri/npm/src/cli/tools/db-dump.js +1 -4
- package/esm/keri/npm/src/core/bytes.js +9 -0
- package/esm/keri/npm/src/core/kel/delegation-ancestry.js +92 -0
- package/esm/keri/npm/src/core/kel/eventing.js +1 -2
- package/esm/keri/npm/src/core/kel/kever.js +11 -9
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +2 -3
- package/esm/keri/npm/src/core/mailbox/kram-pruning.js +15 -22
- package/esm/keri/npm/src/core/mailbox/kram-staging.js +2 -2
- package/esm/keri/npm/src/core/routing.js +2 -2
- package/esm/keri/npm/src/db/basing.js +149 -203
- package/esm/keri/npm/src/db/core/dber.js +1062 -0
- package/esm/keri/npm/src/db/core/index.js +4 -2
- package/esm/keri/npm/src/db/core/lmdb-opener.js +30 -0
- package/esm/keri/npm/src/db/core/lmdber.js +199 -1067
- package/esm/keri/npm/src/db/core/memstore.js +532 -0
- package/esm/keri/npm/src/db/core/path-manager.js +109 -223
- package/esm/keri/npm/src/db/core/store-owner.js +104 -0
- package/esm/keri/npm/src/db/core/webdber.js +252 -0
- package/esm/keri/npm/src/db/escrowing.js +2 -2
- package/esm/keri/npm/src/db/exchange/exchange-nests.js +3 -2
- package/esm/keri/npm/src/db/keeping.js +27 -121
- package/esm/keri/npm/src/db/koming.js +1 -1
- package/esm/keri/npm/src/db/lmdb-stores.js +68 -0
- package/esm/keri/npm/src/db/lmdb.js +6 -0
- package/esm/keri/npm/src/db/mailbox/mailboxing.js +12 -65
- package/esm/keri/npm/src/db/noting.js +11 -55
- package/esm/keri/npm/src/db/reger.js +43 -105
- package/esm/keri/npm/src/db/replay/witness-replay.js +2 -20
- package/esm/keri/npm/src/db/subing.js +1 -1
- package/esm/keri/npm/src/npm/db-lmdb.js +3 -0
- package/esm/keri/npm/src/npm/runtime-node.js +3 -0
- package/esm/keri/npm/src/runtime/index.js +9 -10
- package/esm/keri/npm/src/runtime/node.js +14 -0
- package/esm/keri/npm/src/vdr/eventing.js +1 -1
- package/esm/keri/npm/src/vdr/tel-publication.js +3 -3
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +11 -4
- package/types/acdc/chaining.d.ts +56 -6
- package/types/acdc/chaining.d.ts.map +1 -1
- package/types/acdc/regbasing.d.ts +7 -6
- package/types/acdc/regbasing.d.ts.map +1 -1
- package/types/acdc/regengine.d.ts.map +1 -1
- package/types/acdc/registering.d.ts +8 -4
- package/types/acdc/registering.d.ts.map +1 -1
- package/types/acdc/registraring.d.ts.map +1 -1
- package/types/acdc/salting.d.ts +53 -6
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/credentials/root-replicating.d.ts +85 -0
- package/types/app/credentials/root-replicating.d.ts.map +1 -0
- package/types/app/credentials/verifying.d.ts +2 -1
- package/types/app/credentials/verifying.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +10 -0
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts +2 -0
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/configing.d.ts +7 -5
- package/types/app/identity/configing.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +23 -5
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +1 -1
- package/types/app/identity/lmdb-habery.d.ts +6 -0
- package/types/app/identity/lmdb-habery.d.ts.map +1 -0
- package/types/app/ipex/ipex-graph.d.ts +4 -4
- package/types/app/ipex/ipex-graph.d.ts.map +1 -1
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/ipex-v2.d.ts +11 -2
- package/types/app/ipex/ipex-v2.d.ts.map +1 -1
- package/types/app/ipex/ipexing.d.ts +3 -0
- package/types/app/ipex/ipexing.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +1 -1
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/group-members.d.ts +9 -0
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +2 -1
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/notifications/notifying.d.ts +1 -1
- package/types/app/notifications/notifying.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +3 -1
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +2 -0
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/runtime/agent-runtime.d.ts +7 -0
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/transport/byte-source.d.ts +13 -10
- package/types/app/transport/byte-source.d.ts.map +1 -1
- package/types/app/transport/cesr-egress.d.ts +21 -7
- package/types/app/transport/cesr-egress.d.ts.map +1 -1
- package/types/app/transport/file-bytes.d.ts +11 -0
- package/types/app/transport/file-bytes.d.ts.map +1 -0
- package/types/app/transport/http-client.d.ts +3 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/app/transport/http-messenger.d.ts +15 -10
- package/types/app/transport/http-messenger.d.ts.map +1 -1
- package/types/app/transport/message-source.d.ts +1 -1
- package/types/app/transport/messengering.d.ts +2 -2
- package/types/app/transport/messengering.d.ts.map +1 -1
- package/types/app/transport/node-http-client.d.ts +10 -0
- package/types/app/transport/node-http-client.d.ts.map +1 -0
- package/types/app/transport/tcp-dialer.d.ts +21 -0
- package/types/app/transport/tcp-dialer.d.ts.map +1 -0
- package/types/cli/common/existing.d.ts.map +1 -1
- package/types/cli/credentials/ipex.d.ts.map +1 -1
- package/types/cli/endpoints/oobi-clean.d.ts.map +1 -1
- package/types/cli/identity/init.d.ts.map +1 -1
- package/types/cli/index.d.ts +1 -1
- package/types/cli/index.d.ts.map +1 -1
- package/types/cli/messaging/multisig.d.ts +10 -1
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/cli/tools/annotate.d.ts.map +1 -1
- package/types/cli/tools/benchmark.d.ts.map +1 -1
- package/types/cli/tools/db-dump.d.ts.map +1 -1
- package/types/core/bytes.d.ts +2 -0
- package/types/core/bytes.d.ts.map +1 -1
- package/types/core/kel/delegation-ancestry.d.ts +41 -0
- package/types/core/kel/delegation-ancestry.d.ts.map +1 -0
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/kel/kever.d.ts +5 -4
- package/types/core/kel/kever.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kram-pruning.d.ts +1 -3
- package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
- package/types/db/basing.d.ts +14 -23
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/dber.d.ts +505 -0
- package/types/db/core/dber.d.ts.map +1 -0
- package/types/db/core/index.d.ts +4 -2
- package/types/db/core/index.d.ts.map +1 -1
- package/types/db/core/lmdb-opener.d.ts +17 -0
- package/types/db/core/lmdb-opener.d.ts.map +1 -0
- package/types/db/core/lmdber.d.ts +60 -383
- package/types/db/core/lmdber.d.ts.map +1 -1
- package/types/db/core/memstore.d.ts +129 -0
- package/types/db/core/memstore.d.ts.map +1 -0
- package/types/db/core/path-manager.d.ts +22 -45
- package/types/db/core/path-manager.d.ts.map +1 -1
- package/types/db/core/store-owner.d.ts +59 -0
- package/types/db/core/store-owner.d.ts.map +1 -0
- package/types/db/core/webdber.d.ts +127 -0
- package/types/db/core/webdber.d.ts.map +1 -0
- package/types/db/escrowing.d.ts +3 -3
- package/types/db/escrowing.d.ts.map +1 -1
- package/types/db/exchange/exchange-nests.d.ts.map +1 -1
- package/types/db/keeping.d.ts +9 -23
- package/types/db/keeping.d.ts.map +1 -1
- package/types/db/koming.d.ts +7 -8
- package/types/db/koming.d.ts.map +1 -1
- package/types/db/lmdb-stores.d.ts +45 -0
- package/types/db/lmdb-stores.d.ts.map +1 -0
- package/types/db/lmdb.d.ts +7 -0
- package/types/db/lmdb.d.ts.map +1 -0
- package/types/db/mailbox/mailboxing.d.ts +7 -21
- package/types/db/mailbox/mailboxing.d.ts.map +1 -1
- package/types/db/noting.d.ts +7 -12
- package/types/db/noting.d.ts.map +1 -1
- package/types/db/reger.d.ts +7 -11
- package/types/db/reger.d.ts.map +1 -1
- package/types/db/replay/witness-replay.d.ts +0 -2
- package/types/db/replay/witness-replay.d.ts.map +1 -1
- package/types/db/subing.d.ts +24 -25
- package/types/db/subing.d.ts.map +1 -1
- package/types/npm/db-lmdb.d.ts +4 -0
- package/types/npm/db-lmdb.d.ts.map +1 -0
- package/types/npm/runtime-node.d.ts +4 -0
- package/types/npm/runtime-node.d.ts.map +1 -0
- package/types/runtime/index.d.ts +9 -10
- package/types/runtime/index.d.ts.map +1 -1
- package/types/runtime/node.d.ts +15 -0
- package/types/runtime/node.d.ts.map +1 -0
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/acdc/relating.js +0 -59
- package/esm/keri/npm/src/db/baser-defaults.js +0 -11
- package/types/acdc/relating.d.ts +0 -21
- package/types/acdc/relating.d.ts.map +0 -1
- package/types/db/baser-defaults.d.ts +0 -5
- package/types/db/baser-defaults.d.ts.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Encrypted copies of existing registry roots.
|
|
3
3
|
*
|
|
4
4
|
* `RegistryRoot` replicates held roots to individual members with ESSR `exn`
|
|
5
5
|
* messages, ported from KERIpy `keri.acdc.salting` (kentbull/keripy v2-spec).
|
|
6
|
+
* Operators send them explicitly; group issuance completion and accepted group
|
|
7
|
+
* rotations also send them, fire and forget (`app/credentials/root-replicating.ts`).
|
|
6
8
|
*
|
|
7
9
|
* Local profile, not a KERI, ACDC or #1740 standard. Upstream KERIpy uses ESSR
|
|
8
10
|
* only as the v1 `/essr/req` transport envelope on direct sends.
|
|
@@ -19,9 +21,12 @@
|
|
|
19
21
|
* dependency escrow (`Baser.rrpe`, no timeout) that `processEscrows` retries.
|
|
20
22
|
*/
|
|
21
23
|
import { concatBytes, Counter, Dater, Diger, Encrypter, essrChunks, Kinds, LabelDex, makeBlindUuid, MtrDex, ParserError, Prefixer, Saider, Salter, Texter, unblindBlindState, unblindBoundState, Vrsn_2_0, } from "cesr-ts";
|
|
22
|
-
import { groupWitnesserSelection, isLocalGroupHab } from "../app/multisig/group-members.js";
|
|
24
|
+
import { groupWitnesserSelection, isLocalGroupHab, localGroupMember } from "../app/multisig/group-members.js";
|
|
23
25
|
import { ValidationError } from "../core/errors.js";
|
|
26
|
+
import { Kever } from "../core/kel/kever.js";
|
|
24
27
|
import { exchange } from "../core/protocol-eventing.js";
|
|
28
|
+
import { dgKey } from "../db/core/keys.js";
|
|
29
|
+
import { hasNests, loadNests } from "../db/exchange/exchange-nests.js";
|
|
25
30
|
import { makeNowIso8601 } from "../time/mod.js";
|
|
26
31
|
import { validateRip, validateUpdate, verifyRegistryAnchor, vetBlind } from "./regeventing.js";
|
|
27
32
|
/** A stored registry-root copy that can never be retained as received. */
|
|
@@ -65,9 +70,9 @@ function contribution(db, member, verfers) {
|
|
|
65
70
|
const event = db.getEvtSerder(member, said);
|
|
66
71
|
if (!event?.estive)
|
|
67
72
|
continue;
|
|
68
|
-
const
|
|
73
|
+
const key = event.verfers[0]?.qb64 ?? "", index = keys.indexOf(key);
|
|
69
74
|
if (index >= 0)
|
|
70
|
-
return { sn, index };
|
|
75
|
+
return { sn, index, key };
|
|
71
76
|
}
|
|
72
77
|
return null;
|
|
73
78
|
}
|
|
@@ -290,14 +295,8 @@ export class RegistryRoot {
|
|
|
290
295
|
}
|
|
291
296
|
return rip;
|
|
292
297
|
}
|
|
293
|
-
/**
|
|
294
|
-
|
|
295
|
-
*
|
|
296
|
-
* Members, not keys, are compared: a continuing member usually rotates its
|
|
297
|
-
* own key, so it contributes to both establishments. Candidates are the AIDs
|
|
298
|
-
* with accepted key state in this Habery.
|
|
299
|
-
*/
|
|
300
|
-
newcomers(ghab, rotation) {
|
|
298
|
+
/** Return `ghab`'s accepted `rotation` and the establishment before it; ValidationError otherwise. */
|
|
299
|
+
rotation(ghab, rotation) {
|
|
301
300
|
const db = this.hby.db, pre = ghab.pre;
|
|
302
301
|
if (!pre || ghab.db !== db || !isLocalGroupHab(this.hby, ghab)) {
|
|
303
302
|
throw new ValidationError("newcomers require a local group habitat");
|
|
@@ -306,29 +305,59 @@ export class RegistryRoot {
|
|
|
306
305
|
if (!event || !["rot", "drt"].includes(event.ilk) || db.kels.getLast(pre, event.sn) !== rotation) {
|
|
307
306
|
throw new ValidationError("newcomers require an accepted group rotation");
|
|
308
307
|
}
|
|
309
|
-
let prior = null;
|
|
310
308
|
for (const [, sn, said] of db.kels.getOnItemBackIter(pre, event.sn - 1)) {
|
|
311
309
|
if (db.kels.getLast(pre, sn) !== said)
|
|
312
310
|
continue;
|
|
313
311
|
const candidate = db.getEvtSerder(pre, said);
|
|
314
|
-
if (candidate?.estive)
|
|
315
|
-
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
312
|
+
if (candidate?.estive)
|
|
313
|
+
return [event, candidate];
|
|
318
314
|
}
|
|
319
|
-
|
|
320
|
-
|
|
315
|
+
throw new ValidationError("rotation has no accepted prior establishment");
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Return current signing member AIDs added by one accepted group rotation.
|
|
319
|
+
*
|
|
320
|
+
* Members, not keys, are compared: a continuing member usually rotates its
|
|
321
|
+
* own key, so it contributes to both establishments. `rekeyed` also returns
|
|
322
|
+
* continuing members whose contributing key changed, the recipients a
|
|
323
|
+
* re-seal must reach under their new key. Candidates are the AIDs with
|
|
324
|
+
* accepted key state in this Habery.
|
|
325
|
+
*/
|
|
326
|
+
newcomers(ghab, rotation, { rekeyed = false } = {}) {
|
|
327
|
+
const db = this.hby.db, pre = ghab.pre, [event, prior] = this.rotation(ghab, rotation);
|
|
321
328
|
const found = [];
|
|
322
329
|
for (const [keys] of db.states.getTopItemIter()) {
|
|
323
330
|
const aid = keys.join(".");
|
|
324
331
|
if (aid === pre || !db.getKever(aid))
|
|
325
332
|
continue;
|
|
326
|
-
const added = contribution(db, aid, event.verfers);
|
|
327
|
-
if (added &&
|
|
333
|
+
const added = contribution(db, aid, event.verfers), before = contribution(db, aid, prior.verfers);
|
|
334
|
+
if (added && this.current(aid, pre) && (!before || rekeyed && before.key !== added.key)) {
|
|
328
335
|
found.push([added.index, aid]);
|
|
336
|
+
}
|
|
329
337
|
}
|
|
330
338
|
return found.sort((left, right) => left[0] - right[0]).map(([, aid]) => aid);
|
|
331
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Return the members `ghab`'s local member re-seals held roots to after `rotation`.
|
|
342
|
+
*
|
|
343
|
+
* Only a signer of the accepted rotation that held a key in the prior
|
|
344
|
+
* establishment sends, and it sends to each current signing member that is
|
|
345
|
+
* new in the rotation or whose contributing key changed (`newcomers` with
|
|
346
|
+
* `rekeyed`). Every such signer sends: copies are idempotent, so no single
|
|
347
|
+
* sender is elected. An empty list means nothing is owed.
|
|
348
|
+
*/
|
|
349
|
+
resealed(ghab, rotation) {
|
|
350
|
+
const recipients = this.newcomers(ghab, rotation, { rekeyed: true });
|
|
351
|
+
const db = this.hby.db, [event, prior] = this.rotation(ghab, rotation);
|
|
352
|
+
const member = localGroupMember(this.hby, ghab.pre).pre;
|
|
353
|
+
const own = contribution(db, member, event.verfers);
|
|
354
|
+
if (!own || !contribution(db, member, prior.verfers))
|
|
355
|
+
return [];
|
|
356
|
+
const { indices } = Kever.verifyIndexedSignatures(event.raw, db.sigs.get(dgKey(ghab.pre, rotation)), event.verfers);
|
|
357
|
+
if (!indices.includes(own.index))
|
|
358
|
+
return [];
|
|
359
|
+
return recipients.filter(aid => aid !== member);
|
|
360
|
+
}
|
|
332
361
|
/** Return the current signing member AIDs of `gid` known to this Habery. */
|
|
333
362
|
members(gid) {
|
|
334
363
|
const db = this.hby.db, gkever = db.getKever(gid), found = [];
|
|
@@ -365,10 +394,17 @@ export class RegistryRoot {
|
|
|
365
394
|
// sending
|
|
366
395
|
/**
|
|
367
396
|
* Return one authorized copy of a root held locally for `gid` or the issuer.
|
|
397
|
+
*
|
|
368
398
|
* `hab` is the local individual sender. `event` defaults to the registry's
|
|
369
|
-
* accepted BUP head.
|
|
399
|
+
* accepted BUP head. `td` is the recipient's verification hint: omitted, it
|
|
400
|
+
* is derived for this registry (`hint`); `""` sends none; any other value is
|
|
401
|
+
* an override. Before sealing, a derived or override hint must let this
|
|
402
|
+
* sender's own root reproduce the BUP `b` commitment, so a copy never carries
|
|
403
|
+
* a hint that cannot verify it. `hinted` refuses with RootUnverified, instead
|
|
404
|
+
* of sending a copy without a hint, when no local evidence yields one;
|
|
405
|
+
* automatic sends set it.
|
|
370
406
|
*/
|
|
371
|
-
prepare(hab, recipient, gid, registry, { event, td } = {}) {
|
|
407
|
+
prepare(hab, recipient, gid, registry, { event, td, hinted = false } = {}) {
|
|
372
408
|
const rip = this.authorize(hab.pre, recipient, gid, registry);
|
|
373
409
|
let root = null;
|
|
374
410
|
for (const holder of [gid, rip.issuer]) {
|
|
@@ -380,8 +416,74 @@ export class RegistryRoot {
|
|
|
380
416
|
}
|
|
381
417
|
if (!root)
|
|
382
418
|
throw new ValidationError("no local root is held for this registry");
|
|
383
|
-
const [bup] = this.event(registry, event ?? this.rgy.store.headEvent(registry).said, rip);
|
|
384
|
-
|
|
419
|
+
const [bup, sn] = this.event(registry, event ?? this.rgy.store.headEvent(registry).said, rip);
|
|
420
|
+
const uuid = makeBlindUuid({ salt: root.qb64, sn }).qb64;
|
|
421
|
+
let hint = td;
|
|
422
|
+
if (hint === undefined) {
|
|
423
|
+
hint = this.hint(gid, registry, uuid, bup) ?? undefined;
|
|
424
|
+
if (hint === undefined && hinted) {
|
|
425
|
+
throw new RootUnverified(`no held opening or stored Grant ACDC lets this root reproduce registry ${registry} update ${bup.said}; `
|
|
426
|
+
+ "send it with an ACDC hint");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else if (hint && !this.reproduces(bup.sad.b, uuid, hint, gid)) {
|
|
430
|
+
throw new ValidationError(`the local root does not reproduce registry ${registry} update ${bup.said} with ACDC hint ${hint}`);
|
|
431
|
+
}
|
|
432
|
+
return rootExn(hab, recipient, gid, registry, root, { event: bup.said, td: hint });
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Return the ACDC SAID by which `uuid` reproduces `bup`'s commitment, or null.
|
|
436
|
+
*
|
|
437
|
+
* Derived per registry, never shared across registries: the ACDC of the
|
|
438
|
+
* BUP's locally held opening first, then each ACDC of `registry` nested in a
|
|
439
|
+
* locally stored exchange. A candidate counts only when it reproduces the
|
|
440
|
+
* commitment, which is the sender self-check.
|
|
441
|
+
*/
|
|
442
|
+
hint(gid, registry, uuid, bup) {
|
|
443
|
+
const held = this.rgy.store.publicOpening(bup.said)?.td.nonce;
|
|
444
|
+
const blid = bup.sad.b;
|
|
445
|
+
if (held && this.reproduces(blid, uuid, held, gid))
|
|
446
|
+
return held;
|
|
447
|
+
for (const td of this.acdcs(registry)) {
|
|
448
|
+
if (this.reproduces(blid, uuid, td, gid))
|
|
449
|
+
return td;
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Yield SAIDs of ACDCs in `registry` nested in any locally stored exchange.
|
|
455
|
+
*
|
|
456
|
+
* A custodian finds them in its own `/ipex/grant`; a member holding only a
|
|
457
|
+
* received copy finds them nested in the `/multisig/exn` proposal it signed.
|
|
458
|
+
* Nests are walked recursively, so the wrapper depth does not matter, and no
|
|
459
|
+
* index table is needed. An exchange whose stored nests no longer decode is
|
|
460
|
+
* skipped: hint search is advisory and the self-check still guards the copy.
|
|
461
|
+
*/
|
|
462
|
+
*acdcs(registry) {
|
|
463
|
+
const db = this.hby.db;
|
|
464
|
+
for (const [keys] of db.exns.getTopItemIter()) {
|
|
465
|
+
const said = keys.join(".");
|
|
466
|
+
if (!hasNests(db, said))
|
|
467
|
+
continue;
|
|
468
|
+
let nests;
|
|
469
|
+
try {
|
|
470
|
+
nests = loadNests(db, said);
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
if (error instanceof ParserError || error instanceof ValidationError)
|
|
474
|
+
continue;
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
yield* RegistryRoot.nested(nests, registry);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/** Depth-first SAIDs of nested bodies whose `rd` is `registry`. */
|
|
481
|
+
static *nested(nests, registry) {
|
|
482
|
+
for (const nest of nests) {
|
|
483
|
+
if (nest.body.ked?.rd === registry && nest.body.said)
|
|
484
|
+
yield nest.body.said;
|
|
485
|
+
yield* RegistryRoot.nested(nest.nests ?? [], registry);
|
|
486
|
+
}
|
|
385
487
|
}
|
|
386
488
|
// receiving
|
|
387
489
|
/** Recheck exact accepted BUP membership and issuer anchoring; return the BUP and its ordinal. */
|
|
@@ -537,7 +537,7 @@ export class Unblind {
|
|
|
537
537
|
// Serialization may suspend; repeat all authority and snapshot checks inside
|
|
538
538
|
// the single writer. This does not rewrite another Grant or any Admit.
|
|
539
539
|
const wires = yield* abortable((signal) => prepareNests(nests, signal));
|
|
540
|
-
const result = db.
|
|
540
|
+
const result = db.transaction(() => {
|
|
541
541
|
this.live(op);
|
|
542
542
|
this.current(op);
|
|
543
543
|
const retained = db.enst.get([op.grant]);
|
|
@@ -553,9 +553,7 @@ export class Unblind {
|
|
|
553
553
|
db.exrs.pin([op.grant, node], record);
|
|
554
554
|
return { kind: "accept", said: op.grant };
|
|
555
555
|
});
|
|
556
|
-
yield*
|
|
557
|
-
await db.env.flushed;
|
|
558
|
-
});
|
|
556
|
+
yield* db.flushed();
|
|
559
557
|
return result;
|
|
560
558
|
}
|
|
561
559
|
/** Discard approved work and unsent replies; delivered bytes remain caller-owned. */
|
|
@@ -266,7 +266,7 @@ export class Oobiery {
|
|
|
266
266
|
};
|
|
267
267
|
// A new or resumed attempt supersedes old terminal facts, but keeps its
|
|
268
268
|
// queued row until intake succeeds. Cancellation can therefore retry it.
|
|
269
|
-
this.hby.db.
|
|
269
|
+
this.hby.db.transaction(() => {
|
|
270
270
|
this.pinQueueStore(kind, url, queuedRecord);
|
|
271
271
|
this.hby.db.eoobi.rem(url);
|
|
272
272
|
this.hby.db.roobi.rem(url);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finite delivery of prepared `/registry/root` copies; transmission is never receipt.
|
|
3
|
+
*
|
|
4
|
+
* `replicateRegistryRoots` is the KERIpy `rootreplicating.replicate` happy path:
|
|
5
|
+
* group commands call it once a send is possible and report each outcome. There
|
|
6
|
+
* is no acknowledgement, sent-copies record or retry; receiving is idempotent,
|
|
7
|
+
* so duplicate copies are harmless, and `tufa vc registry root send` is the
|
|
8
|
+
* recovery path for a copy that was not sent.
|
|
9
|
+
*/
|
|
10
|
+
import { parseBytes } from "cesr-ts";
|
|
11
|
+
import { action, race } from "effection";
|
|
12
|
+
import { AppError, ValidationError } from "../../core/errors.js";
|
|
13
|
+
import { CREDENTIAL_MAILBOX_TOPIC } from "../../core/mailbox/mailbox-topics.js";
|
|
14
|
+
import { localGroupMember } from "../multisig/group-members.js";
|
|
15
|
+
/** The operator command that resends a copy automatic replication did not send. */
|
|
16
|
+
export const RootCopyRecovery = "tufa vc registry root send";
|
|
17
|
+
/**
|
|
18
|
+
* Send each copy from `sender` to its recipient's `credential` mailbox, in
|
|
19
|
+
* order, and return one outcome per copy. A failed copy does not stop later
|
|
20
|
+
* copies. The caller runs `postman.deliverDo` for the duration; Poster wait
|
|
21
|
+
* misuse or closure still throws.
|
|
22
|
+
*/
|
|
23
|
+
export function* deliverRegistryRoots(postman, sender, copies) {
|
|
24
|
+
const outcomes = [];
|
|
25
|
+
for (const copy of copies) {
|
|
26
|
+
const event = postman.send({ src: sender, dest: copy.recipient, topic: CREDENTIAL_MAILBOX_TOPIC, msg: copy.wire }, { notify: true });
|
|
27
|
+
const outcome = yield* postman.wait(event);
|
|
28
|
+
outcomes.push(outcome.kin === "sent"
|
|
29
|
+
? { kin: "sent", copy, deliveries: outcome.deliveries }
|
|
30
|
+
: { kin: "failed", copy, error: outcome.error });
|
|
31
|
+
}
|
|
32
|
+
return outcomes;
|
|
33
|
+
}
|
|
34
|
+
/** Run `operation` for at most `milliseconds` of `clock` time; null when the budget ends first. */
|
|
35
|
+
function* within(clock, milliseconds, operation) {
|
|
36
|
+
return yield* race([
|
|
37
|
+
operation,
|
|
38
|
+
action(resolve => {
|
|
39
|
+
const timer = clock.setTimeout(() => resolve(null), Math.max(milliseconds, 0));
|
|
40
|
+
return () => clock.clearTimeout(timer);
|
|
41
|
+
}),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fire and forget one copy of each of `registries`' held roots for group
|
|
46
|
+
* `ghab` to each recipient, signed by `ghab`'s local member.
|
|
47
|
+
*
|
|
48
|
+
* `recipients` defaults to every other current signing member. Each copy is
|
|
49
|
+
* prepared with `RegistryRoot.prepare` and a per-registry derived hint
|
|
50
|
+
* (`hinted`, so a copy that could never verify is reported, not sent) and
|
|
51
|
+
* delivered through `deliverRegistryRoots`, as `tufa vc registry root send`
|
|
52
|
+
* does. `timeoutMs` is this replication's own budget: a copy reached after it
|
|
53
|
+
* ends, or still sending when it ends, is reported unsent. Nothing is retried
|
|
54
|
+
* or recorded and an `AppError` never escapes: every outcome is returned for
|
|
55
|
+
* display. The caller runs `postman.deliverDo` for the duration.
|
|
56
|
+
*/
|
|
57
|
+
export function* replicateRegistryRoots(root, postman, clock, ghab, registries, { recipients, timeoutMs }) {
|
|
58
|
+
const gid = ghab.pre, sender = localGroupMember(root.hby, gid), deadline = clock.now() + Math.max(timeoutMs, 0);
|
|
59
|
+
const targets = recipients ?? root.members(gid).filter(aid => aid !== sender.pre);
|
|
60
|
+
const reports = [];
|
|
61
|
+
for (const registry of registries) {
|
|
62
|
+
for (const recipient of targets) {
|
|
63
|
+
const result = {
|
|
64
|
+
phase: "root_copy",
|
|
65
|
+
gid,
|
|
66
|
+
registry,
|
|
67
|
+
sender: sender.pre,
|
|
68
|
+
recipient,
|
|
69
|
+
transmitted: false,
|
|
70
|
+
};
|
|
71
|
+
let failure = null;
|
|
72
|
+
try {
|
|
73
|
+
if (clock.now() >= deadline) {
|
|
74
|
+
throw new ValidationError("registry root copies did not complete within their budget");
|
|
75
|
+
}
|
|
76
|
+
const wire = root.prepare(sender, recipient, gid, registry, { hinted: true });
|
|
77
|
+
result.exchange = parseBytes(wire)[0].body.said;
|
|
78
|
+
const outcome = yield* within(clock, deadline - clock.now(), deliverRegistryRoots(postman, sender.pre, [{ recipient, wire }]));
|
|
79
|
+
if (!outcome)
|
|
80
|
+
failure = new ValidationError("registry root copies did not complete within their budget");
|
|
81
|
+
else if (outcome[0].kin === "failed")
|
|
82
|
+
failure = outcome[0].error ?? "delivery failed";
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (!(error instanceof AppError))
|
|
86
|
+
throw error;
|
|
87
|
+
failure = error;
|
|
88
|
+
}
|
|
89
|
+
reports.push(failure === null ? { ...result, transmitted: true } : {
|
|
90
|
+
...result,
|
|
91
|
+
error: failure instanceof Error ? failure.message : String(failure),
|
|
92
|
+
recovery: RootCopyRecovery,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return reports;
|
|
97
|
+
}
|
|
98
|
+
/** Budget for re-seal copies from a command without its own `--timeout`, as KERIpy `rootreplicating.Budget`. */
|
|
99
|
+
export const RootResealBudgetMs = 30_000;
|
|
100
|
+
/**
|
|
101
|
+
* After an accepted group rotation, send held roots to new or rekeyed members.
|
|
102
|
+
*
|
|
103
|
+
* Fire and forget like `replicateRegistryRoots`: only a signer of the rotation
|
|
104
|
+
* that held a prior key sends (`RegistryRoot.resealed`), every such signer
|
|
105
|
+
* sends with no election, a member holding no root for the group sends
|
|
106
|
+
* nothing, and no failure ever fails the rotation. The caller runs
|
|
107
|
+
* `postman.deliverDo` for the duration.
|
|
108
|
+
*/
|
|
109
|
+
export function* resealRegistryRoots(root, postman, clock, ghab, rotation, { timeoutMs = RootResealBudgetMs } = {}) {
|
|
110
|
+
let recipients;
|
|
111
|
+
try {
|
|
112
|
+
recipients = root.resealed(ghab, rotation);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (!(error instanceof AppError))
|
|
116
|
+
throw error;
|
|
117
|
+
return [{ phase: "root_copy", gid: ghab.pre, rotation, transmitted: false, error: error.message }];
|
|
118
|
+
}
|
|
119
|
+
const registries = root.held(ghab.pre);
|
|
120
|
+
if (!recipients.length || !registries.length)
|
|
121
|
+
return [];
|
|
122
|
+
return yield* replicateRegistryRoots(root, postman, clock, ghab, registries, { recipients, timeoutMs });
|
|
123
|
+
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*
|
|
8
8
|
* TypeScript divergence:
|
|
9
9
|
* - ordinary outcomes are explicit decisions instead of exception-only control
|
|
10
|
-
* flow; durable corruption and unsupported `DI2I` still
|
|
10
|
+
* flow; durable corruption and unsupported delegated `DI2I`/`DI1I` still
|
|
11
|
+
* fail explicitly
|
|
11
12
|
*/
|
|
12
13
|
import { Dater, Ilks, Saider, } from "cesr-ts";
|
|
13
14
|
import { Deck } from "../../core/deck.js";
|
|
@@ -92,6 +93,15 @@ export class Verifier {
|
|
|
92
93
|
processCredential(args) {
|
|
93
94
|
const creder = args.creder;
|
|
94
95
|
const said = credentialSaid(creder);
|
|
96
|
+
// Parser hydration does not verify bodies; KERIpy's Serder does at parse time.
|
|
97
|
+
if (!creder.verify()) {
|
|
98
|
+
return {
|
|
99
|
+
kind: "reject",
|
|
100
|
+
reason: "invalidCredential",
|
|
101
|
+
said,
|
|
102
|
+
detail: "Credential body does not match its SAID.",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
95
105
|
const regk = creder.regid;
|
|
96
106
|
const issuer = creder.issuer;
|
|
97
107
|
const schema = creder.schema;
|
|
@@ -259,10 +269,18 @@ export class Verifier {
|
|
|
259
269
|
}
|
|
260
270
|
const attrib = creder.attrib;
|
|
261
271
|
let operator = op;
|
|
262
|
-
if (operator
|
|
272
|
+
if (operator === "I1I" || operator === "DI1I") {
|
|
273
|
+
// Issuer identity suppresses the issuee default and never reads issuees.
|
|
274
|
+
if (creder.issuer !== issuer) {
|
|
275
|
+
if (operator === "I1I")
|
|
276
|
+
return null;
|
|
277
|
+
throw new ValidationError("DI1I credential chain verification is not implemented.");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
else if (operator !== "I2I" && operator !== "DI2I" && operator !== "NI2I") {
|
|
263
281
|
operator = isRecord(attrib) && typeof attrib.i === "string" ? "I2I" : "NI2I";
|
|
264
282
|
}
|
|
265
|
-
if (operator
|
|
283
|
+
if (operator === "I2I" || operator === "DI2I") {
|
|
266
284
|
if (!isRecord(attrib) || typeof attrib.i !== "string") {
|
|
267
285
|
return null;
|
|
268
286
|
}
|
|
@@ -11,9 +11,31 @@ import { abortable } from "../runtime/abortable.js";
|
|
|
11
11
|
import { runtimeTurn } from "../runtime/runtime-turn.js";
|
|
12
12
|
import { CesrHttpRefusal } from "../transport/cesr-egress.js";
|
|
13
13
|
import { messengerFrom, streamMessengerFrom } from "../transport/messengering.js";
|
|
14
|
-
import {
|
|
14
|
+
import { requireTcpDialer } from "../transport/tcp-dialer.js";
|
|
15
15
|
import { TransportError } from "../transport/transport-error.js";
|
|
16
16
|
import { introduce } from "./introducing.js";
|
|
17
|
+
/**
|
|
18
|
+
* A v2 message refused for a peer this node must treat as v1.
|
|
19
|
+
*
|
|
20
|
+
* A v1 parser treats a v2 version string as an extraction failure and discards the rest of
|
|
21
|
+
* its stream, so sending would lose more than this one message.
|
|
22
|
+
*/
|
|
23
|
+
export class PeerVersionError extends ValidationError {
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Whether an AID's own accepted KEL ends in a v1 event, which is the only evidence of a v1 reader.
|
|
27
|
+
*
|
|
28
|
+
* An AID with no accepted KEL is not refused. A delegate awaiting approval has only an escrowed
|
|
29
|
+
* inception, and refusing its approval traffic would stop the delegation from ever completing.
|
|
30
|
+
*/
|
|
31
|
+
function knownV1Peer(hby, aid) {
|
|
32
|
+
const kever = hby.db.getKever(aid);
|
|
33
|
+
return kever !== null && kever !== undefined && kever.serder.pvrsn.major < 2;
|
|
34
|
+
}
|
|
35
|
+
/** Whether any frame of this complete signed wire needs a v2 reader. */
|
|
36
|
+
function wireNeedsV2(message) {
|
|
37
|
+
return parseBytes(message).some(frame => frame.body.pvrsn.major >= 2 || (frame.gvrsn?.major ?? 1) >= 2);
|
|
38
|
+
}
|
|
17
39
|
/**
|
|
18
40
|
* Preserve resolved endpoint schemes and signed bytes for concrete transport construction.
|
|
19
41
|
*
|
|
@@ -374,11 +396,20 @@ export class Poster {
|
|
|
374
396
|
failed.push(new ValidationError(`No supported endpoint URL for ${endpoint.eid}.`));
|
|
375
397
|
continue;
|
|
376
398
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
399
|
+
let target;
|
|
400
|
+
try {
|
|
401
|
+
target = yield* this.prepareBytesTarget(hab, route, {
|
|
402
|
+
eid: endpoint.eid,
|
|
403
|
+
url: endpoint.url,
|
|
404
|
+
urls: endpoint.urls,
|
|
405
|
+
}, evt);
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
if (!(error instanceof PeerVersionError))
|
|
409
|
+
throw error;
|
|
410
|
+
failed.push(error);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
382
413
|
try {
|
|
383
414
|
if (target.kind !== "stored") {
|
|
384
415
|
const refusal = yield* this.sendPreparedTarget(target, evt, signal);
|
|
@@ -416,7 +447,7 @@ export class Poster {
|
|
|
416
447
|
*sendPreparedTarget(target, evt, signal) {
|
|
417
448
|
if (target.kind === "tcp" && evt.replies) {
|
|
418
449
|
// Explicit duplex work retains its receive owner; send-only factories need none.
|
|
419
|
-
yield*
|
|
450
|
+
yield* requireTcpDialer(this.http).messenger(target.url, this.clock, this.protocol).send(target.message, {
|
|
420
451
|
signal,
|
|
421
452
|
replies: true,
|
|
422
453
|
});
|
|
@@ -482,11 +513,18 @@ export class Poster {
|
|
|
482
513
|
* @param endpoint One selected endpoint AID and URL from the route.
|
|
483
514
|
* @param evt Original signed source and concrete ordinary/stream operation.
|
|
484
515
|
* @returns A prepared remote target, or stored after authorized local mailbox publication.
|
|
516
|
+
* @throws PeerVersionError when the wire needs a v2 reader that this peer is known not to be.
|
|
485
517
|
*/
|
|
486
518
|
*prepareBytesTarget(hab, route, endpoint, evt) {
|
|
519
|
+
if (wireNeedsV2(evt.msg) && knownV1Peer(this.hby, evt.dest)) {
|
|
520
|
+
throw new PeerVersionError(`Refusing a v2 message for ${evt.dest}, whose latest accepted event is v1.`);
|
|
521
|
+
}
|
|
487
522
|
if (route.kind !== "direct") {
|
|
488
523
|
return yield* this.prepareForwardedTarget(hab, evt.dest, evt.topic, evt, endpoint);
|
|
489
524
|
}
|
|
525
|
+
if (wireNeedsV2(evt.msg) && knownV1Peer(this.hby, endpoint.eid)) {
|
|
526
|
+
throw new PeerVersionError(`Refusing a v2 message for endpoint ${endpoint.eid}, whose latest accepted event is v1.`);
|
|
527
|
+
}
|
|
490
528
|
return remoteCesrTarget(endpoint, evt.msg);
|
|
491
529
|
}
|
|
492
530
|
/**
|
|
@@ -516,6 +554,10 @@ export class Poster {
|
|
|
516
554
|
else {
|
|
517
555
|
msgs.push(evt.msg);
|
|
518
556
|
}
|
|
557
|
+
// The provider reads the envelope, so it must read v2 whenever the envelope or child is v2.
|
|
558
|
+
if ((hab.kelVersion.major >= 2 || msgs.some(wireNeedsV2)) && knownV1Peer(this.hby, endpoint.eid)) {
|
|
559
|
+
throw new PeerVersionError(`Refusing a v2 envelope for provider ${endpoint.eid}, whose latest accepted event is v1.`);
|
|
560
|
+
}
|
|
519
561
|
const forwarded = [];
|
|
520
562
|
for (const message of msgs)
|
|
521
563
|
forwarded.push(yield* buildForwardedDelivery(hab, { recipient, topic, message }));
|
|
@@ -142,8 +142,11 @@ export class Exchanger {
|
|
|
142
142
|
}
|
|
143
143
|
/** Resolve the configured body-version route owner without dispatching or changing evidence. */
|
|
144
144
|
handlerFor(serder) {
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
return this.routeHandler(serder.route ?? "", serder.pvrsn.major);
|
|
146
|
+
}
|
|
147
|
+
/** Resolve the owner one route and protocol major would dispatch to, without a message body. */
|
|
148
|
+
routeHandler(route, major) {
|
|
149
|
+
return this.versionedRoutes.get(route)?.get(major) ?? this.routes.get(route);
|
|
147
150
|
}
|
|
148
151
|
/**
|
|
149
152
|
* Verify, persist, and dispatch one inbound `exn`.
|
|
@@ -209,7 +212,7 @@ export class Exchanger {
|
|
|
209
212
|
const nests = args.nests?.length
|
|
210
213
|
? yield* abortable((signal) => prepareNests(args.nests, signal))
|
|
211
214
|
: undefined;
|
|
212
|
-
const result = this.hby.db.
|
|
215
|
+
const result = this.hby.db.transaction(() => {
|
|
213
216
|
if (selectedGrant) {
|
|
214
217
|
const db = this.hby.db;
|
|
215
218
|
const stored = selectedGrant === "proof" ? db.epse.get([said]) : db.exns.get([said]) ?? db.epse.get([said]);
|
|
@@ -268,9 +271,7 @@ export class Exchanger {
|
|
|
268
271
|
return admission;
|
|
269
272
|
});
|
|
270
273
|
// Durable completion joins the actual LMDB flush; no external file lifetime remains.
|
|
271
|
-
yield*
|
|
272
|
-
await this.hby.db.env.flushed;
|
|
273
|
-
});
|
|
274
|
+
yield* this.hby.db.flushed();
|
|
274
275
|
if (!result)
|
|
275
276
|
return undefined;
|
|
276
277
|
for (const cue of result.cues)
|
|
@@ -448,7 +449,7 @@ export class Exchanger {
|
|
|
448
449
|
const table = schema ? this.hby.db.emse : this.hby.db.epse;
|
|
449
450
|
let end;
|
|
450
451
|
// Capture a finite key horizon without retaining bodies or holding a cursor across mutations.
|
|
451
|
-
for (const key of table.sdb
|
|
452
|
+
for (const key of this.hby.db.scanKeys(table.sdb, { reverse: true, limit: 1 })) {
|
|
452
453
|
end = b(`${textDecoder.decode(key)}\0`);
|
|
453
454
|
}
|
|
454
455
|
if (!end) {
|
|
@@ -457,7 +458,7 @@ export class Exchanger {
|
|
|
457
458
|
let start;
|
|
458
459
|
for (;;) {
|
|
459
460
|
let physical;
|
|
460
|
-
for (const key of table.sdb
|
|
461
|
+
for (const key of this.hby.db.scanKeys(table.sdb, { start, end, limit: 1 })) {
|
|
461
462
|
physical = textDecoder.decode(key);
|
|
462
463
|
}
|
|
463
464
|
if (!physical)
|
|
@@ -1291,7 +1292,7 @@ export class Exchanger {
|
|
|
1291
1292
|
/** Decide expiry and remove pending rows in the same native transaction. */
|
|
1292
1293
|
*expirePending(serder) {
|
|
1293
1294
|
const said = serder.said;
|
|
1294
|
-
const result = this.hby.db.
|
|
1295
|
+
const result = this.hby.db.transaction(() => {
|
|
1295
1296
|
const status = this.escrowStatus(said);
|
|
1296
1297
|
if (status.kind === "accepted") {
|
|
1297
1298
|
this.clearPending(serder);
|
|
@@ -1301,9 +1302,7 @@ export class Exchanger {
|
|
|
1301
1302
|
return undefined;
|
|
1302
1303
|
return this.retireCurrent(serder, this.expiryReport(status));
|
|
1303
1304
|
});
|
|
1304
|
-
yield*
|
|
1305
|
-
await this.hby.db.env.flushed;
|
|
1306
|
-
});
|
|
1305
|
+
yield* this.hby.db.flushed();
|
|
1307
1306
|
return result;
|
|
1308
1307
|
}
|
|
1309
1308
|
/** Clear only dependency markers, keeping all accepted evidence and metadata intact. */
|