@totemsdk/proofgraph 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/canonical.d.ts +4 -4
- package/dist/canonical.js +11 -26
- package/dist/index.js +1 -1
- package/dist/io.d.ts +8 -5
- package/dist/io.js +132 -8
- package/package.json +32 -23
- package/rust/pkg/package.json +27 -0
- package/rust/pkg/proofgraph_wasm.d.ts +30 -0
- package/rust/pkg/proofgraph_wasm.js +9 -0
- package/rust/pkg/proofgraph_wasm_bg.js +690 -0
- package/rust/pkg/proofgraph_wasm_bg.wasm +0 -0
- package/rust/pkg/proofgraph_wasm_bg.wasm.d.ts +25 -0
- package/rust/pkg-node/package.json +21 -0
- package/rust/pkg-node/proofgraph_wasm.d.ts +30 -0
- package/rust/pkg-node/proofgraph_wasm.js +709 -0
- package/rust/pkg-node/proofgraph_wasm_bg.wasm +0 -0
- package/rust/pkg-node/proofgraph_wasm_bg.wasm.d.ts +25 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Totem SDK Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# @totemsdk/proofgraph
|
|
2
|
+
|
|
3
|
+
Local deterministic proof relationship graph for Totem Edge — indexes proofs, identities, manifests, anchors, and subjects into a queryable, content-addressed DAG.
|
|
4
|
+
|
|
5
|
+
No network. No storage. Pure in-memory immutable graph with SHA3-256 content addressing.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @totemsdk/proofgraph
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
`@totemsdk/proofgraph` takes `SignedProof` objects (from [`@totemsdk/proof`](https://www.npmjs.com/package/@totemsdk/proof)) and builds a local relationship graph you can query and verify. Every mutation returns a new graph with a recomputed `graphId` — the ID is a deterministic SHA3-256 over node and edge content, so the same logical graph always produces the same ID regardless of when it was built.
|
|
16
|
+
|
|
17
|
+
Key properties:
|
|
18
|
+
|
|
19
|
+
- **Immutable** — every add/mutation returns a new `ProofGraph`; originals are never modified
|
|
20
|
+
- **Idempotent** — adding the same node or edge twice has no effect (deduplication by content ID)
|
|
21
|
+
- **Content-deterministic** — `graphId` excludes `createdAt` timestamps so identical content always hashes identically
|
|
22
|
+
- **Interface-only storage** — `ProofGraphStoragePort` is an interface; no concrete adapter ships in this package
|
|
23
|
+
|
|
24
|
+
## All exports
|
|
25
|
+
|
|
26
|
+
### Graph construction
|
|
27
|
+
|
|
28
|
+
| Export | Description |
|
|
29
|
+
|--------|-------------|
|
|
30
|
+
| `createProofGraph()` | Create an empty graph |
|
|
31
|
+
| `setGraphMetadata(graph, metadata)` | Attach mutable metadata; recomputes `graphId` |
|
|
32
|
+
| `addProof(graph, signed)` | Add a `SignedProof` with all its linked nodes and edges |
|
|
33
|
+
| `addIdentityDocument(graph, doc)` | Add a `TotemIdentityDocument` node |
|
|
34
|
+
| `addIdentityClaim(graph, signed)` | Add a `SignedIdentityClaim` with its edge type |
|
|
35
|
+
| `addManifest(graph, signed)` | Add a `SignedManifest` with `manifests_as` address edges |
|
|
36
|
+
| `addReceiptLike(graph, receipt)` | Add a receipt node; optionally link to a proof |
|
|
37
|
+
| `addAnchor(graph, anchor)` | Add a standalone anchor node; optionally link to a proof |
|
|
38
|
+
| `addNode(graph, node)` | Low-level: add a pre-built `ProofGraphNode` |
|
|
39
|
+
| `addEdge(graph, edge)` | Low-level: add a pre-built `ProofGraphEdge` |
|
|
40
|
+
| `buildEdge(type, from, to, contextId?)` | Construct an edge without adding it |
|
|
41
|
+
|
|
42
|
+
### Query
|
|
43
|
+
|
|
44
|
+
| Export | Description |
|
|
45
|
+
|--------|-------------|
|
|
46
|
+
| `findNode(graph, refId)` | Find a node by its `refId` |
|
|
47
|
+
| `getProofNodes(graph)` | All nodes of type `'proof'` |
|
|
48
|
+
| `findProofsBySubject(graph, subjectId)` | Proofs where the subject matches (via `about` or `proves` edges) |
|
|
49
|
+
| `findProofsByIssuer(graph, issuer)` | Proofs issued by a given address |
|
|
50
|
+
| `findAnchorsForProof(graph, proofId)` | Anchor nodes linked to a proof via `anchored_to` edges |
|
|
51
|
+
| `findRevocations(graph, proofId)` | Proofs that revoke a given proof |
|
|
52
|
+
| `findSupersessions(graph, proofId)` | Proofs that supersede a given proof |
|
|
53
|
+
| `findConflicts(graph)` | All subject nodes targeted by more than one proof |
|
|
54
|
+
| `getEvidenceTrail(graph, proofId)` | Evidence nodes linked to a proof, in insertion order |
|
|
55
|
+
| `getProofLineage(graph, proofId)` | Walk `derived_from` chain; returns ancestor nodes (cycle-safe) |
|
|
56
|
+
| `resolveCurrentProofSet(graph)` | Active proofs — revoked and superseded proofs are excluded |
|
|
57
|
+
| `getManifestsForAddress(graph, address)` | Manifest nodes linked to an address |
|
|
58
|
+
| `getEdgesFrom(graph, refId)` | All edges originating from a node |
|
|
59
|
+
| `getEdgesTo(graph, refId)` | All edges terminating at a node |
|
|
60
|
+
| `getEdgesByType(graph, type)` | All edges of a given type |
|
|
61
|
+
| `getEdgesBetween(graph, fromId, toId)` | All edges between two specific nodes |
|
|
62
|
+
| `reachableFrom(graph, startId, edgeTypes?)` | BFS reachability from a node, optionally filtered by edge type |
|
|
63
|
+
|
|
64
|
+
### Verification
|
|
65
|
+
|
|
66
|
+
| Export | Description |
|
|
67
|
+
|--------|-------------|
|
|
68
|
+
| `verifyProofGraph(graph)` | Verify all proof nodes; returns `ProofGraphVerifyResult` |
|
|
69
|
+
| `verifyGraphProofs(graph)` | Returns `string[]` — IDs of proofs that fail verification |
|
|
70
|
+
|
|
71
|
+
### Import / Export
|
|
72
|
+
|
|
73
|
+
| Export | Description |
|
|
74
|
+
|--------|-------------|
|
|
75
|
+
| `exportProofGraph(graph)` | Serialize to JSON string |
|
|
76
|
+
| `importProofGraph(json)` | Deserialize and verify `graphId` integrity — throws on tamper |
|
|
77
|
+
|
|
78
|
+
### Canonical helpers
|
|
79
|
+
|
|
80
|
+
| Export | Description |
|
|
81
|
+
|--------|-------------|
|
|
82
|
+
| `computeNodeId(type, refId, data?)` | Deterministic SHA3-256 node ID |
|
|
83
|
+
| `computeEdgeId(type, from, to)` | Deterministic SHA3-256 edge ID |
|
|
84
|
+
| `computeProofGraphId(graph)` | SHA3-256 of all node and edge IDs (excludes `createdAt`, `metadata`) |
|
|
85
|
+
| `recomputeGraphId(graph)` | Return a new graph with a freshly computed `graphId` |
|
|
86
|
+
| `toHex(bytes)` | Uint8Array → lowercase hex (no 0x prefix) |
|
|
87
|
+
| `canonicalJson(value)` | Deterministic JSON — sorted keys, no `undefined` |
|
|
88
|
+
|
|
89
|
+
## Type reference
|
|
90
|
+
|
|
91
|
+
### `ProofGraph`
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface ProofGraph {
|
|
95
|
+
graphId: string; // SHA3-256 over all node/edge content IDs
|
|
96
|
+
nodes: ProofGraphNode[];
|
|
97
|
+
edges: ProofGraphEdge[];
|
|
98
|
+
createdAt: number; // Unix ms — excluded from graphId
|
|
99
|
+
metadata?: Record<string, unknown>; // Also excluded from graphId
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `ProofGraphNode`
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
interface ProofGraphNode {
|
|
107
|
+
id: string; // computeNodeId(type, refId, data)
|
|
108
|
+
type: ProofGraphNodeType;
|
|
109
|
+
refId: string; // External ID (proofId, address, evidenceId, …)
|
|
110
|
+
createdAt: number; // Unix ms — excluded from graphId hash
|
|
111
|
+
data?: Record<string, unknown>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type ProofGraphNodeType =
|
|
115
|
+
| 'proof' | 'subject' | 'issuer' | 'evidence'
|
|
116
|
+
| 'anchor' | 'manifest' | 'address' | 'identity'
|
|
117
|
+
| 'receipt';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `ProofGraphEdge`
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
interface ProofGraphEdge {
|
|
124
|
+
id: string; // computeEdgeId(type, from, to)
|
|
125
|
+
type: ProofGraphEdgeType;
|
|
126
|
+
from: string; // source node refId
|
|
127
|
+
to: string; // target node refId
|
|
128
|
+
contextId?: string; // e.g. the proofId that caused this edge
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type ProofGraphEdgeType =
|
|
132
|
+
| 'issued_by' | 'about' | 'proves' | 'has_evidence'
|
|
133
|
+
| 'anchored_to' | 'revokes' | 'supersedes' | 'delegates_to'
|
|
134
|
+
| 'derived_from' | 'manifests_as' | 'controls' | 'supports';
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `ProofGraphVerifyResult`
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface ProofGraphVerifyResult {
|
|
141
|
+
valid: boolean;
|
|
142
|
+
invalidProofs: string[]; // proofIds that failed verification
|
|
143
|
+
checkedCount: number;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `ReceiptLikeInput` / `AnchorInput`
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
interface ReceiptLikeInput {
|
|
151
|
+
id: string;
|
|
152
|
+
data?: Record<string, unknown>;
|
|
153
|
+
proofId?: string; // If set, adds a 'supports' edge from receipt → proof
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface AnchorInput {
|
|
157
|
+
hash: string; // Used as the node's refId
|
|
158
|
+
proofId?: string; // If set, adds an 'anchored_to' edge from proof → anchor
|
|
159
|
+
[key: string]: unknown; // Additional fields stored as node data
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Usage
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import {
|
|
167
|
+
createProofGraph,
|
|
168
|
+
addProof,
|
|
169
|
+
addAnchor,
|
|
170
|
+
verifyProofGraph,
|
|
171
|
+
findProofsBySubject,
|
|
172
|
+
resolveCurrentProofSet,
|
|
173
|
+
exportProofGraph,
|
|
174
|
+
importProofGraph,
|
|
175
|
+
} from '@totemsdk/proofgraph';
|
|
176
|
+
import { createProof, signProof, createAnchorCommitment } from '@totemsdk/proof';
|
|
177
|
+
|
|
178
|
+
// 1. Build a signed proof
|
|
179
|
+
const unsigned = createProof({
|
|
180
|
+
kind: 'ownership',
|
|
181
|
+
subject: { id: 'totem:subject:asset:nft-001', kind: 'nft' },
|
|
182
|
+
issuer: 'MxROOT...',
|
|
183
|
+
});
|
|
184
|
+
const signed = signProof(unsigned, seed, keyIndex);
|
|
185
|
+
|
|
186
|
+
// 2. Build the graph
|
|
187
|
+
let graph = createProofGraph();
|
|
188
|
+
graph = addProof(graph, signed);
|
|
189
|
+
|
|
190
|
+
// 3. Add an on-chain anchor
|
|
191
|
+
graph = addAnchor(graph, {
|
|
192
|
+
hash: createAnchorCommitment(signed),
|
|
193
|
+
proofId: signed.proofId,
|
|
194
|
+
txId: '0xabc...',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// 4. Query
|
|
198
|
+
const forAsset = findProofsBySubject(graph, 'totem:subject:asset:nft-001');
|
|
199
|
+
console.log('Proofs:', forAsset.length);
|
|
200
|
+
|
|
201
|
+
const active = resolveCurrentProofSet(graph);
|
|
202
|
+
console.log('Active proofs:', active.length);
|
|
203
|
+
|
|
204
|
+
// 5. Verify all signatures
|
|
205
|
+
const result = verifyProofGraph(graph);
|
|
206
|
+
console.assert(result.valid, result.invalidProofs);
|
|
207
|
+
|
|
208
|
+
// 6. Serialize / deserialize (graphId is verified on import)
|
|
209
|
+
const json = exportProofGraph(graph);
|
|
210
|
+
const restored = importProofGraph(json);
|
|
211
|
+
console.assert(restored.graphId === graph.graphId);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Design notes
|
|
215
|
+
|
|
216
|
+
### Content-deterministic `graphId`
|
|
217
|
+
|
|
218
|
+
`computeProofGraphId` excludes `node.createdAt` and `graph.metadata` from the hash. Two graphs built at different times from identical proofs will have the same `graphId`. This makes the ID suitable as a stable content address for caching and deduplication.
|
|
219
|
+
|
|
220
|
+
### Immutable + idempotent mutations
|
|
221
|
+
|
|
222
|
+
Every function that modifies a graph (`addProof`, `addEdge`, etc.) returns a new `ProofGraph` with a recomputed `graphId`. Calling the same add function twice with the same arguments returns a graph identical to calling it once — duplicate nodes and edges are silently ignored.
|
|
223
|
+
|
|
224
|
+
### `ProofGraphStoragePort`
|
|
225
|
+
|
|
226
|
+
This package exports a `ProofGraphStoragePort` interface only. If you need persistent storage, implement the interface in your own adapter:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import type { ProofGraphStoragePort, ProofGraph } from '@totemsdk/proofgraph';
|
|
230
|
+
|
|
231
|
+
class MyStorage implements ProofGraphStoragePort {
|
|
232
|
+
async save(graph: ProofGraph): Promise<void> { /* ... */ }
|
|
233
|
+
async load(graphId: string): Promise<ProofGraph | null> { /* ... */ }
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Related packages
|
|
238
|
+
|
|
239
|
+
- [`@totemsdk/proof`](https://www.npmjs.com/package/@totemsdk/proof) — signed proof creation and verification
|
|
240
|
+
- [`@totemsdk/proof-integritas`](https://www.npmjs.com/package/@totemsdk/proof-integritas) — Integritas v2 on-chain anchoring
|
|
241
|
+
- [`@totemsdk/identity`](https://www.npmjs.com/package/@totemsdk/identity) — identity documents and claims
|
|
242
|
+
- [`@totemsdk/core`](https://www.npmjs.com/package/@totemsdk/core) — WOTS signing primitives
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT
|
package/dist/canonical.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Canonical JSON and ID rules for @totemsdk/proofgraph.
|
|
3
3
|
*
|
|
4
|
-
* canonicalJson
|
|
5
|
-
*
|
|
4
|
+
* canonicalJson and toHex are re-exported from @totemsdk/proof, which is the
|
|
5
|
+
* canonical implementation across the Totem SDK.
|
|
6
6
|
*
|
|
7
7
|
* ID rules:
|
|
8
8
|
* Node: type + ":" + refId (e.g. "proof:totem:proof:abc123")
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* Graph: sha3_256("totem-proofgraph" + canonicalJson(nodeHashes) + canonicalJson(sortedEdges))
|
|
11
11
|
* — node.createdAt and graph.metadata excluded from graphId (time-variant / mutable)
|
|
12
12
|
*/
|
|
13
|
+
import { canonicalJson, toHex } from '@totemsdk/proof';
|
|
13
14
|
import type { ProofGraphNode, ProofGraphEdge } from './types.js';
|
|
14
|
-
export
|
|
15
|
-
export declare function canonicalJson(value: unknown): string;
|
|
15
|
+
export { canonicalJson, toHex };
|
|
16
16
|
export declare function computeNodeId(type: string, refId: string): string;
|
|
17
17
|
export declare function computeEdgeId(type: string, from: string, to: string, proofId: string | undefined, data: Record<string, unknown> | undefined): string;
|
|
18
18
|
export declare function computeProofGraphId(nodes: ProofGraphNode[], edges: ProofGraphEdge[]): string;
|
package/dist/canonical.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Canonical JSON and ID rules for @totemsdk/proofgraph.
|
|
4
4
|
*
|
|
5
|
-
* canonicalJson
|
|
6
|
-
*
|
|
5
|
+
* canonicalJson and toHex are re-exported from @totemsdk/proof, which is the
|
|
6
|
+
* canonical implementation across the Totem SDK.
|
|
7
7
|
*
|
|
8
8
|
* ID rules:
|
|
9
9
|
* Node: type + ":" + refId (e.g. "proof:totem:proof:abc123")
|
|
@@ -12,37 +12,22 @@
|
|
|
12
12
|
* — node.createdAt and graph.metadata excluded from graphId (time-variant / mutable)
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.toHex =
|
|
16
|
-
exports.canonicalJson = canonicalJson;
|
|
15
|
+
exports.toHex = exports.canonicalJson = void 0;
|
|
17
16
|
exports.computeNodeId = computeNodeId;
|
|
18
17
|
exports.computeEdgeId = computeEdgeId;
|
|
19
18
|
exports.computeProofGraphId = computeProofGraphId;
|
|
20
19
|
exports.recomputeGraphId = recomputeGraphId;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.join('');
|
|
26
|
-
}
|
|
27
|
-
function canonicalJson(value) {
|
|
28
|
-
if (value === null || typeof value !== 'object') {
|
|
29
|
-
return JSON.stringify(value);
|
|
30
|
-
}
|
|
31
|
-
if (Array.isArray(value)) {
|
|
32
|
-
return '[' + value.map(canonicalJson).join(',') + ']';
|
|
33
|
-
}
|
|
34
|
-
const obj = value;
|
|
35
|
-
const keys = Object.keys(obj).sort();
|
|
36
|
-
const pairs = keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`);
|
|
37
|
-
return '{' + pairs.join(',') + '}';
|
|
38
|
-
}
|
|
20
|
+
const core_1 = require("@totemsdk/core");
|
|
21
|
+
const proof_1 = require("@totemsdk/proof");
|
|
22
|
+
Object.defineProperty(exports, "canonicalJson", { enumerable: true, get: function () { return proof_1.canonicalJson; } });
|
|
23
|
+
Object.defineProperty(exports, "toHex", { enumerable: true, get: function () { return proof_1.toHex; } });
|
|
39
24
|
function computeNodeId(type, refId) {
|
|
40
25
|
return `${type}:${refId}`;
|
|
41
26
|
}
|
|
42
27
|
function computeEdgeId(type, from, to, proofId, data) {
|
|
43
|
-
const canonicalDataHash = toHex((0,
|
|
28
|
+
const canonicalDataHash = (0, proof_1.toHex)((0, core_1.sha3_256)(new TextEncoder().encode((0, proof_1.canonicalJson)(data ?? {}))));
|
|
44
29
|
const input = 'totem-proofgraph-edge' + type + from + to + (proofId ?? '') + canonicalDataHash;
|
|
45
|
-
return toHex((0,
|
|
30
|
+
return (0, proof_1.toHex)((0, core_1.sha3_256)(new TextEncoder().encode(input)));
|
|
46
31
|
}
|
|
47
32
|
/**
|
|
48
33
|
* Content-stable projection of a node for graphId computation.
|
|
@@ -58,8 +43,8 @@ function computeProofGraphId(nodes, edges) {
|
|
|
58
43
|
.sort((a, b) => a.id.localeCompare(b.id))
|
|
59
44
|
.map(nodeForHashing);
|
|
60
45
|
const sortedEdges = [...edges].sort((a, b) => a.id.localeCompare(b.id));
|
|
61
|
-
const input = 'totem-proofgraph' + canonicalJson(sortedNodes) + canonicalJson(sortedEdges);
|
|
62
|
-
return toHex((0,
|
|
46
|
+
const input = 'totem-proofgraph' + (0, proof_1.canonicalJson)(sortedNodes) + (0, proof_1.canonicalJson)(sortedEdges);
|
|
47
|
+
return (0, proof_1.toHex)((0, core_1.sha3_256)(new TextEncoder().encode(input)));
|
|
63
48
|
}
|
|
64
49
|
function recomputeGraphId(graph) {
|
|
65
50
|
return computeProofGraphId(graph.nodes, graph.edges);
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.importProofGraph = exports.exportProofGraph = exports.verifyGraphProofs = exports.verifyProofGraph = exports.reachableFrom = exports.getEdgesBetween = exports.getManifestsForAddress = exports.resolveCurrentProofSet = exports.getProofLineage = exports.getEvidenceTrail = exports.findConflicts = exports.findSupersessions = exports.findRevocations = exports.findAnchorsForProof = exports.findProofsByIssuer = exports.findProofsBySubject = exports.getProofNodes = exports.getEdgesByType = exports.getEdgesTo = exports.getEdgesFrom = exports.findNode = exports.addAnchor = exports.addReceiptLike = exports.addManifest = exports.addIdentityClaim = exports.addProof = exports.addIdentityDocument = exports.addEdge = exports.addNode = exports.buildEdge = exports.setGraphMetadata = exports.createProofGraph = exports.recomputeGraphId = exports.computeProofGraphId = exports.computeEdgeId = exports.computeNodeId = exports.canonicalJson = exports.toHex = void 0;
|
|
19
|
-
var canonical_js_1 = require("./canonical.js");
|
|
19
|
+
var canonical_js_1 = require("./canonical.js"); // canonicalJson / toHex re-exported from @totemsdk/proof
|
|
20
20
|
Object.defineProperty(exports, "toHex", { enumerable: true, get: function () { return canonical_js_1.toHex; } });
|
|
21
21
|
Object.defineProperty(exports, "canonicalJson", { enumerable: true, get: function () { return canonical_js_1.canonicalJson; } });
|
|
22
22
|
Object.defineProperty(exports, "computeNodeId", { enumerable: true, get: function () { return canonical_js_1.computeNodeId; } });
|
package/dist/io.d.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Serialization helpers for ProofGraph.
|
|
3
3
|
*
|
|
4
4
|
* exportProofGraph: JSON-stringify the graph for storage or transport.
|
|
5
|
-
* importProofGraph: parse JSON, recompute graphId,
|
|
5
|
+
* importProofGraph: parse JSON, validate every node/edge, recompute graphId,
|
|
6
|
+
* and throw if verification fails.
|
|
6
7
|
*
|
|
7
|
-
* The graphId guards against silent corruption in transit.
|
|
8
|
+
* The graphId guards against silent corruption in transit. Node/edge
|
|
9
|
+
* validation prevents hostile or malformed input from entering the system.
|
|
8
10
|
*/
|
|
9
11
|
import type { ProofGraph } from './types.js';
|
|
10
12
|
/**
|
|
@@ -15,9 +17,10 @@ export declare function exportProofGraph(graph: ProofGraph): string;
|
|
|
15
17
|
/**
|
|
16
18
|
* Deserialize a ProofGraph from a JSON string.
|
|
17
19
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
+
* Validates every node and edge against structural constraints, then
|
|
21
|
+
* recomputes the graphId from nodes + edges. Throws if any constraint
|
|
22
|
+
* is violated or if the recomputed graphId does not match the stored value.
|
|
20
23
|
*
|
|
21
|
-
* @throws Error if graphId verification fails.
|
|
24
|
+
* @throws Error if parsing, validation, or graphId verification fails.
|
|
22
25
|
*/
|
|
23
26
|
export declare function importProofGraph(json: string): ProofGraph;
|
package/dist/io.js
CHANGED
|
@@ -3,14 +3,120 @@
|
|
|
3
3
|
* Serialization helpers for ProofGraph.
|
|
4
4
|
*
|
|
5
5
|
* exportProofGraph: JSON-stringify the graph for storage or transport.
|
|
6
|
-
* importProofGraph: parse JSON, recompute graphId,
|
|
6
|
+
* importProofGraph: parse JSON, validate every node/edge, recompute graphId,
|
|
7
|
+
* and throw if verification fails.
|
|
7
8
|
*
|
|
8
|
-
* The graphId guards against silent corruption in transit.
|
|
9
|
+
* The graphId guards against silent corruption in transit. Node/edge
|
|
10
|
+
* validation prevents hostile or malformed input from entering the system.
|
|
9
11
|
*/
|
|
10
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
13
|
exports.exportProofGraph = exportProofGraph;
|
|
12
14
|
exports.importProofGraph = importProofGraph;
|
|
13
15
|
const canonical_js_1 = require("./canonical.js");
|
|
16
|
+
const VALID_NODE_TYPES = new Set([
|
|
17
|
+
'proof', 'identity', 'identity-claim', 'manifest', 'address',
|
|
18
|
+
'subject', 'evidence', 'anchor', 'receipt', 'payment', 'device',
|
|
19
|
+
'service', 'policy', 'custom',
|
|
20
|
+
]);
|
|
21
|
+
const VALID_EDGE_TYPES = new Set([
|
|
22
|
+
'proves', 'issued_by', 'signed_by', 'about', 'references',
|
|
23
|
+
'derived_from', 'supports', 'contradicts', 'supersedes', 'revokes',
|
|
24
|
+
'anchored_to', 'delegates_to', 'controls', 'depends_on',
|
|
25
|
+
'manifests_as', 'conflicts_with',
|
|
26
|
+
]);
|
|
27
|
+
const MAX_NODES = 10000;
|
|
28
|
+
const MAX_EDGES = 50000;
|
|
29
|
+
const MAX_STRING_LENGTH = 4096;
|
|
30
|
+
const MAX_DATA_DEPTH = 10;
|
|
31
|
+
const MAX_DATA_KEYS = 256;
|
|
32
|
+
/**
|
|
33
|
+
* Validate a single ProofGraphNode.
|
|
34
|
+
* Throws if the node violates structural constraints.
|
|
35
|
+
*/
|
|
36
|
+
function validateNode(node, index) {
|
|
37
|
+
if (!node || typeof node !== 'object') {
|
|
38
|
+
throw new Error(`importProofGraph: nodes[${index}] is not an object`);
|
|
39
|
+
}
|
|
40
|
+
const n = node;
|
|
41
|
+
if (typeof n.id !== 'string' || n.id.length === 0) {
|
|
42
|
+
throw new Error(`importProofGraph: nodes[${index}] missing or empty id`);
|
|
43
|
+
}
|
|
44
|
+
if (typeof n.refId !== 'string' || n.refId.length === 0) {
|
|
45
|
+
throw new Error(`importProofGraph: nodes[${index}] missing or empty refId`);
|
|
46
|
+
}
|
|
47
|
+
if (n.id.length > MAX_STRING_LENGTH) {
|
|
48
|
+
throw new Error(`importProofGraph: nodes[${index}].id exceeds ${MAX_STRING_LENGTH} chars`);
|
|
49
|
+
}
|
|
50
|
+
if (n.refId.length > MAX_STRING_LENGTH) {
|
|
51
|
+
throw new Error(`importProofGraph: nodes[${index}].refId exceeds ${MAX_STRING_LENGTH} chars`);
|
|
52
|
+
}
|
|
53
|
+
if (!VALID_NODE_TYPES.has(String(n.type))) {
|
|
54
|
+
throw new Error(`importProofGraph: nodes[${index}] invalid type "${String(n.type)}"`);
|
|
55
|
+
}
|
|
56
|
+
if (typeof n.createdAt !== 'number' || n.createdAt < 0 || !Number.isFinite(n.createdAt)) {
|
|
57
|
+
throw new Error(`importProofGraph: nodes[${index}] invalid or negative createdAt`);
|
|
58
|
+
}
|
|
59
|
+
if (n.data !== undefined && n.data !== null) {
|
|
60
|
+
if (typeof n.data !== 'object') {
|
|
61
|
+
throw new Error(`importProofGraph: nodes[${index}].data must be an object`);
|
|
62
|
+
}
|
|
63
|
+
const keys = Object.keys(n.data);
|
|
64
|
+
if (keys.length > MAX_DATA_KEYS) {
|
|
65
|
+
throw new Error(`importProofGraph: nodes[${index}].data exceeds ${MAX_DATA_KEYS} keys`);
|
|
66
|
+
}
|
|
67
|
+
checkDepth(n.data, 0, MAX_DATA_DEPTH, `nodes[${index}].data`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validate a single ProofGraphEdge.
|
|
72
|
+
* Throws if the edge violates structural constraints.
|
|
73
|
+
*/
|
|
74
|
+
function validateEdge(edge, index) {
|
|
75
|
+
if (!edge || typeof edge !== 'object') {
|
|
76
|
+
throw new Error(`importProofGraph: edges[${index}] is not an object`);
|
|
77
|
+
}
|
|
78
|
+
const e = edge;
|
|
79
|
+
if (typeof e.id !== 'string' || e.id.length === 0) {
|
|
80
|
+
throw new Error(`importProofGraph: edges[${index}] missing or empty id`);
|
|
81
|
+
}
|
|
82
|
+
if (typeof e.from !== 'string' || e.from.length === 0) {
|
|
83
|
+
throw new Error(`importProofGraph: edges[${index}] missing or empty from`);
|
|
84
|
+
}
|
|
85
|
+
if (typeof e.to !== 'string' || e.to.length === 0) {
|
|
86
|
+
throw new Error(`importProofGraph: edges[${index}] missing or empty to`);
|
|
87
|
+
}
|
|
88
|
+
if (e.id.length > MAX_STRING_LENGTH || e.from.length > MAX_STRING_LENGTH || e.to.length > MAX_STRING_LENGTH) {
|
|
89
|
+
throw new Error(`importProofGraph: edges[${index}] string field exceeds ${MAX_STRING_LENGTH} chars`);
|
|
90
|
+
}
|
|
91
|
+
if (!VALID_EDGE_TYPES.has(String(e.type))) {
|
|
92
|
+
throw new Error(`importProofGraph: edges[${index}] invalid type "${String(e.type)}"`);
|
|
93
|
+
}
|
|
94
|
+
if (e.proofId !== undefined && typeof e.proofId !== 'string') {
|
|
95
|
+
throw new Error(`importProofGraph: edges[${index}].proofId must be a string`);
|
|
96
|
+
}
|
|
97
|
+
if (e.data !== undefined && e.data !== null) {
|
|
98
|
+
if (typeof e.data !== 'object') {
|
|
99
|
+
throw new Error(`importProofGraph: edges[${index}].data must be an object`);
|
|
100
|
+
}
|
|
101
|
+
checkDepth(e.data, 0, MAX_DATA_DEPTH, `edges[${index}].data`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Recursive depth check on nested data objects to prevent deep-object attacks.
|
|
106
|
+
*/
|
|
107
|
+
function checkDepth(obj, depth, maxDepth, path) {
|
|
108
|
+
if (depth > maxDepth) {
|
|
109
|
+
throw new Error(`importProofGraph: ${path} exceeds max nesting depth of ${maxDepth}`);
|
|
110
|
+
}
|
|
111
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
112
|
+
if (typeof key === 'string' && key.length > MAX_STRING_LENGTH) {
|
|
113
|
+
throw new Error(`importProofGraph: ${path} key exceeds ${MAX_STRING_LENGTH} chars`);
|
|
114
|
+
}
|
|
115
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Uint8Array)) {
|
|
116
|
+
checkDepth(value, depth + 1, maxDepth, `${path}.${key}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
14
120
|
/**
|
|
15
121
|
* Serialize a ProofGraph to a JSON string.
|
|
16
122
|
* The stored graphId was computed at mutation time and is included as-is.
|
|
@@ -21,10 +127,11 @@ function exportProofGraph(graph) {
|
|
|
21
127
|
/**
|
|
22
128
|
* Deserialize a ProofGraph from a JSON string.
|
|
23
129
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
130
|
+
* Validates every node and edge against structural constraints, then
|
|
131
|
+
* recomputes the graphId from nodes + edges. Throws if any constraint
|
|
132
|
+
* is violated or if the recomputed graphId does not match the stored value.
|
|
26
133
|
*
|
|
27
|
-
* @throws Error if graphId verification fails.
|
|
134
|
+
* @throws Error if parsing, validation, or graphId verification fails.
|
|
28
135
|
*/
|
|
29
136
|
function importProofGraph(json) {
|
|
30
137
|
let parsed;
|
|
@@ -41,9 +148,26 @@ function importProofGraph(json) {
|
|
|
41
148
|
if (!Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) {
|
|
42
149
|
throw new Error('importProofGraph: missing nodes or edges arrays');
|
|
43
150
|
}
|
|
151
|
+
if (graph.nodes.length > MAX_NODES) {
|
|
152
|
+
throw new Error(`importProofGraph: node count ${graph.nodes.length} exceeds limit ${MAX_NODES}`);
|
|
153
|
+
}
|
|
154
|
+
if (graph.edges.length > MAX_EDGES) {
|
|
155
|
+
throw new Error(`importProofGraph: edge count ${graph.edges.length} exceeds limit ${MAX_EDGES}`);
|
|
156
|
+
}
|
|
157
|
+
// Validate every node and edge
|
|
158
|
+
for (let i = 0; i < graph.nodes.length; i++) {
|
|
159
|
+
validateNode(graph.nodes[i], i);
|
|
160
|
+
}
|
|
161
|
+
for (let i = 0; i < graph.edges.length; i++) {
|
|
162
|
+
validateEdge(graph.edges[i], i);
|
|
163
|
+
}
|
|
164
|
+
// Recompute and verify graphId
|
|
165
|
+
const originalId = typeof graph.graphId === 'string' ? graph.graphId : '';
|
|
44
166
|
const recomputed = (0, canonical_js_1.recomputeGraphId)(graph);
|
|
45
|
-
|
|
46
|
-
|
|
167
|
+
// If a stored graphId exists, verify it matches
|
|
168
|
+
if (originalId !== '' && recomputed !== originalId) {
|
|
169
|
+
throw new Error(`importProofGraph: graphId mismatch — stored="${originalId}" recomputed="${recomputed}"`);
|
|
47
170
|
}
|
|
48
|
-
|
|
171
|
+
// Return with graphId set to the recomputed value for consistency
|
|
172
|
+
return { ...graph, graphId: recomputed };
|
|
49
173
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/proofgraph",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Local deterministic proof relationship graph for Totem Edge — indexes proofs, identities, manifests, and subjects into a queryable DAG",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Local deterministic proof relationship graph for Totem Edge — indexes proofs, identities, manifests, and subjects into a queryable content-addressed DAG",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -13,30 +13,23 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
|
+
"rust/pkg/",
|
|
17
|
+
"rust/pkg-node/",
|
|
16
18
|
"README.md",
|
|
17
19
|
"LICENSE"
|
|
18
20
|
],
|
|
19
21
|
"dependencies": {
|
|
20
|
-
"@totemsdk/
|
|
21
|
-
"@totemsdk/
|
|
22
|
-
"@totemsdk/manifest": "0.1.
|
|
23
|
-
"@totemsdk/identity": "0.1.
|
|
24
|
-
},
|
|
25
|
-
"peerDependencies": {
|
|
26
|
-
"@noble/hashes": "^2.0.0"
|
|
27
|
-
},
|
|
28
|
-
"peerDependenciesMeta": {
|
|
29
|
-
"@noble/hashes": {
|
|
30
|
-
"optional": false
|
|
31
|
-
}
|
|
22
|
+
"@totemsdk/core": "^1.2.8",
|
|
23
|
+
"@totemsdk/proof": "^0.1.2",
|
|
24
|
+
"@totemsdk/manifest": "^0.1.3",
|
|
25
|
+
"@totemsdk/identity": "^0.1.4"
|
|
32
26
|
},
|
|
33
27
|
"devDependencies": {
|
|
34
|
-
"@
|
|
35
|
-
"@types/jest": "^29.0.0",
|
|
28
|
+
"@types/jest": "^30.0.0",
|
|
36
29
|
"@types/node": "^20.0.0",
|
|
37
|
-
"jest": "^
|
|
30
|
+
"jest": "^30.4.2",
|
|
38
31
|
"ts-jest": "^29.0.0",
|
|
39
|
-
"typescript": "^
|
|
32
|
+
"typescript": "^7.0.2"
|
|
40
33
|
},
|
|
41
34
|
"engines": {
|
|
42
35
|
"node": ">=18.0.0"
|
|
@@ -46,19 +39,35 @@
|
|
|
46
39
|
},
|
|
47
40
|
"license": "MIT",
|
|
48
41
|
"author": "Totem SDK",
|
|
49
|
-
"homepage": "https://
|
|
42
|
+
"homepage": "https://totem.ing",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/totem-sdk/totem-sdk/issues"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/totem-sdk/totem-sdk.git",
|
|
49
|
+
"directory": "packages/proofgraph"
|
|
50
|
+
},
|
|
50
51
|
"keywords": [
|
|
51
52
|
"totem",
|
|
52
53
|
"totemsdk",
|
|
53
54
|
"minima",
|
|
55
|
+
"blockchain",
|
|
56
|
+
"quantum-resistant",
|
|
57
|
+
"wots",
|
|
58
|
+
"utxo",
|
|
54
59
|
"proof",
|
|
55
60
|
"graph",
|
|
56
61
|
"dag",
|
|
57
|
-
"identity"
|
|
62
|
+
"identity",
|
|
63
|
+
"attestation",
|
|
64
|
+
"verification",
|
|
65
|
+
"content-addressed"
|
|
58
66
|
],
|
|
59
67
|
"scripts": {
|
|
60
|
-
"build": "tsc",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
68
|
+
"build": "npm run build:wasm && tsc && node ../../scripts/clean-wasm-output-ignores.mjs",
|
|
69
|
+
"build:wasm": "cd rust && wasm-pack build --target bundler --out-dir pkg && wasm-pack build --target nodejs --out-dir pkg-node",
|
|
70
|
+
"clean": "rm -rf dist rust/pkg rust/pkg-node rust/target",
|
|
71
|
+
"test": "jest"
|
|
63
72
|
}
|
|
64
73
|
}
|