keri-ts 2.2.1 → 2.2.3
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/_dnt.polyfills.js +1 -127
- package/esm/keri/npm/src/acdc/dhking.js +6 -1
- package/esm/keri/npm/src/acdc/salting.js +18 -0
- package/esm/keri/npm/src/acdc/scheming.js +31 -18
- package/esm/keri/npm/src/acdc/unblinding.js +112 -6
- package/esm/keri/npm/src/app/delivery/forwarding.js +9 -0
- package/esm/keri/npm/src/app/delivery/respondant.js +21 -0
- package/esm/keri/npm/src/app/identity/delegating.js +2 -5
- package/esm/keri/npm/src/app/identity/habbing.js +52 -6
- package/esm/keri/npm/src/app/identity/keeping.js +38 -1
- package/esm/keri/npm/src/app/ipex/group-issuance.js +9 -6
- package/esm/keri/npm/src/app/ipex/ipex-building.js +2 -1
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +32 -8
- package/esm/keri/npm/src/app/ipex/issuance.js +142 -43
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +50 -0
- package/esm/keri/npm/src/app/ipex/presenting.js +36 -1
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +150 -36
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +37 -9
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +9 -3
- package/esm/keri/npm/src/app/multisig/counseling.js +76 -3
- package/esm/keri/npm/src/app/multisig/event-forwarding.js +27 -0
- package/esm/keri/npm/src/app/multisig/group-members.js +22 -0
- package/esm/keri/npm/src/app/multisig/grouping.js +120 -1
- package/esm/keri/npm/src/app/multisig/proposals.js +1 -1
- package/esm/keri/npm/src/app/multisig/replace-member.js +34 -0
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +43 -23
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +59 -25
- package/esm/keri/npm/src/app/runtime/operation-owners.js +1 -0
- package/esm/keri/npm/src/app/runtime/signer-conditions.js +51 -0
- package/esm/keri/npm/src/app/transport/cesr-egress.js +6 -0
- package/esm/keri/npm/src/core/errors.js +91 -3
- package/esm/keri/npm/src/core/json-schema-meta.js +688 -0
- package/esm/keri/npm/src/core/json-schema.js +147 -0
- package/esm/keri/npm/src/core/kel/eventing.js +20 -2
- package/esm/keri/npm/src/core/scheming.js +27 -26
- package/esm/keri/npm/src/db/replay/tel-replay.js +6 -0
- package/esm/keri/npm/src/db/store-lock.js +12 -0
- package/esm/keri/npm/src/runtime/index.js +9 -7
- package/esm/keri/npm/src/runtime/web/output-barrier.js +9 -0
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +2 -2
- package/types/_dnt.polyfills.d.ts +0 -99
- package/types/_dnt.polyfills.d.ts.map +1 -1
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/salting.d.ts +2 -0
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/scheming.d.ts +2 -0
- package/types/acdc/scheming.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +16 -5
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +1 -0
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/delivery/respondant.d.ts +7 -0
- package/types/app/delivery/respondant.d.ts.map +1 -1
- package/types/app/identity/delegating.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +21 -1
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +18 -0
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/ipex/group-issuance.d.ts.map +1 -1
- package/types/app/ipex/ipex-building.d.ts.map +1 -1
- package/types/app/ipex/ipex-discloser.d.ts +31 -19
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +46 -10
- package/types/app/ipex/issuance.d.ts.map +1 -1
- package/types/app/ipex/pending-presentations.d.ts +62 -1
- package/types/app/ipex/pending-presentations.d.ts.map +1 -1
- package/types/app/ipex/presenting.d.ts +39 -4
- package/types/app/ipex/presenting.d.ts.map +1 -1
- package/types/app/ipex/unblinding-workflow.d.ts +51 -12
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
- package/types/app/mailbox/mailbox-poller.d.ts +11 -2
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +4 -1
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +22 -1
- package/types/app/multisig/counseling.d.ts.map +1 -1
- package/types/app/multisig/event-forwarding.d.ts +6 -0
- package/types/app/multisig/event-forwarding.d.ts.map +1 -0
- package/types/app/multisig/group-members.d.ts +6 -0
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts +26 -0
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/proposals.d.ts +3 -2
- package/types/app/multisig/proposals.d.ts.map +1 -1
- package/types/app/multisig/proposing.d.ts +8 -1
- package/types/app/multisig/proposing.d.ts.map +1 -1
- package/types/app/multisig/replace-member.d.ts +30 -0
- package/types/app/multisig/replace-member.d.ts.map +1 -0
- package/types/app/runtime/agent-runtime.d.ts +3 -0
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/ipex-auxiliaries.d.ts +8 -3
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -1
- package/types/app/runtime/operation-owners.d.ts.map +1 -1
- package/types/app/runtime/signer-conditions.d.ts +32 -0
- package/types/app/runtime/signer-conditions.d.ts.map +1 -0
- package/types/app/transport/cesr-egress.d.ts +1 -0
- package/types/app/transport/cesr-egress.d.ts.map +1 -1
- package/types/core/errors.d.ts +20 -1
- package/types/core/errors.d.ts.map +1 -1
- package/types/core/json-schema-meta.d.ts +19 -0
- package/types/core/json-schema-meta.d.ts.map +1 -0
- package/types/core/json-schema.d.ts +29 -0
- package/types/core/json-schema.d.ts.map +1 -0
- package/types/core/scheming.d.ts +1 -0
- package/types/core/scheming.d.ts.map +1 -1
- package/types/db/replay/tel-replay.d.ts +1 -0
- package/types/db/replay/tel-replay.d.ts.map +1 -1
- package/types/db/store-lock.d.ts +2 -0
- package/types/db/store-lock.d.ts.map +1 -1
- package/types/runtime/index.d.ts +11 -9
- package/types/runtime/index.d.ts.map +1 -1
- package/types/runtime/web/output-barrier.d.ts +1 -0
- package/types/runtime/web/output-barrier.d.ts.map +1 -1
- package/types/version.d.ts +2 -2
|
@@ -30,7 +30,7 @@ import { Keeper, PreSit } from "../../db/keeping.js";
|
|
|
30
30
|
import { makeNowIso8601 } from "../../time/mod.js";
|
|
31
31
|
import { oobiReplyMessages } from "../bootstrap/oobi-replies.js";
|
|
32
32
|
import { loadStoredReply } from "../delivery/stored-replies.js";
|
|
33
|
-
import { acceptedGroupMembers, latestGroupContribution } from "../multisig/group-members.js";
|
|
33
|
+
import { acceptedGroupMembers, isAcceptedGroupMember, latestGroupContribution } from "../multisig/group-members.js";
|
|
34
34
|
import { branToSaltQb64, encodeHugeNumber, ensureKeeperCryptoReady, Manager, normalizeSaltQb64, saltySigner, } from "./keeping.js";
|
|
35
35
|
/** Reserved alias for the local signatory habitat record. */
|
|
36
36
|
export const SIGNER = "__signatory__";
|
|
@@ -1547,6 +1547,10 @@ export class Habery {
|
|
|
1547
1547
|
* - this is the TypeScript analogue of `Habery.joinGroupHab()`
|
|
1548
1548
|
* - it does not fabricate key state; accepted KEL state must still arrive
|
|
1549
1549
|
* through `Kevery.processEvent()`
|
|
1550
|
+
* - KERIpy overwrites the recorded member unconditionally. Here a recorded member that is still a
|
|
1551
|
+
* signing or rotation member of the group's accepted key state is kept (a Habery holding two valid
|
|
1552
|
+
* members must not switch which one signs), and one that is in neither list is replaced by `mhab`.
|
|
1553
|
+
* Replacing creates no event and signs nothing; membership itself changes only by rotation.
|
|
1550
1554
|
*/
|
|
1551
1555
|
joinGroupHab(pre, group, mhab, smids, rmids, ns) {
|
|
1552
1556
|
if (!pre) {
|
|
@@ -1569,18 +1573,54 @@ export class Habery {
|
|
|
1569
1573
|
throw new ValidationError(`Group alias ${group} is already bound to ${existingName}.`);
|
|
1570
1574
|
}
|
|
1571
1575
|
const existingRecord = this.db.getHab(pre);
|
|
1572
|
-
|
|
1573
|
-
|
|
1576
|
+
const replacing = !!existingRecord?.mid && existingRecord.mid !== mhab.pre;
|
|
1577
|
+
if (replacing) {
|
|
1578
|
+
// Membership is decided by accepted key state, never by the caller's lists or a proposal.
|
|
1579
|
+
if (!this.db.getKever(pre)) {
|
|
1580
|
+
throw new ValidationError(`Group prefix ${pre} is already joined as ${existingRecord.name}; whether member ${existingRecord.mid} `
|
|
1581
|
+
+ `is still a member cannot be judged until the group has accepted key state.`);
|
|
1582
|
+
}
|
|
1583
|
+
if (isAcceptedGroupMember(this.db, pre, existingRecord.mid)) {
|
|
1584
|
+
throw new ValidationError(`Group prefix ${pre} is already joined as ${existingRecord.name} by member ${existingRecord.mid}, `
|
|
1585
|
+
+ `which is still a member in its accepted key state.`);
|
|
1586
|
+
}
|
|
1587
|
+
if (!isAcceptedGroupMember(this.db, pre, mhab.pre)) {
|
|
1588
|
+
throw new ValidationError(`Local member identifier ${mhab.pre} is not a member in the accepted key state of group ${pre}.`);
|
|
1589
|
+
}
|
|
1574
1590
|
}
|
|
1575
1591
|
const hab = this.habs.get(pre) ?? new Hab(group, this.db, this.ks, this.mgr, this.cf, this.rtr, this.rvy, this.kevery, ns, pre);
|
|
1576
|
-
this.persistGroupHabRecord(pre, group, mhab.pre, signingMembers, rotationMembers, ns);
|
|
1592
|
+
this.persistGroupHabRecord(pre, group, mhab.pre, signingMembers, rotationMembers, ns, existingRecord ?? undefined);
|
|
1577
1593
|
this.db.groups.add(pre);
|
|
1578
1594
|
if (hab.accepted) {
|
|
1579
1595
|
this.markAcceptedGroupHab(pre);
|
|
1580
1596
|
}
|
|
1581
1597
|
this.habs.set(pre, hab);
|
|
1598
|
+
if (replacing && !this.readonly) {
|
|
1599
|
+
// The stored lists must follow accepted state, which the old member's record kept from being committed.
|
|
1600
|
+
this.recoverAcceptedGroupMembers(pre);
|
|
1601
|
+
this.retireGroupMemberState(pre, existingRecord.mid);
|
|
1602
|
+
}
|
|
1582
1603
|
return hab;
|
|
1583
1604
|
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Drop the durable rows that address `group` to its former local member `old`: registry root copies
|
|
1607
|
+
* escrowed for `old` (the holder check now refuses them, so a retry would only fail) and the markers
|
|
1608
|
+
* of copies sent to `old` as a recipient. Rows keyed by the group itself stay. Live operations are
|
|
1609
|
+
* owned by the runtime and are not reached from here.
|
|
1610
|
+
*/
|
|
1611
|
+
retireGroupMemberState(group, old) {
|
|
1612
|
+
for (const [keys] of [...this.db.rrpe.getTopItemIter()]) {
|
|
1613
|
+
const serder = this.db.exns.get([keys.join(".")]);
|
|
1614
|
+
const data = serder?.sad?.a;
|
|
1615
|
+
if (serder?.route === "/registry/root" && serder.sad?.ri === old && data?.gid === group) {
|
|
1616
|
+
this.db.rrpe.rem(keys);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
for (const [keys] of [...this.db.rrsn.getTopItemIter([group])]) {
|
|
1620
|
+
if (keys[2] === old)
|
|
1621
|
+
this.db.rrsn.rem(keys);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1584
1624
|
/** Commit accepted group member metadata after the corresponding group event is accepted. */
|
|
1585
1625
|
commitGroupHabMembers(pre, smids, rmids = smids) {
|
|
1586
1626
|
const record = this.db.getHab(pre);
|
|
@@ -1774,12 +1814,15 @@ export class Habery {
|
|
|
1774
1814
|
return sigers;
|
|
1775
1815
|
}
|
|
1776
1816
|
/** Persist group habitat metadata and member ordinals. */
|
|
1777
|
-
persistGroupHabRecord(pre, group, mid, smids, rmids, ns) {
|
|
1817
|
+
persistGroupHabRecord(pre, group, mid, smids, rmids, ns, replaced) {
|
|
1778
1818
|
this.db.pinHab(pre, new HabitatRecord({
|
|
1779
1819
|
hid: pre,
|
|
1780
1820
|
name: group,
|
|
1781
1821
|
domain: ns,
|
|
1782
1822
|
mid,
|
|
1823
|
+
// A rejoin or replacement keeps the rest of the record.
|
|
1824
|
+
sid: replaced?.sid,
|
|
1825
|
+
watchers: replaced?.watchers ? [...replaced.watchers] : undefined,
|
|
1783
1826
|
smids: [...smids],
|
|
1784
1827
|
rmids: [...rmids],
|
|
1785
1828
|
}));
|
|
@@ -1878,7 +1921,7 @@ export function branToSeedAeid(bran) {
|
|
|
1878
1921
|
* durable DB state rather than depending on process-local creation history.
|
|
1879
1922
|
*/
|
|
1880
1923
|
export function* openHabery(args) {
|
|
1881
|
-
const { name, base = "", temp = false, headDirPath, compat = false, readonly = false, cf: providedCf, createConfig, skipConfig = false, skipSignator = false, opener, stores, bran, seed, aeid, salt, algo, } = args;
|
|
1924
|
+
const { name, base = "", temp = false, headDirPath, compat = false, readonly = false, cf: providedCf, createConfig, skipConfig = false, skipSignator = false, opener, stores, bran, seed, aeid, salt, algo, heldSigners, } = args;
|
|
1882
1925
|
let db;
|
|
1883
1926
|
let ks;
|
|
1884
1927
|
let cf;
|
|
@@ -1921,6 +1964,9 @@ export function* openHabery(args) {
|
|
|
1921
1964
|
algo,
|
|
1922
1965
|
salt: normalizeSaltQb64(salt),
|
|
1923
1966
|
});
|
|
1967
|
+
// Before the Habery exists, so before any runtime built on it can sign. A bad entry fails the open.
|
|
1968
|
+
if (heldSigners)
|
|
1969
|
+
mgr.hold(heldSigners);
|
|
1924
1970
|
const hby = new Habery(name, base, temp, headDirPath, compat, readonly, db, ks, mgr, cf, skipSignator, opener, !!stores);
|
|
1925
1971
|
delivered = true;
|
|
1926
1972
|
return hby;
|
|
@@ -193,6 +193,25 @@ function resolveSuiteCodes(codes, count, code, { label, allowZero = false, }) {
|
|
|
193
193
|
function emptyLot(dt = "") {
|
|
194
194
|
return new PubLot({ pubs: [], ridx: 0, kidx: 0, dt });
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* No private key for `pub`: neither held (`Manager.hold`) nor stored in the Keeper. A custodial host releases
|
|
198
|
+
* its signers on purpose, so a background loop that needs one treats this as a condition to wait out, not as a
|
|
199
|
+
* failure of the runtime. The message is the one the Keeper has always used.
|
|
200
|
+
*/
|
|
201
|
+
export class SignerNotHeldError extends Error {
|
|
202
|
+
constructor(pub) {
|
|
203
|
+
super(`Missing prikey in db for pubkey=${pub}`);
|
|
204
|
+
/** The qualified public key whose private key is missing. */
|
|
205
|
+
Object.defineProperty(this, "pub", {
|
|
206
|
+
enumerable: true,
|
|
207
|
+
configurable: true,
|
|
208
|
+
writable: true,
|
|
209
|
+
value: void 0
|
|
210
|
+
});
|
|
211
|
+
this.name = "SignerNotHeldError";
|
|
212
|
+
this.pub = pub;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
196
215
|
/**
|
|
197
216
|
* Key-management coordinator backed by `Keeper` state.
|
|
198
217
|
*
|
|
@@ -251,6 +270,13 @@ export class Manager {
|
|
|
251
270
|
writable: true,
|
|
252
271
|
value: new Map()
|
|
253
272
|
});
|
|
273
|
+
/** Told after `hold()` borrows signers; a loop that waited for a missing signer resumes on it. */
|
|
274
|
+
Object.defineProperty(this, "holdListeners", {
|
|
275
|
+
enumerable: true,
|
|
276
|
+
configurable: true,
|
|
277
|
+
writable: true,
|
|
278
|
+
value: new Set()
|
|
279
|
+
});
|
|
254
280
|
const { ks, seed = "", aeid = "", pidx = 0, algo = Algos.salty, salt = randomSaltQb64(), tier = Tiers.low, } = args;
|
|
255
281
|
this._ks = ks;
|
|
256
282
|
this._seed = seed;
|
|
@@ -496,6 +522,17 @@ export class Manager {
|
|
|
496
522
|
}
|
|
497
523
|
for (const [pub, signer] of batch)
|
|
498
524
|
this.held.set(pub, signer);
|
|
525
|
+
for (const listener of [...this.holdListeners])
|
|
526
|
+
listener();
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Be told after each `hold()`, so a background loop whose signer was released can resume at once.
|
|
530
|
+
*
|
|
531
|
+
* @returns A function that stops the notifications.
|
|
532
|
+
*/
|
|
533
|
+
onHold(listener) {
|
|
534
|
+
this.holdListeners.add(listener);
|
|
535
|
+
return () => this.holdListeners.delete(listener);
|
|
499
536
|
}
|
|
500
537
|
/**
|
|
501
538
|
* Drop held signer references: all of them, or only those for `pubs`.
|
|
@@ -519,7 +556,7 @@ export class Manager {
|
|
|
519
556
|
return held;
|
|
520
557
|
const signer = this.ks.pris.get(pub, this.signerDecrypter());
|
|
521
558
|
if (!signer) {
|
|
522
|
-
throw new
|
|
559
|
+
throw new SignerNotHeldError(pub);
|
|
523
560
|
}
|
|
524
561
|
return signer;
|
|
525
562
|
}
|
|
@@ -50,7 +50,7 @@ import { proposeRegistryEvent } from "../multisig/proposing.js";
|
|
|
50
50
|
import { envelopeFromFrame } from "../protocol/parsering.js";
|
|
51
51
|
import { exchangeWire } from "./ipex-discloser.js";
|
|
52
52
|
import { acceptedRegistryEvidence, isIssuanceOrigin } from "./ipex-node-authentication.js";
|
|
53
|
-
import { ISSUANCE_QUERY_RESEND_MS, } from "./issuance.js";
|
|
53
|
+
import { ISSUANCE_QUERY_RESEND_MS, partyComplete, } from "./issuance.js";
|
|
54
54
|
/** Milliseconds between scans of the stored wrappers for the final proof. */
|
|
55
55
|
const PROOF_SCAN_MS = 1000;
|
|
56
56
|
/** How many of the group's latest accepted events {@link deriveGroupCustody} considers as coordinators. */
|
|
@@ -327,7 +327,7 @@ function* proposalOf(ops, grant, proposal) {
|
|
|
327
327
|
*/
|
|
328
328
|
export function* approveGroupIssuance(ops, grantSaid, options = {}) {
|
|
329
329
|
const { deps, discloser } = ops;
|
|
330
|
-
if (ops.
|
|
330
|
+
if (ops.liveOf(grantSaid, "issuer") || partyComplete(deps, grantSaid, "issuer")) {
|
|
331
331
|
throw new ValidationError("Issuance was already begun for this Grant");
|
|
332
332
|
}
|
|
333
333
|
const { child } = yield* proposalOf(ops, grantSaid, options.proposal);
|
|
@@ -413,7 +413,7 @@ function pendingGrant(deps, said) {
|
|
|
413
413
|
*/
|
|
414
414
|
export function approveGroupIssuanceExchange(ops, said, coordinator) {
|
|
415
415
|
const { deps } = ops;
|
|
416
|
-
if (ops.
|
|
416
|
+
if (ops.liveOf(said, "holder") || partyComplete(deps, said, "holder")) {
|
|
417
417
|
throw new ValidationError("Issuance was already begun for this Grant");
|
|
418
418
|
}
|
|
419
419
|
const { grant, nodes } = pendingGrant(deps, said);
|
|
@@ -430,7 +430,7 @@ export function approveGroupIssuanceExchange(ops, said, coordinator) {
|
|
|
430
430
|
*/
|
|
431
431
|
export function* followGroupIssuanceProof(ops, said, options = {}) {
|
|
432
432
|
const { deps } = ops;
|
|
433
|
-
if (ops.
|
|
433
|
+
if (ops.liveOf(said, "issuer") || partyComplete(deps, said, "issuer")) {
|
|
434
434
|
throw new ValidationError("Issuance was already begun for this Grant");
|
|
435
435
|
}
|
|
436
436
|
const { grant, nodes } = pendingGrant(deps, said);
|
|
@@ -647,7 +647,7 @@ export function registerGroupRoutes(ops) {
|
|
|
647
647
|
acceptsNests: true,
|
|
648
648
|
verify: (args) => structural(deps.hby, args.serder, args.nests ?? []),
|
|
649
649
|
handle: (args) => {
|
|
650
|
-
for (const op of ops.
|
|
650
|
+
for (const op of ops.liveOps()) {
|
|
651
651
|
if (op.group?.accepts(args.serder, args.nests)) {
|
|
652
652
|
op.group.pending.set(args.serder.said, { wrapper: args.serder, child: args.nests[0] });
|
|
653
653
|
}
|
|
@@ -761,7 +761,10 @@ export function* stepGroup(ops, op) {
|
|
|
761
761
|
for (const peer of party.others())
|
|
762
762
|
yield* send(peer, msg);
|
|
763
763
|
};
|
|
764
|
-
|
|
764
|
+
// The accepted Grant is one fact of the Habery. A holder in the issuer's own Habery is not finished by the issuer
|
|
765
|
+
// finishing it: its key exchange completes, and its proof goes to its members, in its own steps.
|
|
766
|
+
const issuerHere = op.role === "holder" && deps.hby.habs.has(g.grant.pre);
|
|
767
|
+
if (deps.exc.complete(grant) && (!issuerHere || g.mode === "resumed"))
|
|
765
768
|
return true;
|
|
766
769
|
if (op.expires === null) {
|
|
767
770
|
const deadline = deps.exc.exchangeDeadline(g.grant);
|
|
@@ -40,7 +40,8 @@ export function ipexExnBody(sender, route, nodes, options) {
|
|
|
40
40
|
throw new Error("IPEX reply parties do not match the prior.");
|
|
41
41
|
}
|
|
42
42
|
if (verb === "spurn" && prior.route === "/ipex/grant" && prior.sad.p) {
|
|
43
|
-
throw new Error("Only
|
|
43
|
+
throw new Error("Only a flow-starting Grant (one with no prior) may be spurned; a Grant that answers an Apply or Agree has no "
|
|
44
|
+
+ "rejection message, so do not Admit it and keep it as evidence.");
|
|
44
45
|
}
|
|
45
46
|
recipient = prior.pre;
|
|
46
47
|
xid = prior.ilk === "xip" ? prior.said : prior.sad.x;
|
|
@@ -25,6 +25,12 @@ import { ipexExnBody, prepareIpexExn } from "./ipex-building.js";
|
|
|
25
25
|
import { walkIpexGraph } from "./ipex-graph.js";
|
|
26
26
|
import { acceptedRegistryEvidence, authenticateIpexNode, issuedNodeVerdict } from "./ipex-node-authentication.js";
|
|
27
27
|
import { ipexGrantWalk } from "./ipex-v2.js";
|
|
28
|
+
/** Refuse the builder's `prior` where the disclosure API names the prior exchange by `priorSaid`. */
|
|
29
|
+
function refuseBuilderPrior(options) {
|
|
30
|
+
if (options.prior !== undefined) {
|
|
31
|
+
throw new ValidationError("Name the prior exchange with priorSaid; prior is not a disclosure option.");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
28
34
|
/** Reuse a matching accepted KEL seal, or author once and gather real witness receipts. */
|
|
29
35
|
export function* anchorExchange(hby, receiptor, hab, body, auths) {
|
|
30
36
|
if (receiptor.hby !== hby)
|
|
@@ -61,8 +67,10 @@ export function exchangeWire(hab, exn, nested = new Uint8Array(), source, gvrsn
|
|
|
61
67
|
return concatBytes(Counter.makeGVC(gvrsn), exn.raw, Counter.enclose({ qb64: concatBytes(proof, nested), version: gvrsn }));
|
|
62
68
|
}
|
|
63
69
|
/**
|
|
64
|
-
* Re-encode a signed exchange from its retained, authenticated factors: the
|
|
65
|
-
* from the stored signatures or source seal and the stored nests.
|
|
70
|
+
* Re-encode a signed exchange from its retained, authenticated factors: the exact bytes its signer presented,
|
|
71
|
+
* rebuilt from the stored signatures or source seal and the stored nests (`runtime.exc.retainedInput(said)` supplies
|
|
72
|
+
* them). Signs nothing, so it needs no keys. Public: a host that must hand the presented bytes to a third party
|
|
73
|
+
* reads them with this instead of re-signing.
|
|
66
74
|
*/
|
|
67
75
|
export function* storedExchangeWire(retained, gvrsn = Vrsn_2_0) {
|
|
68
76
|
const nests = [...retained.nests ?? []];
|
|
@@ -367,22 +375,30 @@ export class IpexDiscloser {
|
|
|
367
375
|
}
|
|
368
376
|
/** Create an application request; no credential-use approval is invented for negotiation. */
|
|
369
377
|
*prepareApply(sender, options) {
|
|
370
|
-
const hab = this.signer(sender, options.gvrsn ?? Vrsn_2_0), exn =
|
|
378
|
+
const hab = this.signer(sender, options.gvrsn ?? Vrsn_2_0), exn = this.applyBody(sender, options);
|
|
371
379
|
return {
|
|
372
380
|
exn,
|
|
373
381
|
wire: exchangeWire(hab, exn, undefined, undefined, options.gvrsn ?? Vrsn_2_0),
|
|
374
382
|
support: new Uint8Array(),
|
|
375
383
|
};
|
|
376
384
|
}
|
|
385
|
+
/** The unsigned Apply body, for a sender that signs elsewhere (a group's members each sign their share). */
|
|
386
|
+
applyBody(sender, options) {
|
|
387
|
+
return ipexExnBody(sender, "/ipex/apply", [], options);
|
|
388
|
+
}
|
|
377
389
|
/** Accept or decline the exact authenticated predecessor and its negotiated anchoring choice. */
|
|
378
390
|
*prepareReply(sender, verb, priorSaid, message = "", recipient, auths) {
|
|
379
|
-
const hab = this.signer(sender),
|
|
380
|
-
const exn = ipexExnBody(sender, `/ipex/${verb}`, [], { message, prior, recipient });
|
|
391
|
+
const hab = this.signer(sender), exn = yield* this.replyBody(sender, verb, priorSaid, message, recipient);
|
|
381
392
|
const source = verb === "agree" && exn.sad.a.ax?.[0]
|
|
382
393
|
? yield* anchorExchange(this.hby, this.receiptor, hab, exn, auths)
|
|
383
394
|
: undefined;
|
|
384
395
|
return { exn, wire: exchangeWire(hab, exn, undefined, source), support: source ? this.kel(hab) : new Uint8Array() };
|
|
385
396
|
}
|
|
397
|
+
/** The unsigned Agree or Spurn body answering an accepted, authenticated predecessor. */
|
|
398
|
+
*replyBody(sender, verb, priorSaid, message = "", recipient) {
|
|
399
|
+
const prior = yield* this.prior(priorSaid);
|
|
400
|
+
return ipexExnBody(sender, `/ipex/${verb}`, [], { message, prior, recipient });
|
|
401
|
+
}
|
|
386
402
|
/** Extend the frozen Grant's exact origin proof; ordinary intake still authenticates the supplied presentation evidence. */
|
|
387
403
|
*attachPresentationProof(prepared, opening, source) {
|
|
388
404
|
const frames = parseBytes(prepared.raw), node = frames[0].body;
|
|
@@ -463,15 +479,22 @@ export class IpexDiscloser {
|
|
|
463
479
|
}
|
|
464
480
|
/** Offer only the selected commitment/view without issuer proof attachments. */
|
|
465
481
|
*prepareOffer(sender, selection, options) {
|
|
466
|
-
const hab = this.signer(sender, options.gvrsn ?? Vrsn_2_0)
|
|
467
|
-
const
|
|
468
|
-
const [exn, nests] = yield* prepareIpexExn(sender, "/ipex/offer", raw, { ...options, prior });
|
|
482
|
+
const hab = this.signer(sender, options.gvrsn ?? Vrsn_2_0);
|
|
483
|
+
const { exn, nests } = yield* this.prepareOfferBody(sender, selection, options);
|
|
469
484
|
return {
|
|
470
485
|
exn,
|
|
471
486
|
wire: exchangeWire(hab, exn, nests, undefined, options.gvrsn ?? Vrsn_2_0),
|
|
472
487
|
support: new Uint8Array(),
|
|
473
488
|
};
|
|
474
489
|
}
|
|
490
|
+
/** Freeze an Offer body and its nested evidence without signing, for a sender that signs elsewhere. */
|
|
491
|
+
*prepareOfferBody(sender, selection, options) {
|
|
492
|
+
refuseBuilderPrior(options);
|
|
493
|
+
const prior = yield* this.prior(options.priorSaid);
|
|
494
|
+
const raw = yield* this.selected(selection, true);
|
|
495
|
+
const [exn, nests] = yield* prepareIpexExn(sender, "/ipex/offer", raw, { ...options, prior });
|
|
496
|
+
return { exn, nests };
|
|
497
|
+
}
|
|
475
498
|
/** Select and authorize once before freezing a Grant body, without signing or committing group approval. */
|
|
476
499
|
*prepareGrantBody(sender, selection, options) {
|
|
477
500
|
return yield* this.grantBody(sender, selection, options, false);
|
|
@@ -482,6 +505,7 @@ export class IpexDiscloser {
|
|
|
482
505
|
}
|
|
483
506
|
/** Both public preparation profiles preserve the same selected view and body construction. */
|
|
484
507
|
*grantBody(sender, selection, options, pendingPresentation) {
|
|
508
|
+
refuseBuilderPrior(options);
|
|
485
509
|
const hab = this.hby.habs.get(sender);
|
|
486
510
|
if (!hab?.kever || (options.gvrsn ?? Vrsn_2_0).major < 2) {
|
|
487
511
|
throw new ValidationError("ACDC v2 Grant preparation requires an accepted local sender and attachment genus 2.");
|