@mikeargento/bitgraph-audit 0.7.0 → 0.9.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 +4 -0
- package/dist/audit.d.ts +8 -6
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +28 -8
- package/dist/audit.js.map +1 -1
- package/dist/ceilings.d.ts +25 -0
- package/dist/ceilings.d.ts.map +1 -1
- package/dist/ceilings.js +159 -31
- package/dist/ceilings.js.map +1 -1
- package/dist/cli.js +40 -3
- package/dist/cli.js.map +1 -1
- package/dist/exports.d.ts +39 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/exports.js +371 -0
- package/dist/exports.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest.d.ts.map +1 -1
- package/dist/ingest.js +179 -19
- package/dist/ingest.js.map +1 -1
- package/dist/report-json.d.ts.map +1 -1
- package/dist/report-json.js +15 -0
- package/dist/report-json.js.map +1 -1
- package/dist/report-md.d.ts.map +1 -1
- package/dist/report-md.js +225 -0
- package/dist/report-md.js.map +1 -1
- package/dist/settlement-blobs.d.ts +131 -0
- package/dist/settlement-blobs.d.ts.map +1 -0
- package/dist/settlement-blobs.js +682 -0
- package/dist/settlement-blobs.js.map +1 -0
- package/dist/types.d.ts +254 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/__tests__/fixtures/settlement/0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c.bin +0 -0
- package/src/__tests__/fixtures/settlement/ceiling-51979918.json +101 -0
- package/src/__tests__/fixtures/settlement/pointer-51979918.json +69 -0
- package/src/__tests__/settlement.test.ts +254 -0
- package/src/audit.ts +30 -8
- package/src/ceilings.ts +167 -33
- package/src/cli.ts +39 -3
- package/src/exports.ts +478 -0
- package/src/index.ts +16 -0
- package/src/ingest.ts +192 -18
- package/src/report-json.ts +15 -0
- package/src/report-md.ts +248 -0
- package/src/settlement-blobs.ts +689 -0
- package/src/types.ts +234 -2
package/src/types.ts
CHANGED
|
@@ -123,6 +123,13 @@ export type AnomalyCode =
|
|
|
123
123
|
| "attestation-measurement-mismatch"
|
|
124
124
|
/** A validated attestation document's user_data is not bound to this proof's canonical proof hash. */
|
|
125
125
|
| "attestation-user-data-mismatch"
|
|
126
|
+
// --- Exports (bitgraph-export/1) ---
|
|
127
|
+
/** A file declares format "bitgraph-export/1" but lacks an export's structure (a proof object and tree.rootDocument). Not checked; fails the audit (exit bit 1). */
|
|
128
|
+
| "export-malformed"
|
|
129
|
+
/** A file declares a bitgraph-export format other than "bitgraph-export/1". Not checked; fails the audit (exit bit 1), as an unsupported proof version does. */
|
|
130
|
+
| "export-unsupported-format"
|
|
131
|
+
/** A file opens an export object but is larger than the audit's cap for one export's JSON (IngestLimits.maxExportJsonBytes). Not checked; fails the audit (exit bit 1). */
|
|
132
|
+
| "export-too-large"
|
|
126
133
|
| (string & {});
|
|
127
134
|
|
|
128
135
|
/**
|
|
@@ -408,6 +415,8 @@ export interface IngestCounts {
|
|
|
408
415
|
witnesses: number;
|
|
409
416
|
/** Ceiling files (bitgraph-ceiling/1) and ceiling status notes (bitgraph-ceiling-status/1). */
|
|
410
417
|
ceilings?: number;
|
|
418
|
+
/** Export-shaped files (format "bitgraph-export/..."), checked or rejected. */
|
|
419
|
+
exports?: number;
|
|
411
420
|
/** Container entries skipped for unsafe paths. */
|
|
412
421
|
skippedUnsafePaths: number;
|
|
413
422
|
}
|
|
@@ -437,6 +446,14 @@ export interface IngestLimits {
|
|
|
437
446
|
* declaring a larger size aborts ingest before any allocation.
|
|
438
447
|
*/
|
|
439
448
|
maxMetadataEntryBytes: number;
|
|
449
|
+
/**
|
|
450
|
+
* Ceiling on one export's JSON (bitgraph-export/1), for every container,
|
|
451
|
+
* directories included. Other JSON candidates stop at 8 MiB; an entry that
|
|
452
|
+
* opens an export object may run to this size (the owner's export of a
|
|
453
|
+
* large tree lists every leaf). A larger export is reported
|
|
454
|
+
* (export-too-large) and not checked. Default 192 MiB.
|
|
455
|
+
*/
|
|
456
|
+
maxExportJsonBytes?: number;
|
|
440
457
|
}
|
|
441
458
|
|
|
442
459
|
/**
|
|
@@ -473,6 +490,11 @@ export interface IngestResult {
|
|
|
473
490
|
ceilings?: CeilingFile[];
|
|
474
491
|
/** Ceiling status notes (bitgraph-ceiling-status/1): a package saying why a ceiling is absent. */
|
|
475
492
|
ceilingStatuses?: CeilingFile[];
|
|
493
|
+
/**
|
|
494
|
+
* Export-shaped files (format "bitgraph-export/..."), in observation order.
|
|
495
|
+
* Evidence, never artifacts. Optional so older embedders' IngestResults still type.
|
|
496
|
+
*/
|
|
497
|
+
exports?: ExportFile[];
|
|
476
498
|
/** Present when a root manifest.json entry existed. */
|
|
477
499
|
manifest?: ManifestReport;
|
|
478
500
|
|
|
@@ -1314,6 +1336,8 @@ export interface AuditOptions {
|
|
|
1314
1336
|
trustedRootCaDer?: Uint8Array;
|
|
1315
1337
|
/** Ceilings in time: the declared writer, the chain, and an optional Base RPC for the one online check. */
|
|
1316
1338
|
ceilings?: import("./ceilings.js").CeilingAuditOptions;
|
|
1339
|
+
/** Exports (bitgraph-export/1): extra spec hashes, PCR0 pins, and optional lookups for embedders that allow network. */
|
|
1340
|
+
exports?: import("./exports.js").ExportAuditOptions;
|
|
1317
1341
|
}
|
|
1318
1342
|
|
|
1319
1343
|
/**
|
|
@@ -1351,7 +1375,7 @@ export interface CeilingCheck {
|
|
|
1351
1375
|
*/
|
|
1352
1376
|
status: "verified" | "failed" | "pending" | "unmatched";
|
|
1353
1377
|
reason?: string;
|
|
1354
|
-
/** One line for people, e.g. "Ceiling: Base block N at hh:mm:ss UTC
|
|
1378
|
+
/** One line for people, e.g. "Ceiling: included in Base block N at hh:mm:ss UTC. ..."; it never claims settlement the file does not prove. */
|
|
1355
1379
|
label?: string;
|
|
1356
1380
|
window?: {
|
|
1357
1381
|
floorBlock: number | null;
|
|
@@ -1364,6 +1388,37 @@ export interface CeilingCheck {
|
|
|
1364
1388
|
/** Online check against a Base RPC: null when not asked or unreachable. */
|
|
1365
1389
|
onChain: boolean | null;
|
|
1366
1390
|
onChainDetail?: string;
|
|
1391
|
+
/** Present when the ceiling file carries a settlement pointer (bitgraph-settlement/1). */
|
|
1392
|
+
settlement?: CeilingSettlementCheck;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* A ceiling's settlement on Ethereum, checked offline in two layers: the
|
|
1397
|
+
* POINTER (the Ethereum header, the batcher transaction's inclusion, its
|
|
1398
|
+
* blob commitments) and the BLOBS (blob bytes kept in the bundle, verified
|
|
1399
|
+
* against those commitments and decoded down to the ceiling transaction).
|
|
1400
|
+
* Blob bytes absent from the bundle are reported, never a failure.
|
|
1401
|
+
*/
|
|
1402
|
+
export interface CeilingSettlementCheck {
|
|
1403
|
+
/** verified: both layers passed. pointer-only: the pointer passed and no blob bytes were in the bundle. failed: a layer that ran failed. */
|
|
1404
|
+
status: "verified" | "pointer-only" | "failed";
|
|
1405
|
+
pointer: {
|
|
1406
|
+
ok: boolean;
|
|
1407
|
+
reason?: string;
|
|
1408
|
+
/** The Ethereum block the batcher transaction is in, read from its verified header. */
|
|
1409
|
+
existedBy?: { blockNumber: number; blockHash: string; blockTimestamp: number };
|
|
1410
|
+
};
|
|
1411
|
+
blobs: {
|
|
1412
|
+
status: "verified" | "failed" | "absent";
|
|
1413
|
+
detail: string;
|
|
1414
|
+
/** Blobs the pointer names, and how many had bytes in the bundle. */
|
|
1415
|
+
listed: number;
|
|
1416
|
+
supplied: number;
|
|
1417
|
+
/** Where the ceiling transaction was found inside the decoded batch data. */
|
|
1418
|
+
located?: { baseBlockNumber: number; baseTxIndex: number; txHash: string; batchComplete: boolean; framesDecoded: number; framesTotal: number | null };
|
|
1419
|
+
};
|
|
1420
|
+
/** One line per layer, for people. */
|
|
1421
|
+
lines: string[];
|
|
1367
1422
|
}
|
|
1368
1423
|
|
|
1369
1424
|
export interface CeilingAnalysis {
|
|
@@ -1375,6 +1430,160 @@ export interface CeilingAnalysis {
|
|
|
1375
1430
|
statuses: Array<{ path: string; status: string; note: string }>;
|
|
1376
1431
|
}
|
|
1377
1432
|
|
|
1433
|
+
// ---------------------------------------------------------------------------
|
|
1434
|
+
// Exports (bitgraph-export/1)
|
|
1435
|
+
// ---------------------------------------------------------------------------
|
|
1436
|
+
|
|
1437
|
+
/**
|
|
1438
|
+
* An export-shaped file as found in the bundle: a JSON object whose format
|
|
1439
|
+
* field starts with "bitgraph-export/". Found by structure, never by name.
|
|
1440
|
+
* Evidence, never an artifact.
|
|
1441
|
+
*/
|
|
1442
|
+
export interface ExportFile {
|
|
1443
|
+
path: string;
|
|
1444
|
+
fileSha256Hex: string;
|
|
1445
|
+
/** The format the file declares. */
|
|
1446
|
+
format: string;
|
|
1447
|
+
/**
|
|
1448
|
+
* ok: a bitgraph-export/1 document (bitgraph-verify's parseExport accepts it).
|
|
1449
|
+
* malformed: declares bitgraph-export/1 but lacks a proof object or tree.rootDocument.
|
|
1450
|
+
* unsupported-format: declares another bitgraph-export version.
|
|
1451
|
+
* too-large: opens an export object but is larger than the audit's export JSON cap; not parsed (json is empty).
|
|
1452
|
+
*/
|
|
1453
|
+
status: "ok" | "malformed" | "unsupported-format" | "too-large";
|
|
1454
|
+
json: Record<string, unknown>;
|
|
1455
|
+
/** Canonical hash of the proof the export carries, when that proof joined the proof analysis (a proof-shaped bitgraph/1 object). */
|
|
1456
|
+
proofHash?: string;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/** One claim exactly as bitgraph-verify's verifyExport states it. */
|
|
1460
|
+
export interface ExportClaimRecord {
|
|
1461
|
+
/** Stable id, dotted: "proof.signature", "tree.member", "bytes.floor", "ceiling.base", "confirmed.floor" ... */
|
|
1462
|
+
id: string;
|
|
1463
|
+
name: string;
|
|
1464
|
+
result: "TRUE" | "FALSE" | "UNDETERMINED" | "NOT_CARRIED";
|
|
1465
|
+
/** What the result rests on (the primitive or the evidence), empty when nothing was checked. */
|
|
1466
|
+
restsOn: string;
|
|
1467
|
+
detail: string;
|
|
1468
|
+
/** offline: recomputed from the bytes in hand. confirmed: from the caller's own chain lookups (never made by the CLI). */
|
|
1469
|
+
level: "offline" | "confirmed";
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/** The three time claims as verifyExport establishes them. Never merged; a null field was not established. */
|
|
1473
|
+
export interface ExportTimes {
|
|
1474
|
+
/** The committed bytes were finished after this Ethereum block (the proof's signed floor block, its header checked by hash). */
|
|
1475
|
+
floor: { blockNumber: number; blockHash: string; blockTimestamp: number } | null;
|
|
1476
|
+
/** The record existed by this Base block, at its time. Provisional until the block is checked against Base (offline, always provisional). */
|
|
1477
|
+
ceilingBase: { blockNumber: number; blockHash: string; blockTimestamp: number; provisional: boolean } | null;
|
|
1478
|
+
/** The record existed by this Ethereum block (through Base's output root; needs no trust in Base). */
|
|
1479
|
+
ceilingEthereum: { blockNumber: number; blockHash: string; blockTimestamp: number } | null;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
/** A file in the bundle that an export covers, matched by SHA-256 against a leaf's digests. */
|
|
1483
|
+
export interface ExportCoveredFile {
|
|
1484
|
+
/** SHA-256 of the file, lowercase hex. */
|
|
1485
|
+
sha256Hex: string;
|
|
1486
|
+
/** Every bundle path holding these bytes. */
|
|
1487
|
+
paths: string[];
|
|
1488
|
+
byteLength: number;
|
|
1489
|
+
/** The leaf it matched, in tree order: the first leaf whose committed or original digest is the file's, as verifyExport picks it. */
|
|
1490
|
+
leafIndex: number | null;
|
|
1491
|
+
/** That leaf's placement: "as-is", "trailer/1", "container/1" or "container/2". */
|
|
1492
|
+
placement: string | null;
|
|
1493
|
+
/**
|
|
1494
|
+
* committed-bytes: the file is the leaf's committed bytes (its artifact digest).
|
|
1495
|
+
* original: the file is the original the committed bytes were made from (its origin digest).
|
|
1496
|
+
* as-is: the leaf records the file exactly as it is (the two digests are the same).
|
|
1497
|
+
*/
|
|
1498
|
+
matchedAs: "committed-bytes" | "original" | "as-is";
|
|
1499
|
+
/** The name an owner's export lists for that leaf. Unsigned and informational. */
|
|
1500
|
+
nameInExport?: string;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/** A claim of one run that is not common to every run of its export, with its place in verifyExport's order. */
|
|
1504
|
+
export interface ExportOwnClaim extends ExportClaimRecord {
|
|
1505
|
+
/** Index of this claim in the run's whole claim list, in verifyExport's order. */
|
|
1506
|
+
position: number;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* One verifyExport run: with one covered file, or once without a file when
|
|
1511
|
+
* the bundle holds none. The run's whole claim list, in verifyExport's
|
|
1512
|
+
* order, is the export's common claims (ExportCheck.claims) with this run's
|
|
1513
|
+
* own claims put back at their positions: exportRunClaims(check, run).
|
|
1514
|
+
* verifyExport's reasons are that list's FALSE and UNDETERMINED claims, and
|
|
1515
|
+
* its time claims are the export's (ExportCheck.times): they do not depend
|
|
1516
|
+
* on the file.
|
|
1517
|
+
*/
|
|
1518
|
+
export interface ExportRun {
|
|
1519
|
+
/** The covered file this run checked; null for the run without a file. */
|
|
1520
|
+
file: ExportCoveredFile | null;
|
|
1521
|
+
/** verifyExport's verdict over its offline claims. */
|
|
1522
|
+
verdict: "TRUE" | "FALSE" | "UNDETERMINED";
|
|
1523
|
+
/** This run's claims that differ from the export's common claims (in practice the ones about its file: tree.member, bytes.member, bytes.floor). */
|
|
1524
|
+
claims: ExportOwnClaim[];
|
|
1525
|
+
/** The member the run established (index, count, placement, digests), when it did. */
|
|
1526
|
+
member: { index: number; count: number; placement: string; artifactHex: string; originHex: string } | null;
|
|
1527
|
+
/**
|
|
1528
|
+
* What the floor covers for this run's file, read from its claims: content
|
|
1529
|
+
* (the committed bytes carry the commitment, so they were finished after the
|
|
1530
|
+
* floor block; an original inside them is not dated by it), record (an as-is
|
|
1531
|
+
* leaf: the record was made after the floor block, the bytes themselves are
|
|
1532
|
+
* not dated), or null (the file was not established as a member, or no file
|
|
1533
|
+
* was checked).
|
|
1534
|
+
*/
|
|
1535
|
+
floorCovers: "content" | "record" | null;
|
|
1536
|
+
/** verifyExport's plain-language reading, written from its claims. */
|
|
1537
|
+
reading: string;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/** One export file, checked. */
|
|
1541
|
+
export interface ExportCheck {
|
|
1542
|
+
path: string;
|
|
1543
|
+
fileSha256Hex: string;
|
|
1544
|
+
format: string;
|
|
1545
|
+
/** checked: verifyExport ran. malformed / unsupported-format / too-large: rejected at ingest, not checked (exit bit 1). */
|
|
1546
|
+
status: "checked" | "malformed" | "unsupported-format" | "too-large";
|
|
1547
|
+
/** Why the file was not checked. */
|
|
1548
|
+
reason?: string;
|
|
1549
|
+
/** member: carries one member's evidence (tree.member). owner: carries the whole list (tree.leaves). root-only: neither. */
|
|
1550
|
+
kind?: "member" | "owner" | "root-only";
|
|
1551
|
+
/** Canonical hash of the export's proof, which also appears in the proof analysis. */
|
|
1552
|
+
proofHash?: string;
|
|
1553
|
+
/**
|
|
1554
|
+
* The audit's judgment of the export: FALSE when any run's verdict is FALSE
|
|
1555
|
+
* or any claim of any run is FALSE (a confirmed claim included, from an
|
|
1556
|
+
* embedder's lookup); else UNDETERMINED when any run is; else TRUE. A FALSE
|
|
1557
|
+
* export fails the audit (exit bit 1), as a bad proof does.
|
|
1558
|
+
*/
|
|
1559
|
+
verdict: "TRUE" | "FALSE" | "UNDETERMINED";
|
|
1560
|
+
/** Ids of the claims that are FALSE in any run, in first-seen order. */
|
|
1561
|
+
failedClaims: string[];
|
|
1562
|
+
/** The three time claims, never merged. They do not depend on the file, so every run states the same; read from the first. */
|
|
1563
|
+
times: ExportTimes;
|
|
1564
|
+
/** The claims every run states alike (same id, result, level, restsOn and detail), in verifyExport's order. With one run, all of its claims. */
|
|
1565
|
+
claims: ExportClaimRecord[];
|
|
1566
|
+
/** Files in the bundle this export covers, sorted by first path. */
|
|
1567
|
+
files: ExportCoveredFile[];
|
|
1568
|
+
/** One run per covered file (in the order of files), or one run without a file when the bundle holds none. */
|
|
1569
|
+
runs: ExportRun[];
|
|
1570
|
+
/**
|
|
1571
|
+
* How the per-file runs of an owner's export were made. as-given: verifyExport
|
|
1572
|
+
* on the export itself, once per file. member-from-list: the owner's list was
|
|
1573
|
+
* checked once (claim tree.leaves TRUE), then each file's member evidence was
|
|
1574
|
+
* derived from it exactly as verifyExport derives it, verifyExport ran on that
|
|
1575
|
+
* member form, and the list claim was put back in its place; the runs equal
|
|
1576
|
+
* the as-given runs, without rebuilding the whole tree once per file.
|
|
1577
|
+
*/
|
|
1578
|
+
runMode?: "as-given" | "member-from-list";
|
|
1579
|
+
/** Covered files not checked one by one: only when an owner's list fails and the per-file budget ran out, or a file could not be re-read. */
|
|
1580
|
+
unchecked?: Array<{ file: ExportCoveredFile; reason: string }>;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
export interface ExportAnalysis {
|
|
1584
|
+
checks: ExportCheck[];
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1378
1587
|
export interface AuditResult {
|
|
1379
1588
|
runMetadata: AuditRunMetadata;
|
|
1380
1589
|
ingest: IngestResult;
|
|
@@ -1388,6 +1597,8 @@ export interface AuditResult {
|
|
|
1388
1597
|
attestations: AttestationAnalysis;
|
|
1389
1598
|
/** Ceilings in time on Base. Absent from results made before this stage existed. */
|
|
1390
1599
|
ceilings?: CeilingAnalysis;
|
|
1600
|
+
/** Exports (bitgraph-export/1), each checked with verifyExport per covered file. Absent from results made before this stage existed. */
|
|
1601
|
+
exports?: ExportAnalysis;
|
|
1391
1602
|
}
|
|
1392
1603
|
|
|
1393
1604
|
/**
|
|
@@ -1395,7 +1606,13 @@ export interface AuditResult {
|
|
|
1395
1606
|
*
|
|
1396
1607
|
* bit 1 (value 1): verification failures. Set when any proof's canonical
|
|
1397
1608
|
* checks failed at either tier, or any proof-shaped input was rejected
|
|
1398
|
-
* as an unsupported version
|
|
1609
|
+
* as an unsupported version, or any export (bitgraph-export/1) has a
|
|
1610
|
+
* FALSE claim (its verdict is FALSE), or an export-shaped file was
|
|
1611
|
+
* rejected as malformed or of an unsupported format. An export's
|
|
1612
|
+
* attestation claims are part of its verdict: an export is checked as
|
|
1613
|
+
* one self-contained object, as a carrier is. NOT_CARRIED claims (a
|
|
1614
|
+
* pending ceiling, the covered file absent) never fail an export.
|
|
1615
|
+
* artifact-unavailable is NOT a failure: a
|
|
1399
1616
|
* proof without artifact bytes passes or fails on its bytes-free checks
|
|
1400
1617
|
* alone, unless a supplied trust policy makes those checks fail (for
|
|
1401
1618
|
* example requireSlot), in which case its status is "failed" and it
|
|
@@ -1585,6 +1802,19 @@ export interface ReportSummary {
|
|
|
1585
1802
|
segmentsUpperBounded: number;
|
|
1586
1803
|
segmentsUnanchored: number;
|
|
1587
1804
|
};
|
|
1805
|
+
/** Exports (bitgraph-export/1), as counts. Present only when the bundle carries export-shaped files. */
|
|
1806
|
+
exports?: {
|
|
1807
|
+
/** Export-shaped files found. */
|
|
1808
|
+
files: number;
|
|
1809
|
+
/** Exports checked, by the audit's verdict. */
|
|
1810
|
+
verdictTrue: number;
|
|
1811
|
+
verdictFalse: number;
|
|
1812
|
+
verdictUndetermined: number;
|
|
1813
|
+
/** Export-shaped files rejected (malformed or an unsupported format). */
|
|
1814
|
+
rejected: number;
|
|
1815
|
+
/** verifyExport runs made with a covered file from the bundle. */
|
|
1816
|
+
coveredFilesChecked: number;
|
|
1817
|
+
};
|
|
1588
1818
|
exit: ExitFlags;
|
|
1589
1819
|
}
|
|
1590
1820
|
|
|
@@ -1631,6 +1861,8 @@ export interface AuditJsonReport {
|
|
|
1631
1861
|
};
|
|
1632
1862
|
/** Ceilings in time on Base (bitgraph-ceiling/1). Absent when the bundle carries none. */
|
|
1633
1863
|
ceilings?: import("./types.js").CeilingAnalysis;
|
|
1864
|
+
/** Exports (bitgraph-export/1), one entry per export file in observation order. Absent when the bundle carries none. */
|
|
1865
|
+
exports?: ExportAnalysis;
|
|
1634
1866
|
attestations: {
|
|
1635
1867
|
records: ProofAttestationRecord[];
|
|
1636
1868
|
counts: AttestationAnalysis["counts"];
|