@sanning/proof 0.19.0 → 0.20.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/README.md +30 -29
- package/dist/agent-record.d.ts +62 -0
- package/dist/agent-record.js +527 -0
- package/dist/agent-schemas.generated.d.ts +15 -0
- package/dist/agent-schemas.generated.js +5759 -0
- package/dist/case-file.d.ts +32 -89
- package/dist/case-file.js +41 -93
- package/dist/case.d.ts +343 -810
- package/dist/case.js +693 -1151
- package/dist/chain.d.ts +19 -47
- package/dist/chain.js +78 -205
- package/dist/claims.d.ts +211 -0
- package/dist/claims.js +1081 -0
- package/dist/cli.d.ts +45 -68
- package/dist/cli.js +618 -1022
- package/dist/compose.js +23 -65
- package/dist/crypto.d.ts +7 -8
- package/dist/crypto.js +60 -147
- package/dist/declaration.d.ts +68 -194
- package/dist/declaration.js +175 -518
- package/dist/environment.d.ts +9 -30
- package/dist/environment.js +17 -66
- package/dist/evidence.d.ts +119 -160
- package/dist/evidence.js +744 -1103
- package/dist/exit-codes.d.ts +11 -27
- package/dist/exit-codes.js +24 -59
- package/dist/file-match.d.ts +33 -79
- package/dist/file-match.js +33 -69
- package/dist/grading.d.ts +98 -135
- package/dist/grading.js +224 -380
- package/dist/handoff.d.ts +62 -160
- package/dist/handoff.js +153 -393
- package/dist/index.d.ts +14 -10
- package/dist/index.js +60 -167
- package/dist/json-schema.d.ts +29 -0
- package/dist/json-schema.js +298 -0
- package/dist/merkle.js +16 -28
- package/dist/message-parts.d.ts +56 -212
- package/dist/message-parts.js +142 -502
- package/dist/side-content.d.ts +11 -22
- package/dist/side-content.js +22 -65
- package/dist/types.d.ts +17 -0
- package/dist/types.js +19 -5
- package/dist/verifier.d.ts +40 -114
- package/dist/verifier.js +196 -535
- package/dist/witness-cache.d.ts +24 -45
- package/dist/witness-cache.js +38 -81
- package/dist/witness-lookup.d.ts +36 -57
- package/dist/witness-lookup.js +89 -139
- package/dist/zip-reader.d.ts +18 -33
- package/dist/zip-reader.js +19 -34
- package/package.json +11 -3
- package/src/agent-record.ts +608 -0
- package/src/agent-schemas.generated.ts +5764 -0
- package/src/case-file.ts +45 -109
- package/src/case.ts +848 -1322
- package/src/chain.ts +85 -224
- package/src/claims.ts +1253 -0
- package/src/cli.ts +714 -1176
- package/src/compose.ts +33 -79
- package/src/crypto.ts +60 -147
- package/src/declaration.ts +219 -656
- package/src/environment.ts +17 -65
- package/src/evidence.ts +958 -1381
- package/src/exit-codes.ts +24 -63
- package/src/file-match.ts +40 -89
- package/src/grading.ts +287 -421
- package/src/handoff.ts +175 -427
- package/src/index.ts +132 -177
- package/src/json-schema.ts +295 -0
- package/src/merkle.ts +16 -28
- package/src/message-parts.ts +155 -533
- package/src/side-content.ts +25 -68
- package/src/types.ts +55 -36
- package/src/verifier.ts +202 -526
- package/src/witness-cache.ts +45 -94
- package/src/witness-lookup.ts +107 -177
- package/src/zip-reader.ts +20 -38
- package/vectors/README.md +52 -34
- package/vectors/agent-record.json +2175 -0
- package/vectors/case-file-index.json +1 -1
- package/vectors/case-split.json +2 -2
- package/vectors/chain-reading.json +3 -3
- package/vectors/file-match.json +285 -148
- package/vectors/jcs-reader.json +6 -6
- package/vectors/pack-grading.json +102 -25
- package/vectors/sandbox-evidence.json +597 -580
- package/vectors/stamp-on-chain.json +18 -18
- package/vectors/witness-cache.json +49 -49
- package/dist/agent-proof.d.ts +0 -28
- package/dist/agent-proof.js +0 -226
- package/dist/run-adapters.d.ts +0 -10
- package/dist/run-adapters.js +0 -35
- package/dist/run.d.ts +0 -209
- package/dist/run.js +0 -1405
- package/src/agent-proof.ts +0 -264
- package/src/run-adapters.ts +0 -44
- package/src/run.ts +0 -1598
package/src/witness-cache.ts
CHANGED
|
@@ -1,85 +1,52 @@
|
|
|
1
|
-
// The witnessed-time cache
|
|
2
|
-
//
|
|
1
|
+
// The witnessed-time cache (evidence-bundle.md §5.6.6): `witness-cache.json`
|
|
2
|
+
// beside a pack's `bundle.json`, giving the block height and time of each
|
|
3
|
+
// stamp's witness transaction as the pack builder fetched them. Nobody signs it
|
|
4
|
+
// and anyone can edit it, so every member is unverified.
|
|
3
5
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
6
|
+
// Trust rules:
|
|
7
|
+
// - `readWitnessCache` is never an input to the bundle verdict, the pack grade
|
|
8
|
+
// or the case reading.
|
|
9
|
+
// - `checkWitnessCache` asks the public record. A different block is a finding
|
|
10
|
+
// (`differs`); an unsettled lookup leaves the value `not_checked`.
|
|
11
|
+
// - Nothing here returns a time a reader may rely on. A gateway is named, never
|
|
12
|
+
// trusted.
|
|
7
13
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// `witnessed`, gives for each stamp the block height and block time of the
|
|
11
|
-
// witness transaction, as the builder of the pack fetched them. Nobody signs
|
|
12
|
-
// it, and anyone holding the pack can edit it. EVERY MEMBER OF THE FILE IS
|
|
13
|
-
// UNVERIFIED BY CONSTRUCTION, and the name carries that warning to every field
|
|
14
|
-
// added to it later.
|
|
15
|
-
//
|
|
16
|
-
// Because it is a separate file, a reader before v1.25 never opens it: it lists
|
|
17
|
-
// it as a file outside every pack and reads the pack exactly as it would
|
|
18
|
-
// without it. A `cache` member written INSIDE `bundle.json` is an unknown
|
|
19
|
-
// wrapper field, counted in the signed scope by every reader, this one
|
|
20
|
-
// included.
|
|
21
|
-
//
|
|
22
|
-
// WHAT THIS MODULE DOES, AND WHAT IT MUST NEVER DO.
|
|
23
|
-
//
|
|
24
|
-
// - `readWitnessCache` reads the file's bytes against the signed bundle beside
|
|
25
|
-
// it. It is pure, and it is never an input to the bundle verdict, the pack
|
|
26
|
-
// grade or the case reading: neither `verifyEvidenceBundle` nor `readCase` is
|
|
27
|
-
// ever handed the file.
|
|
28
|
-
// - `checkWitnessCache` looks each cached time up on the public record, where a
|
|
29
|
-
// caller asked for a lookup. A lookup that answers with a different block is
|
|
30
|
-
// a finding, reported on the witness line as `differs`. A lookup that does
|
|
31
|
-
// not settle leaves the cached value labelled not checked.
|
|
32
|
-
// - Nothing here returns a time a reader may rely on. The only time this module
|
|
33
|
-
// ever reports as the public record's is one a gateway returned, and a
|
|
34
|
-
// gateway is named, never trusted (6).
|
|
35
|
-
//
|
|
36
|
-
// A file that is absent, that is not JSON under the reader's rule, whose
|
|
37
|
-
// `cache_version` is not `WITNESS_CACHE_VERSION`, whose `witnessed` is not a
|
|
38
|
-
// list, or whose entry is not well formed, reads as no cache for what it could
|
|
39
|
-
// not say. It is never a finding and never moves anything.
|
|
14
|
+
// A file that is absent, malformed, of another `cache_version`, or with a bad
|
|
15
|
+
// entry reads as no cache for what it could not say. It is never a finding.
|
|
40
16
|
|
|
41
17
|
import { jsonDepthExceeds } from "./case-file.js";
|
|
42
18
|
import { jcs, jcsParse } from "./verifier.js";
|
|
43
19
|
|
|
44
20
|
/**
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
* reader cannot print, and reads as none.
|
|
21
|
+
* The largest printable block time, 9999-12-31T23:59:59Z. A larger value reads
|
|
22
|
+
* as no entry.
|
|
48
23
|
*/
|
|
49
24
|
export const MAX_BLOCK_TIME = 253402300799;
|
|
50
25
|
|
|
51
|
-
/** The file's name, beside `bundle.json`
|
|
26
|
+
/** The file's name, beside `bundle.json` (§5.6.1). */
|
|
52
27
|
export const WITNESS_CACHE_NAME = "witness-cache.json";
|
|
53
28
|
|
|
54
|
-
/**
|
|
55
|
-
* The dispatch key: a file whose `cache_version` is absent or any other value
|
|
56
|
-
* reads as no cache.
|
|
57
|
-
*/
|
|
29
|
+
/** Any other or absent `cache_version` reads as no cache. */
|
|
58
30
|
export const WITNESS_CACHE_VERSION = "sanning.witness-cache/v1";
|
|
59
31
|
|
|
60
|
-
/**
|
|
61
|
-
* How deep `[` and `{` may nest in the file before it is refused unread: the
|
|
62
|
-
* limit `bundle.json` and `contents.json` are read under.
|
|
63
|
-
*/
|
|
32
|
+
/** Nesting limit for the file, the same as for `bundle.json` and `contents.json`. */
|
|
64
33
|
export const MAX_WITNESS_CACHE_DEPTH = 512;
|
|
65
34
|
|
|
66
35
|
// The bodies that carry `stamps[]`, the only witnesses the cache speaks for.
|
|
67
36
|
const STAMP_BODIES = new Set(["sanning.stamp.trace/v1", "sanning.stamp.evidence/v2"]);
|
|
68
37
|
const STAMP_REF = /^[0-9a-f]{64}$/;
|
|
69
|
-
// The
|
|
70
|
-
//
|
|
38
|
+
// The only witness kind and id shape a lookup sends, as for the byte lookup
|
|
39
|
+
// (evidence-bundle §5.1.1).
|
|
71
40
|
const ADAPTER_KIND = "arweave";
|
|
72
41
|
const ARWEAVE_ID = /^[A-Za-z0-9_-]{43}$/;
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// the two kernels on one reading of it.
|
|
42
|
+
// An answer for one transaction is a few hundred bytes. Refusing a deeper one
|
|
43
|
+
// before parsing keeps every reader on the same result.
|
|
76
44
|
const MAX_ANSWER_DEPTH = 16;
|
|
77
45
|
|
|
78
46
|
/**
|
|
79
|
-
* One `cache.witnessed[]` entry
|
|
80
|
-
* `
|
|
81
|
-
*
|
|
82
|
-
* says WHICH witness it is about.
|
|
47
|
+
* One readable `cache.witnessed[]` entry joined to its stamp. `witnessKind` and
|
|
48
|
+
* `witnessId` come from the signed body, never the cache: the cache says when,
|
|
49
|
+
* the body alone says which witness.
|
|
83
50
|
*/
|
|
84
51
|
export interface CachedWitnessTime {
|
|
85
52
|
stampRef: string;
|
|
@@ -90,11 +57,8 @@ export interface CachedWitnessTime {
|
|
|
90
57
|
}
|
|
91
58
|
|
|
92
59
|
/**
|
|
93
|
-
* One gateway's answer for one cached time. `
|
|
94
|
-
*
|
|
95
|
-
* block is not the cached one. `unreachable`: it named no block (a transport
|
|
96
|
-
* failure, a refusal, an answer that is not one, or a witness not yet in a
|
|
97
|
-
* block).
|
|
60
|
+
* One gateway's answer for one cached time. `unreachable` means it named no
|
|
61
|
+
* block: transport failure, refusal, malformed answer, or not yet mined.
|
|
98
62
|
*/
|
|
99
63
|
export interface WitnessTimeAnswer {
|
|
100
64
|
gateway: string;
|
|
@@ -104,10 +68,8 @@ export interface WitnessTimeAnswer {
|
|
|
104
68
|
}
|
|
105
69
|
|
|
106
70
|
/**
|
|
107
|
-
* One cached time
|
|
108
|
-
*
|
|
109
|
-
* named the cached one, otherwise `not_checked`. The same worst-answer-wins
|
|
110
|
-
* order as the byte lookup.
|
|
71
|
+
* One cached time and the public record's answer: `differs` if any gateway
|
|
72
|
+
* named another block, else `matches` if any named this one, else `not_checked`.
|
|
111
73
|
*/
|
|
112
74
|
export interface WitnessTimeCheck {
|
|
113
75
|
cached: CachedWitnessTime;
|
|
@@ -119,7 +81,6 @@ function isObject(v: unknown): v is Record<string, unknown> {
|
|
|
119
81
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
120
82
|
}
|
|
121
83
|
|
|
122
|
-
// A member the object itself holds, never one inherited from its prototype.
|
|
123
84
|
function own(o: Record<string, unknown>, key: string): unknown {
|
|
124
85
|
return Object.prototype.hasOwnProperty.call(o, key) ? o[key] : undefined;
|
|
125
86
|
}
|
|
@@ -130,8 +91,8 @@ function blockNumber(value: unknown, upper: number): number | null {
|
|
|
130
91
|
return value;
|
|
131
92
|
}
|
|
132
93
|
|
|
133
|
-
// `stamp_ref` to `[kind, id]` for
|
|
134
|
-
//
|
|
94
|
+
// `stamp_ref` to `[kind, id]` for each signed stamp that names a witness. A
|
|
95
|
+
// `stamp_ref` on two stamps names neither.
|
|
135
96
|
function stampWitnesses(bundle: Record<string, unknown>): Map<string, [string, string]> {
|
|
136
97
|
const out = new Map<string, [string, string]>();
|
|
137
98
|
if (typeof bundle.body_type !== "string" || !STAMP_BODIES.has(bundle.body_type)) return out;
|
|
@@ -157,10 +118,8 @@ function stampWitnesses(bundle: Record<string, unknown>): Map<string, [string, s
|
|
|
157
118
|
return out;
|
|
158
119
|
}
|
|
159
120
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
// twice, and nothing nested past `MAX_WITNESS_CACHE_DEPTH`. `undefined` for
|
|
163
|
-
// anything else.
|
|
121
|
+
// Strict JSON as for `contents.json` (§5.6.2), within `MAX_WITNESS_CACHE_DEPTH`.
|
|
122
|
+
// `undefined` for anything else.
|
|
164
123
|
function readFile(raw: Uint8Array | null | undefined): unknown {
|
|
165
124
|
if (!(raw instanceof Uint8Array)) return undefined;
|
|
166
125
|
if (jsonDepthExceeds(raw, MAX_WITNESS_CACHE_DEPTH)) return undefined;
|
|
@@ -172,17 +131,13 @@ function readFile(raw: Uint8Array | null | undefined): unknown {
|
|
|
172
131
|
}
|
|
173
132
|
|
|
174
133
|
/**
|
|
175
|
-
* The cached witness times
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* reads only
|
|
179
|
-
* `
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* `block_height` is a non-negative safe integer, and whose `block_time` is a
|
|
183
|
-
* whole number of seconds since 1970-01-01T00:00:00Z from 0 to
|
|
184
|
-
* `MAX_BLOCK_TIME`. A `stamp_ref` two entries name reads for neither.
|
|
185
|
-
* Everything else reads as no cache for the stamp it would have spoken for.
|
|
134
|
+
* The cached witness times `raw` gives for the signed `bundle`, in file order.
|
|
135
|
+
* `raw` is `null` where the pack has no cache file. Pure; never throws.
|
|
136
|
+
*
|
|
137
|
+
* An entry reads only if its `stamp_ref` names exactly one signed stamp with an
|
|
138
|
+
* `anchor_ref`, `block_height` is a non-negative safe integer, and `block_time`
|
|
139
|
+
* is Unix seconds from 0 to `MAX_BLOCK_TIME`. A `stamp_ref` named twice reads
|
|
140
|
+
* for neither.
|
|
186
141
|
*/
|
|
187
142
|
export function readWitnessCache(bundle: unknown, raw: Uint8Array | null | undefined): CachedWitnessTime[] {
|
|
188
143
|
if (!isObject(bundle)) return [];
|
|
@@ -208,7 +163,7 @@ export function readWitnessCache(bundle: unknown, raw: Uint8Array | null | undef
|
|
|
208
163
|
return read.filter((c) => named.get(c.stampRef) === 1);
|
|
209
164
|
}
|
|
210
165
|
|
|
211
|
-
/** A block time as RFC 3339 UTC to the second
|
|
166
|
+
/** A block time as RFC 3339 UTC to the second: `2026-09-08T12:10:07Z`. */
|
|
212
167
|
export function formatBlockTime(seconds: number): string {
|
|
213
168
|
return new Date(seconds * 1000).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
214
169
|
}
|
|
@@ -218,10 +173,7 @@ export function renderWitnessTime(blockHeight: number, blockTime: number): strin
|
|
|
218
173
|
return `block ${blockHeight} at ${formatBlockTime(blockTime)}`;
|
|
219
174
|
}
|
|
220
175
|
|
|
221
|
-
/**
|
|
222
|
-
* The request body the lookup sends: one GraphQL query for one transaction's
|
|
223
|
-
* block, as JCS, so both kernels send the same bytes.
|
|
224
|
-
*/
|
|
176
|
+
/** The lookup's request body: one GraphQL query as JCS, so the bytes are fixed. */
|
|
225
177
|
export function witnessTimeQuery(witnessId: string): string {
|
|
226
178
|
return jcs({ query: `query{transaction(id:"${witnessId}"){block{height timestamp}}}` });
|
|
227
179
|
}
|
|
@@ -272,10 +224,9 @@ async function ask(
|
|
|
272
224
|
}
|
|
273
225
|
|
|
274
226
|
/**
|
|
275
|
-
*
|
|
276
|
-
* `fetchImpl`,
|
|
277
|
-
*
|
|
278
|
-
* transaction id, is never looked up.
|
|
227
|
+
* Looks each cached time up on every gateway, in order. With no gateways or no
|
|
228
|
+
* `fetchImpl`, every time is `not_checked`. Only `arweave` witnesses with a
|
|
229
|
+
* 43-character base64url id are looked up.
|
|
279
230
|
*/
|
|
280
231
|
export async function checkWitnessCache(
|
|
281
232
|
cached: readonly CachedWitnessTime[],
|