@sanning/proof 0.18.0 → 0.19.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 -2
- package/dist/case-file.js +2 -1
- package/dist/case.d.ts +20 -4
- package/dist/case.js +56 -14
- package/dist/chain.d.ts +28 -0
- package/dist/chain.js +61 -4
- package/dist/cli.js +227 -34
- package/dist/declaration.d.ts +43 -4
- package/dist/declaration.js +198 -14
- package/dist/environment.d.ts +42 -0
- package/dist/environment.js +136 -0
- package/dist/evidence.d.ts +6 -0
- package/dist/evidence.js +32 -1
- package/dist/exit-codes.d.ts +18 -0
- package/dist/exit-codes.js +25 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +21 -5
- package/dist/run-adapters.d.ts +10 -0
- package/dist/run-adapters.js +35 -0
- package/dist/run.d.ts +209 -0
- package/dist/run.js +1405 -0
- package/dist/verifier.d.ts +43 -0
- package/dist/verifier.js +104 -1
- package/dist/witness-lookup.d.ts +13 -1
- package/dist/witness-lookup.js +12 -2
- package/package.json +1 -1
- package/src/case-file.ts +2 -1
- package/src/case.ts +63 -16
- package/src/chain.ts +81 -4
- package/src/cli.ts +260 -29
- package/src/declaration.ts +242 -12
- package/src/environment.ts +145 -0
- package/src/evidence.ts +29 -1
- package/src/exit-codes.ts +28 -0
- package/src/index.ts +31 -1
- package/src/run-adapters.ts +44 -0
- package/src/run.ts +1598 -0
- package/src/verifier.ts +94 -1
- package/src/witness-lookup.ts +18 -1
- package/vectors/README.md +37 -0
- package/vectors/sandbox-evidence.json +3186 -0
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ npx @sanning/proof verify trace-bundle.json
|
|
|
63
63
|
npx @sanning/proof verify trace-bundle.json --gateways https://arweave.net,https://permagate.io
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
It prints a per-event verdict, then the PACK verdict, and exits by the one table every verb uses: `0` verified · `1` a real failure (bad signature / tamper / broken inclusion) · `2` usage or malformed · `3` undetermined (`partial`, `pending`, `incomplete` or `not_found`: a gateway that could not be reached, a stamp no witness published, or a pack the grading rule calls `incomplete`) · `4` mixed
|
|
66
|
+
It prints a per-event verdict, then the PACK verdict, and exits by the one table every verb uses: `0` verified · `1` a real failure (bad signature / tamper / broken inclusion) · `2` usage or malformed · `3` undetermined (`partial`, `pending`, `incomplete` or `not_found`: a gateway that could not be reached, a stamp no witness published, or a pack the grading rule calls `incomplete`) · `4` mixed · `5` sandbox: every check passed, and a record is signed `environment: "dev"`. Since 0.13.0 `partial` exits `3` whatever caused it; it exited `0` unless a gateway was unreachable. A pack holding a record signed `dev` never prints `VERIFIED`: the headline is `SANDBOX`, the line `Sandbox evidence (environment dev): not production evidence` follows it, `--json` carries `environment_marker` (`dev` or `mixed`), and a run that would exit `0` exits `5`. So does a pack whose records are all `dev` whose only unchecked witness, with `--gateways`, is `simulated`; a failure is still `1`, and a production or mixed pack with a simulated witness is still `3`. A production pack prints what it did before. The headline is the pack verdict, the same one `gradePack` gives the browser verify page: a record nobody hashed leaves the pack incomplete even when the signed bundle itself is `verified`, and both verdicts are printed when they differ. Only `arweave` witnesses are re-fetched: a checkpoint or stamp whose `anchor_ref.kind` is anything else is not checked, and with gateways passed the run answers `partial`, exit `3`. A sandbox plane's `simulated` witness is never fetched and never counted as checked on a chain: where it is the only witness, the verdict line says nothing was looked up and `--json` writes `"on_chain_checked": false`. The producer's asserted `verdict` is shown but **never trusted**, the displayed verdict is always recomputed from the body. The same CLI also verifies the agent's `sanning.agent.proof/v1` inclusion bundle (it sniffs `spec_version`), so one command covers anchor + agent.
|
|
67
67
|
|
|
68
68
|
> The `verify` CLI is live in `@sanning/proof` ≥ 0.2.2, `npx @sanning/proof verify <bundle> [--gateways <url,url>]` (the gateway list was a second positional argument until 0.13.0). `verifyEvidenceBundle` is the programmatic API.
|
|
69
69
|
|
|
@@ -226,7 +226,7 @@ The walk is bounded and follows no link. A symbolic link is never followed: it i
|
|
|
226
226
|
|
|
227
227
|
Known differences from the verify page: the page opens a dropped `.zip`; the page drops a leading byte-order mark from `bundle.json`, which `verify`'s reader refuses; the page reads whatever the browser hands it for a link, a bad name or a very deep tree; and the page loses a record named `__proto__`, which this command keeps.
|
|
228
228
|
|
|
229
|
-
The first line is the case verdict, then the kernel's summary and every exception through `renderException`, then the packs, the files outside them, and the on-chain half. Exit codes: `0` verified · `1` failed · `2` malformed, usage, an input that cannot be read as a case, or an unexpected error in the command · `3` incomplete or partial · `4` mixed (more than one outcome: read the exceptions). This is the one table every verb uses (`EXIT_CODES`, exported from the package root), and `verify` exits `3` on `partial` since 0.13.0.
|
|
229
|
+
The first line is the case verdict, then the kernel's summary and every exception through `renderException`, then the packs, the files outside them, and the on-chain half. Exit codes: `0` verified · `1` failed · `2` malformed, usage, an input that cannot be read as a case, or an unexpected error in the command · `3` incomplete or partial · `4` mixed (more than one outcome: read the exceptions) · `5` sandbox (it would be `0`, and a pack holds a record signed `environment: "dev"`, or every record is `dev` and only simulated witnesses are unchecked; the title is then `Sandbox`, never `Verified`). This is the one table every verb uses (`EXIT_CODES`, exported from the package root), and `verify` exits `3` on `partial` since 0.13.0.
|
|
230
230
|
|
|
231
231
|
A case reading establishes that every pack verified offline, that every record every pack supplied was read in one pile under envelope-spec §4.1's hand-off rules, and that a hand-off record with no counterpart is always named. It does **not** establish that the case is complete, that a party which presented no pack under the named key recorded nothing, that two organizations are independent, or that any witness was re-fetched on chain: the output says `on-chain NOT CHECKED` unless you pass `--gateways <url,url>`. With gateways, each pack's witnesses are re-fetched pack by pack and reported beside the reading, which they never change; a witness that does not match exits `1`, and an on-chain half that could not be settled turns `0` into `3`.
|
|
232
232
|
|
package/dist/case-file.js
CHANGED
|
@@ -82,7 +82,8 @@ export function howToVerifyText(name) {
|
|
|
82
82
|
` sanning-proof case ${name}\n` +
|
|
83
83
|
"\n" +
|
|
84
84
|
"Exit codes: 0 verified, 1 failed, 2 not readable or a usage error,\n" +
|
|
85
|
-
"3 could not be settled (partial, pending, incomplete, not found), 4 mixed
|
|
85
|
+
"3 could not be settled (partial, pending, incomplete, not found), 4 mixed,\n" +
|
|
86
|
+
"5 sandbox (records signed environment dev, not production evidence).\n" +
|
|
86
87
|
"\n" +
|
|
87
88
|
"To also look each witness up on the public record, add --gateways <url,url>.\n" +
|
|
88
89
|
"The lookup is reported on its own line and never changes the verdict word.\n" +
|
package/dist/case.d.ts
CHANGED
|
@@ -243,6 +243,7 @@
|
|
|
243
243
|
*/
|
|
244
244
|
import { type DeclarationRole } from "./declaration.js";
|
|
245
245
|
import { type PackGradeCauseKind, type PackVerdict } from "./grading.js";
|
|
246
|
+
import type { EnvironmentMarker } from "./environment.js";
|
|
246
247
|
import type { SideContentMap } from "./side-content.js";
|
|
247
248
|
/**
|
|
248
249
|
* The verdicts a case can reach. Every one is already in use in
|
|
@@ -991,15 +992,30 @@ export declare const CASE_RELIANCE: Readonly<Record<CaseVerdict, string>>;
|
|
|
991
992
|
*/
|
|
992
993
|
export declare const CASE_RELIANCE_HANDOFF_CLAUSE = "Every hand-off with both sides in this drop is intact.";
|
|
993
994
|
/**
|
|
994
|
-
*
|
|
995
|
-
*
|
|
995
|
+
* The sentence a case reading's reliance line gains where its packs hold an
|
|
996
|
+
* envelope signed `environment: "dev"` (5.5.6, A48), keyed by the environment
|
|
997
|
+
* marker (`environmentMarker`): `dev` where every envelope is signed dev,
|
|
998
|
+
* `mixed` where only some are. Inserted after the line's first sentence, for
|
|
999
|
+
* every verdict word. A drop with no `dev` envelope has no marker and gains
|
|
1000
|
+
* nothing.
|
|
1001
|
+
*/
|
|
1002
|
+
export declare const CASE_RELIANCE_SANDBOX: Readonly<Record<EnvironmentMarker, string>>;
|
|
1003
|
+
/**
|
|
1004
|
+
* What a reader may rely on for one case reading (5.5.6 v1.38, A35 and A48),
|
|
1005
|
+
* byte for byte the text `case_reliance`'s Python sibling returns.
|
|
996
1006
|
*
|
|
997
|
-
* For every word but `verified`
|
|
1007
|
+
* For every word but `verified` the base is `CASE_RELIANCE` unchanged. For
|
|
998
1008
|
* `verified`, the hand-off clause is inserted after the first sentence
|
|
999
1009
|
* where `handoffRecords` is not zero, and left out where it is zero: the
|
|
1000
1010
|
* base sentence never claims a hand-off the drop does not hold.
|
|
1011
|
+
*
|
|
1012
|
+
* Where `environmentMarker` is `dev` or `mixed`, the `CASE_RELIANCE_SANDBOX`
|
|
1013
|
+
* sentence for it is inserted after the first sentence, for every word, so
|
|
1014
|
+
* it directly follows the claim it qualifies (and precedes the hand-off
|
|
1015
|
+
* clause). Where it is null or absent, the text is exactly what it was
|
|
1016
|
+
* before A48.
|
|
1001
1017
|
*/
|
|
1002
|
-
export declare function caseReliance(verdict: CaseVerdict, handoffRecords: number): string;
|
|
1018
|
+
export declare function caseReliance(verdict: CaseVerdict, handoffRecords: number, environmentMarker?: EnvironmentMarker | null): string;
|
|
1003
1019
|
/** The three witness states, and how each is printed (5.5.6, D7). No other. */
|
|
1004
1020
|
export declare const WITNESS_STATES: {
|
|
1005
1021
|
readonly confirmed: "Confirmed on the public record";
|
package/dist/case.js
CHANGED
|
@@ -241,7 +241,8 @@
|
|
|
241
241
|
* sequence. Narrowing any of it here with `handoffs[]` is precisely the move
|
|
242
242
|
* that would reopen proof#137's door.
|
|
243
243
|
*/
|
|
244
|
-
import { DECLARATION_VERSION,
|
|
244
|
+
import { DECLARATION_VERSION, DECLARATION_VERSIONS, checkDeclaration, declarationClaims, packRecords, } from "./declaration.js";
|
|
245
|
+
import { readChainLinks } from "./chain.js";
|
|
245
246
|
import { NOT_A_HANDOFF_FINDING, normalizedSigningKey, readHandoffs, recordIsMalformed, } from "./handoff.js";
|
|
246
247
|
import { coSignaturesPresent, resolveTraceEvents, verifyEvidenceBundle } from "./evidence.js";
|
|
247
248
|
import { accountDisclosure, completenessNotes, gradePack, } from "./grading.js";
|
|
@@ -1152,10 +1153,14 @@ export async function readCase(packs) {
|
|
|
1152
1153
|
grade = signed.status === "verified" ? "malformed" : signed.status;
|
|
1153
1154
|
}
|
|
1154
1155
|
else {
|
|
1155
|
-
|
|
1156
|
+
// Rule 11 reads the chain links of the same pack, and the reader reads
|
|
1157
|
+
// only the signed body, so the bare check below takes the same reading
|
|
1158
|
+
// (evidence-bundle.md 5.4.2 v1.36, A45).
|
|
1159
|
+
const chainLinks = await readChainLinks(pack.bundle);
|
|
1160
|
+
check = checkDeclaration(pack.bundle, pack.content, chainLinks);
|
|
1156
1161
|
const completeness = completenessNotes(pack.bundle);
|
|
1157
1162
|
full = gradePack(verified, accountDisclosure(pack.bundle, pack.content ?? {}), completeness, check);
|
|
1158
|
-
const bareGrade = gradePack(signed, accountDisclosure(pack.bundle, {}), completeness, checkDeclaration(pack.bundle, null));
|
|
1163
|
+
const bareGrade = gradePack(signed, accountDisclosure(pack.bundle, {}), completeness, checkDeclaration(pack.bundle, null, chainLinks));
|
|
1159
1164
|
// NO `failed` HERE RESTS ON BYTES NOBODY SIGNED. The signed grade decides
|
|
1160
1165
|
// where it is `failed` or `malformed`; a failure only the side map
|
|
1161
1166
|
// produced is `incomplete`; otherwise the full grade.
|
|
@@ -1619,7 +1624,9 @@ function declarationVersionOf(bundle) {
|
|
|
1619
1624
|
return null;
|
|
1620
1625
|
}
|
|
1621
1626
|
const version = declaration["declaration_version"];
|
|
1622
|
-
return
|
|
1627
|
+
return typeof version === "string" && DECLARATION_VERSIONS.includes(version)
|
|
1628
|
+
? version
|
|
1629
|
+
: null;
|
|
1623
1630
|
}
|
|
1624
1631
|
// ---- Presentation (optional) ------------------------------------------------
|
|
1625
1632
|
/**
|
|
@@ -1847,23 +1854,58 @@ export const CASE_RELIANCE = {
|
|
|
1847
1854
|
*/
|
|
1848
1855
|
export const CASE_RELIANCE_HANDOFF_CLAUSE = "Every hand-off with both sides in this drop is intact.";
|
|
1849
1856
|
/**
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1857
|
+
* The sentence a case reading's reliance line gains where its packs hold an
|
|
1858
|
+
* envelope signed `environment: "dev"` (5.5.6, A48), keyed by the environment
|
|
1859
|
+
* marker (`environmentMarker`): `dev` where every envelope is signed dev,
|
|
1860
|
+
* `mixed` where only some are. Inserted after the line's first sentence, for
|
|
1861
|
+
* every verdict word. A drop with no `dev` envelope has no marker and gains
|
|
1862
|
+
* nothing.
|
|
1863
|
+
*/
|
|
1864
|
+
export const CASE_RELIANCE_SANDBOX = {
|
|
1865
|
+
dev: "The records in this drop are sandbox records, signed environment " +
|
|
1866
|
+
"dev: they are not production evidence.",
|
|
1867
|
+
mixed: "Some records in this drop are sandbox records, signed environment " +
|
|
1868
|
+
"dev: those are not production evidence.",
|
|
1869
|
+
};
|
|
1870
|
+
/**
|
|
1871
|
+
* What a reader may rely on for one case reading (5.5.6 v1.38, A35 and A48),
|
|
1872
|
+
* byte for byte the text `case_reliance`'s Python sibling returns.
|
|
1852
1873
|
*
|
|
1853
|
-
* For every word but `verified`
|
|
1874
|
+
* For every word but `verified` the base is `CASE_RELIANCE` unchanged. For
|
|
1854
1875
|
* `verified`, the hand-off clause is inserted after the first sentence
|
|
1855
1876
|
* where `handoffRecords` is not zero, and left out where it is zero: the
|
|
1856
1877
|
* base sentence never claims a hand-off the drop does not hold.
|
|
1878
|
+
*
|
|
1879
|
+
* Where `environmentMarker` is `dev` or `mixed`, the `CASE_RELIANCE_SANDBOX`
|
|
1880
|
+
* sentence for it is inserted after the first sentence, for every word, so
|
|
1881
|
+
* it directly follows the claim it qualifies (and precedes the hand-off
|
|
1882
|
+
* clause). Where it is null or absent, the text is exactly what it was
|
|
1883
|
+
* before A48.
|
|
1857
1884
|
*/
|
|
1858
|
-
export function caseReliance(verdict, handoffRecords) {
|
|
1885
|
+
export function caseReliance(verdict, handoffRecords, environmentMarker = null) {
|
|
1886
|
+
let first;
|
|
1887
|
+
let rest;
|
|
1859
1888
|
if (verdict === "verified" && handoffRecords > 0) {
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
"
|
|
1863
|
-
|
|
1864
|
-
|
|
1889
|
+
first = "Every pack in this drop is genuine, unaltered, and matches its own signed declaration.";
|
|
1890
|
+
rest =
|
|
1891
|
+
"Every hand-off with both sides in this drop is intact. Every record " +
|
|
1892
|
+
"in it was signed by its holder's key. This was read offline, on this " +
|
|
1893
|
+
"device. It does not say this drop holds everything that was recorded.";
|
|
1894
|
+
}
|
|
1895
|
+
else {
|
|
1896
|
+
const base = CASE_RELIANCE[verdict];
|
|
1897
|
+
const cut = base.indexOf(". ");
|
|
1898
|
+
first = base.slice(0, cut + 1);
|
|
1899
|
+
rest = base.slice(cut + 2);
|
|
1865
1900
|
}
|
|
1866
|
-
|
|
1901
|
+
// Only `dev` and `mixed` add the sentence. Any other value, which the type
|
|
1902
|
+
// forbids but an untyped caller can pass, reads as no marker.
|
|
1903
|
+
const sandbox = environmentMarker === "dev" || environmentMarker === "mixed"
|
|
1904
|
+
? CASE_RELIANCE_SANDBOX[environmentMarker]
|
|
1905
|
+
: null;
|
|
1906
|
+
if (sandbox === null)
|
|
1907
|
+
return `${first} ${rest}`;
|
|
1908
|
+
return `${first} ${sandbox} ${rest}`;
|
|
1867
1909
|
}
|
|
1868
1910
|
/** The three witness states, and how each is printed (5.5.6, D7). No other. */
|
|
1869
1911
|
export const WITNESS_STATES = {
|
package/dist/chain.d.ts
CHANGED
|
@@ -79,6 +79,34 @@ export interface ChainReading {
|
|
|
79
79
|
chains: ChainSummary[];
|
|
80
80
|
findings: ChainFinding[];
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* One record's place in a chain, as the reader grouped it, for a check that
|
|
84
|
+
* needs the links themselves rather than the findings: rule 11 of
|
|
85
|
+
* evidence-bundle.md 5.4.2, which holds a claimed chain to every link. The
|
|
86
|
+
* twin of Python's `ChainLink`.
|
|
87
|
+
*/
|
|
88
|
+
export interface ChainLink {
|
|
89
|
+
eventId: string;
|
|
90
|
+
publicKey: string;
|
|
91
|
+
chainKey: string;
|
|
92
|
+
source: ChainSource;
|
|
93
|
+
namespace?: string;
|
|
94
|
+
/** The SHA-256 of the record's committed bytes, or its event id for an adapter chain. */
|
|
95
|
+
selfId: string;
|
|
96
|
+
/** The pointer the record carries, or `null` where it begins a chain. */
|
|
97
|
+
prev: string | null;
|
|
98
|
+
seq: number | null;
|
|
99
|
+
/** Position in `body.events`. */
|
|
100
|
+
order: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Every record the chain reader places in a chain, in bundle order, with the
|
|
104
|
+
* identity and pointer it read: the grouping `readChains` reads, over every
|
|
105
|
+
* record an envelope binds, `event_bytes` or the inline `payload` where no
|
|
106
|
+
* `event_bytes` travel, with the identity the SHA-256 of those bytes, exactly
|
|
107
|
+
* as the envelope verifier binds `payload_hash`.
|
|
108
|
+
*/
|
|
109
|
+
export declare function readChainLinks(bundle: unknown): Promise<ChainLink[]>;
|
|
82
110
|
/**
|
|
83
111
|
* Follow every chain the bundle's records describe, and report the links that
|
|
84
112
|
* do not resolve within it.
|
package/dist/chain.js
CHANGED
|
@@ -49,12 +49,13 @@
|
|
|
49
49
|
//
|
|
50
50
|
// (2) is a transitional accommodation, not the design. `metadata` is
|
|
51
51
|
// caller-owned and the profile says a verifier MUST NOT read it for
|
|
52
|
-
// verification — which is exactly why nothing
|
|
52
|
+
// verification — which is exactly why nothing read through it feeds a
|
|
53
|
+
// verdict, a claimed chain included (rule 11 reads linkage (1) only). The fix is
|
|
53
54
|
// for every adapter to set the record's own `previous_hash` (filed separately
|
|
54
55
|
// against `anchor`); when it lands, the namespace table below stops mattering
|
|
55
56
|
// and can go.
|
|
56
57
|
import { hexToBytes, sha256Hex } from "./crypto.js";
|
|
57
|
-
import { jcsParse } from "./verifier.js";
|
|
58
|
+
import { inlinePayloadBytes, jcsParse } from "./verifier.js";
|
|
58
59
|
import { resolveTraceEvents } from "./evidence.js";
|
|
59
60
|
/**
|
|
60
61
|
* The one sentence a surface should print beside these findings, so the reader
|
|
@@ -186,7 +187,33 @@ function adapterLinkage(record) {
|
|
|
186
187
|
* nothing and means a relabelled record DANGLES instead of quietly resolving.
|
|
187
188
|
* That is why this function is async.
|
|
188
189
|
*/
|
|
189
|
-
|
|
190
|
+
/**
|
|
191
|
+
* The record an envelope binds, for rule 11's links: `event_bytes` where the
|
|
192
|
+
* entry carries them, else the inline `payload` read through
|
|
193
|
+
* `inlinePayloadBytes`, the bytes `payload_hash` binds. Its identity is the
|
|
194
|
+
* SHA-256 of whichever bytes it is read from. Python's `_bound_record`.
|
|
195
|
+
*/
|
|
196
|
+
function boundRecord(entry) {
|
|
197
|
+
const eventBytes = entry?.event_bytes;
|
|
198
|
+
if (typeof eventBytes === "string" && eventBytes.length > 0)
|
|
199
|
+
return decodeRecord(eventBytes);
|
|
200
|
+
const envelope = entry?.envelope;
|
|
201
|
+
if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope))
|
|
202
|
+
return null;
|
|
203
|
+
if (!("payload" in envelope))
|
|
204
|
+
return null;
|
|
205
|
+
try {
|
|
206
|
+
const bytes = inlinePayloadBytes(envelope);
|
|
207
|
+
const record = jcsParse(bytes);
|
|
208
|
+
if (record === null || typeof record !== "object" || Array.isArray(record))
|
|
209
|
+
return null;
|
|
210
|
+
return { record, bytes };
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function groupChains(bundle, inline = false) {
|
|
190
217
|
const groups = new Map();
|
|
191
218
|
const events = eventsOf(bundle);
|
|
192
219
|
for (let order = 0; order < events.length; order++) {
|
|
@@ -197,7 +224,11 @@ async function groupChains(bundle) {
|
|
|
197
224
|
// event id an adapter pointer has nothing to resolve against.
|
|
198
225
|
if (eventId === null || publicKey === null)
|
|
199
226
|
continue;
|
|
200
|
-
|
|
227
|
+
// `inline` (rule 11's links only): a record carried as the envelope's
|
|
228
|
+
// inline `payload` with no `event_bytes` is read too. The chain reading
|
|
229
|
+
// itself keeps reading `event_bytes` alone, so its findings and its rule
|
|
230
|
+
// table do not move.
|
|
231
|
+
const decoded = inline ? boundRecord(entry) : decodeRecord(entry?.event_bytes);
|
|
201
232
|
if (decoded === null)
|
|
202
233
|
continue;
|
|
203
234
|
const { record, bytes } = decoded;
|
|
@@ -346,6 +377,32 @@ function findingsFor(group) {
|
|
|
346
377
|
}
|
|
347
378
|
return findings;
|
|
348
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Every record the chain reader places in a chain, in bundle order, with the
|
|
382
|
+
* identity and pointer it read: the grouping `readChains` reads, over every
|
|
383
|
+
* record an envelope binds, `event_bytes` or the inline `payload` where no
|
|
384
|
+
* `event_bytes` travel, with the identity the SHA-256 of those bytes, exactly
|
|
385
|
+
* as the envelope verifier binds `payload_hash`.
|
|
386
|
+
*/
|
|
387
|
+
export async function readChainLinks(bundle) {
|
|
388
|
+
const links = [];
|
|
389
|
+
for (const g of await groupChains(bundle, true)) {
|
|
390
|
+
for (const m of g.members) {
|
|
391
|
+
links.push({
|
|
392
|
+
eventId: m.eventId,
|
|
393
|
+
publicKey: g.publicKey,
|
|
394
|
+
chainKey: g.chainKey,
|
|
395
|
+
source: g.source,
|
|
396
|
+
...(g.namespace !== undefined ? { namespace: g.namespace } : {}),
|
|
397
|
+
selfId: m.selfId,
|
|
398
|
+
prev: m.prev,
|
|
399
|
+
seq: m.seq,
|
|
400
|
+
order: m.order,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return links.sort((a, b) => a.order - b.order);
|
|
405
|
+
}
|
|
349
406
|
/**
|
|
350
407
|
* Follow every chain the bundle's records describe, and report the links that
|
|
351
408
|
* do not resolve within it.
|