@totemsdk/raster-proof 0.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/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * @module @totemsdk/raster-proof
4
+ *
5
+ * Edge-capable raster and visual evidence proof primitives for Totem Edge.
6
+ *
7
+ * This package proves bytes, manifests, provenance, windows and declared
8
+ * relationships. It does NOT prove that a visual interpretation is correct
9
+ * unless a reviewer or downstream model proof says so.
10
+ *
11
+ * Pipeline:
12
+ *
13
+ * bytes + asset metadata + spatial context + provenance
14
+ * → chunk hashes / Merkle root
15
+ * → deterministic raster manifest
16
+ * → window proof (optional)
17
+ * → Totem proof envelope
18
+ * → proofgraph linkage
19
+ *
20
+ * This is NOT a raster-processing engine: no GDAL, no GeoTIFF parsing, no
21
+ * satellite provider APIs, no STAC, no cloud masking, no NDVI, no ML
22
+ * segmentation, no orthomosaic generation, no tile server, no storage.
23
+ *
24
+ * No network, no storage, no map rendering, no GIS engine dependency.
25
+ * SHA3-256 everywhere, safe to run on edge devices.
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.addRasterManifestToGraph = exports.rasterWindowProofToGraphEdges = exports.rasterManifestToGraphEdges = exports.rasterWindowProofToProofGraphNode = exports.rasterManifestToProofGraphNode = exports.verifyRasterProof = exports.signRasterProof = exports.createUnsignedRasterProof = exports.rasterEvidenceRefs = exports.createRasterSpatialRelation = exports.rasterFootprintToSpatialObject = exports.verifyRasterDerivation = exports.createDerivedRasterManifest = exports.rasterWindowProofToEvidenceRef = exports.createRasterWindowProof = exports.rasterManifestToEvidenceRef = exports.validateRasterManifest = exports.createRasterManifest = exports.createRasterMerkleSummary = exports.verifyMerkleProof = exports.createMerkleProof = exports.merkleLeafHash = exports.computeMerkleRoot = exports.chunkBytes = exports.DEFAULT_CHUNK_SIZE_BYTES = exports.hashSubarray = exports.hashString = exports.hashBytes = exports.hashRasterWindowProof = exports.computeRasterWindowProofId = exports.hashRasterManifest = exports.computeRasterManifestId = exports.toHex = exports.canonicalJson = void 0;
29
+ var canonical_js_1 = require("./canonical.js");
30
+ Object.defineProperty(exports, "canonicalJson", { enumerable: true, get: function () { return canonical_js_1.canonicalJson; } });
31
+ Object.defineProperty(exports, "toHex", { enumerable: true, get: function () { return canonical_js_1.toHex; } });
32
+ var canonical_js_2 = require("./canonical.js");
33
+ Object.defineProperty(exports, "computeRasterManifestId", { enumerable: true, get: function () { return canonical_js_2.computeRasterManifestId; } });
34
+ Object.defineProperty(exports, "hashRasterManifest", { enumerable: true, get: function () { return canonical_js_2.hashRasterManifest; } });
35
+ Object.defineProperty(exports, "computeRasterWindowProofId", { enumerable: true, get: function () { return canonical_js_2.computeRasterWindowProofId; } });
36
+ Object.defineProperty(exports, "hashRasterWindowProof", { enumerable: true, get: function () { return canonical_js_2.hashRasterWindowProof; } });
37
+ var hash_js_1 = require("./hash.js");
38
+ Object.defineProperty(exports, "hashBytes", { enumerable: true, get: function () { return hash_js_1.hashBytes; } });
39
+ Object.defineProperty(exports, "hashString", { enumerable: true, get: function () { return hash_js_1.hashString; } });
40
+ Object.defineProperty(exports, "hashSubarray", { enumerable: true, get: function () { return hash_js_1.hashSubarray; } });
41
+ var merkle_js_1 = require("./merkle.js");
42
+ Object.defineProperty(exports, "DEFAULT_CHUNK_SIZE_BYTES", { enumerable: true, get: function () { return merkle_js_1.DEFAULT_CHUNK_SIZE_BYTES; } });
43
+ Object.defineProperty(exports, "chunkBytes", { enumerable: true, get: function () { return merkle_js_1.chunkBytes; } });
44
+ Object.defineProperty(exports, "computeMerkleRoot", { enumerable: true, get: function () { return merkle_js_1.computeMerkleRoot; } });
45
+ Object.defineProperty(exports, "merkleLeafHash", { enumerable: true, get: function () { return merkle_js_1.merkleLeafHash; } });
46
+ Object.defineProperty(exports, "createMerkleProof", { enumerable: true, get: function () { return merkle_js_1.createMerkleProof; } });
47
+ Object.defineProperty(exports, "verifyMerkleProof", { enumerable: true, get: function () { return merkle_js_1.verifyMerkleProof; } });
48
+ Object.defineProperty(exports, "createRasterMerkleSummary", { enumerable: true, get: function () { return merkle_js_1.createRasterMerkleSummary; } });
49
+ var manifest_js_1 = require("./manifest.js");
50
+ Object.defineProperty(exports, "createRasterManifest", { enumerable: true, get: function () { return manifest_js_1.createRasterManifest; } });
51
+ Object.defineProperty(exports, "validateRasterManifest", { enumerable: true, get: function () { return manifest_js_1.validateRasterManifest; } });
52
+ Object.defineProperty(exports, "rasterManifestToEvidenceRef", { enumerable: true, get: function () { return manifest_js_1.rasterManifestToEvidenceRef; } });
53
+ var window_js_1 = require("./window.js");
54
+ Object.defineProperty(exports, "createRasterWindowProof", { enumerable: true, get: function () { return window_js_1.createRasterWindowProof; } });
55
+ Object.defineProperty(exports, "rasterWindowProofToEvidenceRef", { enumerable: true, get: function () { return window_js_1.rasterWindowProofToEvidenceRef; } });
56
+ var provenance_js_1 = require("./provenance.js");
57
+ Object.defineProperty(exports, "createDerivedRasterManifest", { enumerable: true, get: function () { return provenance_js_1.createDerivedRasterManifest; } });
58
+ Object.defineProperty(exports, "verifyRasterDerivation", { enumerable: true, get: function () { return provenance_js_1.verifyRasterDerivation; } });
59
+ var spatial_js_1 = require("./spatial.js");
60
+ Object.defineProperty(exports, "rasterFootprintToSpatialObject", { enumerable: true, get: function () { return spatial_js_1.rasterFootprintToSpatialObject; } });
61
+ Object.defineProperty(exports, "createRasterSpatialRelation", { enumerable: true, get: function () { return spatial_js_1.createRasterSpatialRelation; } });
62
+ var proof_js_1 = require("./proof.js");
63
+ Object.defineProperty(exports, "rasterEvidenceRefs", { enumerable: true, get: function () { return proof_js_1.rasterEvidenceRefs; } });
64
+ Object.defineProperty(exports, "createUnsignedRasterProof", { enumerable: true, get: function () { return proof_js_1.createUnsignedRasterProof; } });
65
+ Object.defineProperty(exports, "signRasterProof", { enumerable: true, get: function () { return proof_js_1.signRasterProof; } });
66
+ Object.defineProperty(exports, "verifyRasterProof", { enumerable: true, get: function () { return proof_js_1.verifyRasterProof; } });
67
+ var proofgraph_js_1 = require("./proofgraph.js");
68
+ Object.defineProperty(exports, "rasterManifestToProofGraphNode", { enumerable: true, get: function () { return proofgraph_js_1.rasterManifestToProofGraphNode; } });
69
+ Object.defineProperty(exports, "rasterWindowProofToProofGraphNode", { enumerable: true, get: function () { return proofgraph_js_1.rasterWindowProofToProofGraphNode; } });
70
+ Object.defineProperty(exports, "rasterManifestToGraphEdges", { enumerable: true, get: function () { return proofgraph_js_1.rasterManifestToGraphEdges; } });
71
+ Object.defineProperty(exports, "rasterWindowProofToGraphEdges", { enumerable: true, get: function () { return proofgraph_js_1.rasterWindowProofToGraphEdges; } });
72
+ Object.defineProperty(exports, "addRasterManifestToGraph", { enumerable: true, get: function () { return proofgraph_js_1.addRasterManifestToGraph; } });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Raster manifest construction and validation for @totemsdk/raster-proof.
3
+ *
4
+ * A RasterManifest is the deterministic, content-addressed record of "what
5
+ * this raster is, where it came from, what bytes back it, and what spatial
6
+ * context it has". Creation is pure — no I/O, no format sniffing.
7
+ */
8
+ import type { EvidenceRef } from '@totemsdk/proof';
9
+ import type { CreateRasterManifestParams, RasterManifest, RasterValidationResult } from './types.js';
10
+ /**
11
+ * Create a deterministic RasterManifest. rasterId is computed from stable
12
+ * fields (everything except rasterId and metadata).
13
+ */
14
+ export declare function createRasterManifest(params: CreateRasterManifestParams): RasterManifest;
15
+ /**
16
+ * Validate the structure of a RasterManifest. Structural only — does not
17
+ * re-hash bytes (verification of a proof envelope also recomputes the ID and
18
+ * manifest hash).
19
+ */
20
+ export declare function validateRasterManifest(manifest: RasterManifest): RasterValidationResult;
21
+ /**
22
+ * Convert a RasterManifest into an EvidenceRef for inclusion in a proof.
23
+ */
24
+ export declare function rasterManifestToEvidenceRef(manifest: RasterManifest): EvidenceRef;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Raster manifest construction and validation for @totemsdk/raster-proof.
4
+ *
5
+ * A RasterManifest is the deterministic, content-addressed record of "what
6
+ * this raster is, where it came from, what bytes back it, and what spatial
7
+ * context it has". Creation is pure — no I/O, no format sniffing.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createRasterManifest = createRasterManifest;
11
+ exports.validateRasterManifest = validateRasterManifest;
12
+ exports.rasterManifestToEvidenceRef = rasterManifestToEvidenceRef;
13
+ const canonical_js_1 = require("./canonical.js");
14
+ const CAPTURE_SKEW_MS = 24 * 60 * 60 * 1000; // capturedAt may lag createdAt by ≤ 24h
15
+ function inLonRange(v) {
16
+ return Number.isFinite(v) && v >= -180 && v <= 180;
17
+ }
18
+ function inLatRange(v) {
19
+ return Number.isFinite(v) && v >= -90 && v <= 90;
20
+ }
21
+ /**
22
+ * Create a deterministic RasterManifest. rasterId is computed from stable
23
+ * fields (everything except rasterId and metadata).
24
+ */
25
+ function createRasterManifest(params) {
26
+ const createdAt = params.createdAt ?? Date.now();
27
+ const body = {
28
+ sourceType: params.sourceType,
29
+ layerType: params.layerType,
30
+ ...(params.capturedAt !== undefined ? { capturedAt: params.capturedAt } : {}),
31
+ createdAt,
32
+ ...(params.deviceId !== undefined ? { deviceId: params.deviceId } : {}),
33
+ ...(params.operatorId !== undefined ? { operatorId: params.operatorId } : {}),
34
+ ...(params.missionId !== undefined ? { missionId: params.missionId } : {}),
35
+ ...(params.providerId !== undefined ? { providerId: params.providerId } : {}),
36
+ ...(params.sceneId !== undefined ? { sceneId: params.sceneId } : {}),
37
+ asset: params.asset,
38
+ ...(params.spatial !== undefined ? { spatial: params.spatial } : {}),
39
+ ...(params.provenance !== undefined ? { provenance: params.provenance } : {}),
40
+ ...(params.metadata !== undefined ? { metadata: params.metadata } : {}),
41
+ };
42
+ return { ...body, rasterId: (0, canonical_js_1.computeRasterManifestId)(body) };
43
+ }
44
+ /**
45
+ * Validate the structure of a RasterManifest. Structural only — does not
46
+ * re-hash bytes (verification of a proof envelope also recomputes the ID and
47
+ * manifest hash).
48
+ */
49
+ function validateRasterManifest(manifest) {
50
+ const errors = [];
51
+ const warnings = [];
52
+ if (typeof manifest.rasterId !== 'string' || manifest.rasterId.length === 0) {
53
+ errors.push('rasterId is required');
54
+ }
55
+ if (!manifest.sourceType) {
56
+ errors.push('sourceType is required');
57
+ }
58
+ if (!manifest.layerType) {
59
+ errors.push('layerType is required');
60
+ }
61
+ if (!manifest.asset || typeof manifest.asset !== 'object') {
62
+ errors.push('asset is required');
63
+ }
64
+ if (manifest.asset && !manifest.asset.format) {
65
+ errors.push('asset.format is required');
66
+ }
67
+ if (manifest.asset && (typeof manifest.asset.contentHash !== 'string' || manifest.asset.contentHash.length === 0)) {
68
+ errors.push('asset.contentHash is required');
69
+ }
70
+ if (manifest.asset && manifest.asset.hashAlgorithm !== 'sha3-256') {
71
+ errors.push('asset.hashAlgorithm must be sha3-256');
72
+ }
73
+ if (manifest.asset && manifest.asset.byteSize !== undefined && manifest.asset.byteSize < 0) {
74
+ errors.push('asset.byteSize must be non-negative');
75
+ }
76
+ if (manifest.asset && manifest.asset.merkleRoot !== undefined && !/^[a-f0-9]{64}$/.test(manifest.asset.merkleRoot)) {
77
+ errors.push('asset.merkleRoot must be 64-char lowercase hex (sha3-256)');
78
+ }
79
+ if (manifest.asset && manifest.asset.chunkSizeBytes !== undefined && manifest.asset.chunkSizeBytes <= 0) {
80
+ errors.push('asset.chunkSizeBytes must be positive');
81
+ }
82
+ if (!Number.isFinite(manifest.createdAt) || manifest.createdAt <= 0) {
83
+ errors.push('createdAt must be finite and positive');
84
+ }
85
+ if (manifest.capturedAt !== undefined) {
86
+ if (!Number.isFinite(manifest.capturedAt) || manifest.capturedAt <= 0) {
87
+ errors.push('capturedAt must be finite and positive');
88
+ }
89
+ else if (manifest.capturedAt > manifest.createdAt + CAPTURE_SKEW_MS) {
90
+ const allowed = manifest.metadata?.['allowFutureCapture'] === true;
91
+ if (!allowed) {
92
+ errors.push('capturedAt is far after createdAt; set metadata.allowFutureCapture = true to allow');
93
+ }
94
+ else {
95
+ warnings.push('capturedAt is far after createdAt (explicitly allowed by metadata.allowFutureCapture)');
96
+ }
97
+ }
98
+ }
99
+ if (manifest.spatial) {
100
+ if (manifest.spatial.bounds !== undefined) {
101
+ const b = manifest.spatial.bounds;
102
+ if (b.length !== 4) {
103
+ errors.push('spatial.bounds must be [minLon, minLat, maxLon, maxLat]');
104
+ }
105
+ else {
106
+ const [minLon, minLat, maxLon, maxLat] = b;
107
+ if (!inLonRange(minLon) || !inLonRange(maxLon)) {
108
+ errors.push('spatial.bounds longitudes must be in [-180, 180]');
109
+ }
110
+ if (!inLatRange(minLat) || !inLatRange(maxLat)) {
111
+ errors.push('spatial.bounds latitudes must be in [-90, 90]');
112
+ }
113
+ if (minLon > maxLon)
114
+ errors.push('spatial.bounds minLon must be <= maxLon');
115
+ if (minLat > maxLat)
116
+ errors.push('spatial.bounds minLat must be <= maxLat');
117
+ }
118
+ }
119
+ if (manifest.spatial.widthPx !== undefined && (!Number.isInteger(manifest.spatial.widthPx) || manifest.spatial.widthPx <= 0)) {
120
+ errors.push('spatial.widthPx must be a positive integer');
121
+ }
122
+ if (manifest.spatial.heightPx !== undefined && (!Number.isInteger(manifest.spatial.heightPx) || manifest.spatial.heightPx <= 0)) {
123
+ errors.push('spatial.heightPx must be a positive integer');
124
+ }
125
+ if (manifest.spatial.resolutionM !== undefined && (!Number.isFinite(manifest.spatial.resolutionM) || manifest.spatial.resolutionM <= 0)) {
126
+ errors.push('spatial.resolutionM must be positive');
127
+ }
128
+ }
129
+ if (manifest.sourceType === 'derived') {
130
+ const derivedFrom = manifest.provenance?.derivedFrom ?? [];
131
+ if (derivedFrom.length === 0) {
132
+ warnings.push('derived raster should declare provenance.derivedFrom source raster IDs');
133
+ }
134
+ }
135
+ return { valid: errors.length === 0, errors, warnings };
136
+ }
137
+ /**
138
+ * Convert a RasterManifest into an EvidenceRef for inclusion in a proof.
139
+ */
140
+ function rasterManifestToEvidenceRef(manifest) {
141
+ return {
142
+ id: manifest.rasterId,
143
+ kind: 'raster-manifest',
144
+ hash: (0, canonical_js_1.hashRasterManifest)(manifest),
145
+ };
146
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Edge-safe Merkle chunking and proof for @totemsdk/raster-proof.
3
+ *
4
+ * DOMAIN SEPARATION: chunk content hashes are SHA3-256 of the raw bytes;
5
+ * Merkle leaves and internal nodes are domain-separated so a chunk hash can
6
+ * never be confused with a node hash:
7
+ * leaf = sha3_256("totem-raster-leaf" + chunk.hash)
8
+ * node = sha3_256("totem-raster-node" + left + right)
9
+ *
10
+ * ODD-LAYER RULE (deterministic): when a Merkle level has an odd number of
11
+ * hashes, the last hash is PROMOTED unchanged to the next level (it is NOT
12
+ * duplicated/hashed with itself). This is the standard RFC 6962-style rule
13
+ * and keeps the tree compact and deterministic.
14
+ *
15
+ * Empty byte arrays are rejected — a raster asset must have bytes.
16
+ */
17
+ import type { RasterChunk, RasterMerkleOptions, RasterMerkleProof, RasterMerkleSummary } from './types.js';
18
+ export declare const DEFAULT_CHUNK_SIZE_BYTES: number;
19
+ /**
20
+ * Domain-separated Merkle leaf hash for a chunk. A user proving "this chunk
21
+ * is in this tree" recomputes merkleLeafHash(chunk) and compares it to
22
+ * RasterMerkleProof.leafHash before verifying the sibling chain.
23
+ */
24
+ export declare function merkleLeafHash(chunk: RasterChunk): string;
25
+ /**
26
+ * Split bytes into fixed-size chunks (default 64 KiB). Each chunk carries a
27
+ * content hash of its raw bytes. Empty input is rejected.
28
+ */
29
+ export declare function chunkBytes(bytes: Uint8Array, chunkSizeBytes?: number): RasterChunk[];
30
+ /**
31
+ * Compute the Merkle root over chunk hashes (deterministic). Odd layers
32
+ * promote the last hash unchanged.
33
+ */
34
+ export declare function computeMerkleRoot(chunks: RasterChunk[]): string;
35
+ /**
36
+ * Build a Merkle inclusion proof for one chunk. The proof's leafHash is the
37
+ * domain-separated leaf hash of that chunk. Callers can reproduce it with
38
+ * merkleLeafHash(chunks[leafIndex]).
39
+ */
40
+ export declare function createMerkleProof(chunks: RasterChunk[], leafIndex: number): RasterMerkleProof;
41
+ /**
42
+ * Verify a Merkle inclusion proof against its own root. Structural check —
43
+ * recomputes the root from leafHash + siblings and compares.
44
+ */
45
+ export declare function verifyMerkleProof(proof: RasterMerkleProof): boolean;
46
+ /**
47
+ * Hash bytes, chunk them, and summarize in one edge-safe pass.
48
+ */
49
+ export declare function createRasterMerkleSummary(bytes: Uint8Array, options?: RasterMerkleOptions): RasterMerkleSummary;
package/dist/merkle.js ADDED
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ /**
3
+ * Edge-safe Merkle chunking and proof for @totemsdk/raster-proof.
4
+ *
5
+ * DOMAIN SEPARATION: chunk content hashes are SHA3-256 of the raw bytes;
6
+ * Merkle leaves and internal nodes are domain-separated so a chunk hash can
7
+ * never be confused with a node hash:
8
+ * leaf = sha3_256("totem-raster-leaf" + chunk.hash)
9
+ * node = sha3_256("totem-raster-node" + left + right)
10
+ *
11
+ * ODD-LAYER RULE (deterministic): when a Merkle level has an odd number of
12
+ * hashes, the last hash is PROMOTED unchanged to the next level (it is NOT
13
+ * duplicated/hashed with itself). This is the standard RFC 6962-style rule
14
+ * and keeps the tree compact and deterministic.
15
+ *
16
+ * Empty byte arrays are rejected — a raster asset must have bytes.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.DEFAULT_CHUNK_SIZE_BYTES = void 0;
20
+ exports.merkleLeafHash = merkleLeafHash;
21
+ exports.chunkBytes = chunkBytes;
22
+ exports.computeMerkleRoot = computeMerkleRoot;
23
+ exports.createMerkleProof = createMerkleProof;
24
+ exports.verifyMerkleProof = verifyMerkleProof;
25
+ exports.createRasterMerkleSummary = createRasterMerkleSummary;
26
+ const core_1 = require("@totemsdk/core");
27
+ const canonical_js_1 = require("./canonical.js");
28
+ const hash_js_1 = require("./hash.js");
29
+ const LEAF_PREFIX = 'totem-raster-leaf';
30
+ const NODE_PREFIX = 'totem-raster-node';
31
+ exports.DEFAULT_CHUNK_SIZE_BYTES = 64 * 1024; // 64 KiB
32
+ function domainHash(prefix, a, b) {
33
+ const encoder = new TextEncoder();
34
+ const payload = b === undefined ? prefix + a : prefix + a + b;
35
+ return (0, canonical_js_1.toHex)((0, core_1.sha3_256)(encoder.encode(payload)));
36
+ }
37
+ /**
38
+ * Domain-separated Merkle leaf hash for a chunk. A user proving "this chunk
39
+ * is in this tree" recomputes merkleLeafHash(chunk) and compares it to
40
+ * RasterMerkleProof.leafHash before verifying the sibling chain.
41
+ */
42
+ function merkleLeafHash(chunk) {
43
+ return domainHash(LEAF_PREFIX, chunk.hash);
44
+ }
45
+ function merkleNodeHash(left, right) {
46
+ return domainHash(NODE_PREFIX, left, right);
47
+ }
48
+ /**
49
+ * Split bytes into fixed-size chunks (default 64 KiB). Each chunk carries a
50
+ * content hash of its raw bytes. Empty input is rejected.
51
+ */
52
+ function chunkBytes(bytes, chunkSizeBytes = exports.DEFAULT_CHUNK_SIZE_BYTES) {
53
+ if (bytes.length === 0) {
54
+ throw new Error('cannot chunk empty bytes; a raster asset must have content');
55
+ }
56
+ if (!Number.isInteger(chunkSizeBytes) || chunkSizeBytes <= 0) {
57
+ throw new Error('chunk size must be a positive integer');
58
+ }
59
+ const chunks = [];
60
+ for (let offset = 0; offset < bytes.length; offset += chunkSizeBytes) {
61
+ const length = Math.min(chunkSizeBytes, bytes.length - offset);
62
+ chunks.push({
63
+ index: chunks.length,
64
+ offset,
65
+ length,
66
+ hash: (0, hash_js_1.hashBytes)(bytes.subarray(offset, offset + length)),
67
+ });
68
+ }
69
+ return chunks;
70
+ }
71
+ function buildLevels(leafHashes) {
72
+ const levels = [leafHashes];
73
+ while (levels[levels.length - 1].length > 1) {
74
+ const cur = levels[levels.length - 1];
75
+ const next = [];
76
+ for (let i = 0; i < cur.length; i += 2) {
77
+ if (i + 1 < cur.length) {
78
+ next.push(merkleNodeHash(cur[i], cur[i + 1]));
79
+ }
80
+ else {
81
+ next.push(cur[i]); // promote the odd last hash unchanged
82
+ }
83
+ }
84
+ levels.push(next);
85
+ }
86
+ return levels;
87
+ }
88
+ /**
89
+ * Compute the Merkle root over chunk hashes (deterministic). Odd layers
90
+ * promote the last hash unchanged.
91
+ */
92
+ function computeMerkleRoot(chunks) {
93
+ if (chunks.length === 0) {
94
+ throw new Error('cannot compute a Merkle root over zero chunks');
95
+ }
96
+ const levels = buildLevels(chunks.map((c) => merkleLeafHash(c)));
97
+ return levels[levels.length - 1][0];
98
+ }
99
+ /**
100
+ * Build a Merkle inclusion proof for one chunk. The proof's leafHash is the
101
+ * domain-separated leaf hash of that chunk. Callers can reproduce it with
102
+ * merkleLeafHash(chunks[leafIndex]).
103
+ */
104
+ function createMerkleProof(chunks, leafIndex) {
105
+ if (chunks.length === 0) {
106
+ throw new Error('cannot create a Merkle proof over zero chunks');
107
+ }
108
+ if (!Number.isInteger(leafIndex) || leafIndex < 0 || leafIndex >= chunks.length) {
109
+ throw new Error(`leafIndex ${leafIndex} out of range [0, ${chunks.length})`);
110
+ }
111
+ const levels = buildLevels(chunks.map((c) => merkleLeafHash(c)));
112
+ const siblings = [];
113
+ let idx = leafIndex;
114
+ for (let level = 0; level < levels.length - 1; level++) {
115
+ const cur = levels[level];
116
+ const odd = cur.length % 2 === 1;
117
+ if (odd && idx === cur.length - 1) {
118
+ // Last element of an odd level is promoted unchanged — no sibling here,
119
+ // the node itself moves up one level.
120
+ idx = Math.floor(cur.length / 2);
121
+ continue;
122
+ }
123
+ const siblingIndex = idx % 2 === 0 ? idx + 1 : idx - 1;
124
+ siblings.push({
125
+ position: idx % 2 === 0 ? 'right' : 'left',
126
+ hash: cur[siblingIndex],
127
+ });
128
+ idx = Math.floor(idx / 2);
129
+ }
130
+ return {
131
+ root: levels[levels.length - 1][0],
132
+ leafHash: merkleLeafHash(chunks[leafIndex]),
133
+ leafIndex,
134
+ siblings,
135
+ hashAlgorithm: 'sha3-256',
136
+ };
137
+ }
138
+ /**
139
+ * Verify a Merkle inclusion proof against its own root. Structural check —
140
+ * recomputes the root from leafHash + siblings and compares.
141
+ */
142
+ function verifyMerkleProof(proof) {
143
+ if (proof.hashAlgorithm !== 'sha3-256')
144
+ return false;
145
+ if (!proof.root || !proof.leafHash || proof.leafIndex < 0)
146
+ return false;
147
+ if (!/^[a-f0-9]{64}$/.test(proof.root) || !/^[a-f0-9]{64}$/.test(proof.leafHash))
148
+ return false;
149
+ let h = proof.leafHash;
150
+ for (const sibling of proof.siblings) {
151
+ if (!/^[a-f0-9]{64}$/.test(sibling.hash))
152
+ return false;
153
+ h = sibling.position === 'left' ? merkleNodeHash(sibling.hash, h) : merkleNodeHash(h, sibling.hash);
154
+ }
155
+ return h === proof.root;
156
+ }
157
+ /**
158
+ * Hash bytes, chunk them, and summarize in one edge-safe pass.
159
+ */
160
+ function createRasterMerkleSummary(bytes, options = {}) {
161
+ const chunkSizeBytes = options.chunkSizeBytes ?? exports.DEFAULT_CHUNK_SIZE_BYTES;
162
+ const contentHash = (0, hash_js_1.hashBytes)(bytes);
163
+ const chunks = chunkBytes(bytes, chunkSizeBytes);
164
+ return {
165
+ contentHash,
166
+ merkleRoot: computeMerkleRoot(chunks),
167
+ chunkSizeBytes,
168
+ chunkCount: chunks.length,
169
+ byteSize: bytes.length,
170
+ };
171
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Proof envelope integration for @totemsdk/raster-proof.
3
+ *
4
+ * Bridges raster manifests, window proofs and Merkle proofs into
5
+ * @totemsdk/proof evidence refs and signed proof envelopes. Signing and
6
+ * verification delegate to @totemsdk/proof. Anchoring is deliberately NOT
7
+ * required for verification.
8
+ */
9
+ import type { EvidenceRef, SignedProof, UnsignedProof } from '@totemsdk/proof';
10
+ import type { CreateRasterProofParams, RasterManifest, RasterProofVerifyResult, RasterWindowProof } from './types.js';
11
+ /**
12
+ * Build the evidence ref list for a raster proof:
13
+ * - raster manifest hash
14
+ * - content hash
15
+ * - Merkle root when present
16
+ * - window proof hash when present
17
+ * - source raster IDs when derived
18
+ * - spatial object ID when present
19
+ */
20
+ export declare function rasterEvidenceRefs(manifest: RasterManifest, windowProof?: RasterWindowProof, spatialObjectId?: string): EvidenceRef[];
21
+ /**
22
+ * Create an unsigned attestation proof for a raster manifest.
23
+ *
24
+ * The proof claims: "this manifest describes this asset, produced by this
25
+ * source, at this time, with this content hash / Merkle root". It does NOT
26
+ * claim the visual interpretation is correct — interpretation is an
27
+ * operator / model / reviewer claim made elsewhere.
28
+ */
29
+ export declare function createUnsignedRasterProof(params: CreateRasterProofParams): UnsignedProof;
30
+ /**
31
+ * Sign an unsigned raster proof with a WOTS key.
32
+ *
33
+ * The caller is responsible for reserving the WOTS key index (see
34
+ * @totemsdk/wots-lease) before calling — one-time key warning applies.
35
+ */
36
+ export declare function signRasterProof(unsigned: UnsignedProof, seed: Uint8Array, keyIndex: number): SignedProof;
37
+ /**
38
+ * Verify a signed raster proof end to end.
39
+ *
40
+ * Checks:
41
+ * 1. the underlying @totemsdk/proof verification (signature, proofId, expiry)
42
+ * 2. payload contains a structurally valid RasterManifest
43
+ * 3. the manifest rasterId matches a recomputation from its fields
44
+ * 4. the manifest evidence hash matches the payload manifest
45
+ * 5. content hash and Merkle root evidence refs are present when declared
46
+ * 6. window proof (when supplied) has a recomputable ID, matches the
47
+ * manifest's Merkle root, and any supplied Merkle proofs verify
48
+ * 7. provenance structure is valid for derived rasters
49
+ *
50
+ * Anchoring is not required. Source raster manifests are not supplied inside
51
+ * the proof, so derivation checks are structural only (full cross-source
52
+ * verification is @totemsdk/raster-proof's verifyRasterDerivation).
53
+ */
54
+ export declare function verifyRasterProof(signed: SignedProof): RasterProofVerifyResult;