@agent-custody/receipts 0.1.2 → 0.1.4

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 CHANGED
@@ -231,6 +231,7 @@ src/sdk/index.ts the interceptor: policy decision, record, wrap(tool fn)
231
231
  src/sdk/claude.ts Claude Code command hook and Claude Agent SDK in-process hooks
232
232
  src/sdk/openai-agents.ts, vercel-ai.ts, langchain.ts framework adapters, tested against the real packages
233
233
  src/sidecar.ts the SDK issuer behind a local HTTP API, for agents in other languages
234
+ vectors/ conformance vectors: receipts, keys, logs, proofs, and expected verdicts; `bun run vectors` regenerates them
234
235
  src/verify.ts offline verification, the human-readable report, and the audit that a later log extends an earlier one
235
236
  src/cli.ts keygen, grant, gateway, hook, serve, log, verify, audit
236
237
  src/index.ts the package's public surface; adapters are exported on ./sdk/<framework> subpaths
@@ -253,6 +254,8 @@ The design is two producers feeding one verifier. The SDK is the top of the funn
253
254
  - SDK core: policy decision, record, and a generic `wrap(tool, fn)` for any framework whose tools are functions.
254
255
  - Claude Code command hook for PreToolUse, PostToolUse, and PostToolUseFailure, with blocking on deny.
255
256
  - Claude Agent SDK in-process hooks over the same handler.
257
+ - The forwarded call carries the receipt id and the attested agent and principal in `_meta`, so a stateful upstream can cite the receipt; the memory server in `@agent-custody/state` runs this way.
258
+ - Conformance vectors, generated by the test suite and published with the spec, and a browser verifier on agent-custody.dev that passes all of them.
256
259
  - Sidecar: the SDK issuer behind a local HTTP API (`serve`), with a Python package on PyPI-ready footing and Go, Java, and Rust clients, so agents in any language get the same receipts from one signing implementation.
257
260
  - Consistency proofs between tree heads (RFC 9162), served by the log and checked by the `audit` command, so an auditor holding an old tree head can prove nothing before it was rewritten.
258
261
  - Remote log: the issuer can append to a log run by someone else over HTTP, whose key then signs the tree heads, so a verifier learns the receipt was in a log the operator could not rewrite. Includes the reference log server, bearer-token auth, and a root endpoint for auditors.
package/dist/crypto.d.ts CHANGED
@@ -21,6 +21,8 @@ export declare function writeKeyPair(kp: KeyPair, dir: string, name: string): {
21
21
  };
22
22
  export declare function loadPrivateKey(path: string): KeyPair;
23
23
  export declare function loadPublicKey(path: string): PublicKeyRef;
24
+ /** A public key from its SPKI PEM text, as found in a .pub file or a conformance vector. */
25
+ export declare function publicKeyFromPem(pem: string): PublicKeyRef;
24
26
  export interface Envelope {
25
27
  payloadType: string;
26
28
  payload: string;
package/dist/crypto.js CHANGED
@@ -50,7 +50,11 @@ export function loadPrivateKey(path) {
50
50
  return { privateKey, publicKey, keyid: keyidOf(publicKey) };
51
51
  }
52
52
  export function loadPublicKey(path) {
53
- const publicKey = createPublicKey(readFileSync(path));
53
+ return publicKeyFromPem(readFileSync(path, "utf8"));
54
+ }
55
+ /** A public key from its SPKI PEM text, as found in a .pub file or a conformance vector. */
56
+ export function publicKeyFromPem(pem) {
57
+ const publicKey = createPublicKey(pem);
54
58
  return { publicKey, keyid: keyidOf(publicKey) };
55
59
  }
56
60
  function pae(payloadType, payload) {
package/dist/gateway.d.ts CHANGED
@@ -4,6 +4,9 @@ import { type Delegation } from "./delegation.ts";
4
4
  export declare const GATEWAY_VERSION = "0.1.0";
5
5
  export declare const RECEIPT_META_KEY = "agent-custody/receipt";
6
6
  export declare const MODEL_META_KEY = "agent-custody/model";
7
+ /** Set by the gateway on the call it forwards upstream: the receipt id, and the agent and principal from the attested grant. */
8
+ export declare const AGENT_META_KEY = "agent-custody/agent";
9
+ export declare const PRINCIPAL_META_KEY = "agent-custody/principal";
7
10
  export interface CallParams {
8
11
  name: string;
9
12
  arguments?: Record<string, unknown>;
package/dist/gateway.js CHANGED
@@ -15,6 +15,9 @@ import { evaluate, policyDigest } from "./policy.js";
15
15
  export const GATEWAY_VERSION = "0.1.0";
16
16
  export const RECEIPT_META_KEY = "agent-custody/receipt";
17
17
  export const MODEL_META_KEY = "agent-custody/model";
18
+ /** Set by the gateway on the call it forwards upstream: the receipt id, and the agent and principal from the attested grant. */
19
+ export const AGENT_META_KEY = "agent-custody/agent";
20
+ export const PRINCIPAL_META_KEY = "agent-custody/principal";
18
21
  function resolveFactArgs(template, args) {
19
22
  const out = {};
20
23
  for (const [k, v] of Object.entries(template)) {
@@ -58,7 +61,7 @@ export async function createGateway(cfg) {
58
61
  const issuer = createIssuer(gatewayKey, cfg.receiptsDir, openLog(cfg, gatewayKey));
59
62
  const upstream = new Client({ name: "agent-custody-gateway", version: GATEWAY_VERSION });
60
63
  await upstream.connect(new StdioClientTransport({ command: cfg.upstream.command, args: cfg.upstream.args, env: cfg.upstream.env, stderr: "inherit" }));
61
- const callUpstream = async (name, args) => (await upstream.callTool({ name, arguments: args }));
64
+ const callUpstream = async (name, args, meta) => (await upstream.callTool({ name, arguments: args, ...(meta ? { _meta: meta } : {}) }));
62
65
  async function gatherFacts(tool, args) {
63
66
  const facts = {};
64
67
  for (const f of cfg.facts.filter((f) => f.forTools.includes(tool))) {
@@ -98,7 +101,9 @@ export async function createGateway(cfg) {
98
101
  }
99
102
  if (policy.decision === "allow") {
100
103
  try {
101
- const result = await callUpstream(tool, args);
104
+ // The upstream learns which receipt this call is, and who the grant says is calling. An upstream that keeps
105
+ // state, such as the memory server, cites the receipt as the source of what it stores.
106
+ const result = await callUpstream(tool, args, { [RECEIPT_META_KEY]: receiptId, [AGENT_META_KEY]: delegation.agent, [PRINCIPAL_META_KEY]: delegation.principal });
102
107
  execution = { status: result.isError ? "failed" : "executed", result, resultDigest: digestOf(result), provenance: "observed" };
103
108
  }
104
109
  catch (e) {
package/docs/usage.md CHANGED
@@ -190,6 +190,10 @@ Any other MCP client works the same way: Python's `mcp` package, LangGraph's MCP
190
190
 
191
191
  The receipt id is the file name under `receiptsDir`.
192
192
 
193
+ ## What the upstream gets
194
+
195
+ The call the gateway forwards carries three `_meta` keys the agent cannot set: `agent-custody/receipt`, the id of the receipt being issued for this call; `agent-custody/agent` and `agent-custody/principal`, from the signed delegation grant. An upstream that keeps state can cite the receipt as the source of what it stores and record the attested caller rather than a claimed one. The memory server in `@agent-custody/state` does exactly that. Fact lookups do not carry them; only the forwarded call does.
196
+
193
197
  ## Operational notes
194
198
 
195
199
  - **Money is integer minor units.** Cedar has no floating point. A float in `args` that a policy touches is an evaluation error, which is a deny.
@@ -98,6 +98,10 @@ A gateway receipt does **not** support:
98
98
  - that the model named in `model` produced the call. No hosted provider signs model identity.
99
99
  - that the gateway operator is honest. The operator holds the gateway key. Against a dishonest operator you need a log copy taken out of their control, or a signer they do not control. See the threat model table in the README.
100
100
 
101
+ ## In the browser
102
+
103
+ [agent-custody.dev/verify](https://agent-custody.dev/verify) runs the same checks in the page over WebCrypto, nothing uploaded. It is a second implementation of this verifier, and it passes every published [conformance vector](https://agent-custody.dev/receipt/vectors).
104
+
101
105
  ## Programmatic verification
102
106
 
103
107
  ```ts
@@ -157,3 +161,7 @@ Programmatically, `auditExtends(older.treeHead, newer.treeHead, proof, keys)` re
157
161
 
158
162
  The remote log also serves `GET /head`, its current tree head signed with the log's key, so an auditor can record heads on a schedule and later audit any two of them without holding a receipt for each.
159
163
 
164
+ ## Conformance vectors
165
+
166
+ `vectors/` in the package holds fixed receipts, keys, logs, proofs, and the verdicts this verifier produces for them, generated by `bun run vectors` and checked by the test suite on every run. A verifier written elsewhere proves it agrees by reproducing every verdict. They are published at [agent-custody.dev/receipt/vectors](https://agent-custody.dev/receipt/vectors).
167
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-custody/receipts",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Chain of custody for AI agents: signed, independently verifiable receipts for tool calls. MCP gateway + Cedar policy + Merkle transparency log",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -43,13 +43,15 @@
43
43
  },
44
44
  "files": [
45
45
  "dist",
46
- "docs"
46
+ "docs",
47
+ "vectors"
47
48
  ],
48
49
  "scripts": {
49
50
  "build": "tsc -p tsconfig.build.json",
50
51
  "typecheck": "tsc --noEmit",
51
52
  "test": "vitest run",
52
- "demo": "tsx scripts/demo.ts"
53
+ "demo": "tsx scripts/demo.ts",
54
+ "vectors": "tsx scripts/vectors.ts"
53
55
  },
54
56
  "engines": {
55
57
  "node": ">=22"
@@ -0,0 +1,147 @@
1
+ {
2
+ "version": "0.2",
3
+ "note": "auditExtends(older, newer, proof, keys): both tree heads must verify against a listed key; the proof is the log's consistency proof between their sizes.",
4
+ "cases": [
5
+ {
6
+ "name": "remote-log-extends",
7
+ "description": "The tree head from the first remote receipt and the log's later head, with the proof the log served.",
8
+ "older": {
9
+ "payloadType": "application/vnd.agent-custody.treehead+json",
10
+ "payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
11
+ "signatures": [
12
+ {
13
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
14
+ "sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
15
+ }
16
+ ]
17
+ },
18
+ "newer": {
19
+ "payloadType": "application/vnd.agent-custody.treehead+json",
20
+ "payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
21
+ "signatures": [
22
+ {
23
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
24
+ "sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
25
+ }
26
+ ]
27
+ },
28
+ "proof": [
29
+ "4869289b5853ed3431c235a1f4e97d1d4205723e622dc71bd836018c62572723"
30
+ ],
31
+ "keys": [
32
+ "log"
33
+ ],
34
+ "expected": {
35
+ "ok": true,
36
+ "failing": []
37
+ }
38
+ },
39
+ {
40
+ "name": "remote-log-wrong-order",
41
+ "description": "The same heads the wrong way round.",
42
+ "older": {
43
+ "payloadType": "application/vnd.agent-custody.treehead+json",
44
+ "payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
45
+ "signatures": [
46
+ {
47
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
48
+ "sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
49
+ }
50
+ ]
51
+ },
52
+ "newer": {
53
+ "payloadType": "application/vnd.agent-custody.treehead+json",
54
+ "payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
55
+ "signatures": [
56
+ {
57
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
58
+ "sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
59
+ }
60
+ ]
61
+ },
62
+ "proof": [
63
+ "4869289b5853ed3431c235a1f4e97d1d4205723e622dc71bd836018c62572723"
64
+ ],
65
+ "keys": [
66
+ "log"
67
+ ],
68
+ "expected": {
69
+ "ok": false,
70
+ "failing": [
71
+ "older is not larger than newer"
72
+ ]
73
+ }
74
+ },
75
+ {
76
+ "name": "remote-log-wrong-key",
77
+ "description": "Tree heads checked against the app key, which did not sign them.",
78
+ "older": {
79
+ "payloadType": "application/vnd.agent-custody.treehead+json",
80
+ "payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
81
+ "signatures": [
82
+ {
83
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
84
+ "sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
85
+ }
86
+ ]
87
+ },
88
+ "newer": {
89
+ "payloadType": "application/vnd.agent-custody.treehead+json",
90
+ "payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
91
+ "signatures": [
92
+ {
93
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
94
+ "sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
95
+ }
96
+ ]
97
+ },
98
+ "proof": [
99
+ "4869289b5853ed3431c235a1f4e97d1d4205723e622dc71bd836018c62572723"
100
+ ],
101
+ "keys": [
102
+ "app"
103
+ ],
104
+ "expected": {
105
+ "ok": false,
106
+ "failing": [
107
+ "older tree head signature",
108
+ "newer tree head signature"
109
+ ]
110
+ }
111
+ },
112
+ {
113
+ "name": "remote-log-bad-proof",
114
+ "description": "A proof with a hash removed.",
115
+ "older": {
116
+ "payloadType": "application/vnd.agent-custody.treehead+json",
117
+ "payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
118
+ "signatures": [
119
+ {
120
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
121
+ "sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
122
+ }
123
+ ]
124
+ },
125
+ "newer": {
126
+ "payloadType": "application/vnd.agent-custody.treehead+json",
127
+ "payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
128
+ "signatures": [
129
+ {
130
+ "keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
131
+ "sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
132
+ }
133
+ ]
134
+ },
135
+ "proof": [],
136
+ "keys": [
137
+ "log"
138
+ ],
139
+ "expected": {
140
+ "ok": false,
141
+ "failing": [
142
+ "newer log extends older log"
143
+ ]
144
+ }
145
+ }
146
+ ]
147
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "version": "0.2",
3
+ "note": "canonical JSON: keys sorted, no whitespace, undefined dropped. digest = hex sha256 of the canonical string. keyid = hex sha256 of the SPKI DER public key. DSSE PAE = 'DSSEv1 ' + len(type) + ' ' + type + ' ' + len(payload) + ' ' + payload, signed with Ed25519.",
4
+ "values": [
5
+ {
6
+ "value": {
7
+ "b": 1,
8
+ "a": [
9
+ 3,
10
+ {
11
+ "z": null,
12
+ "y": "ÿ"
13
+ }
14
+ ]
15
+ },
16
+ "canonical": "{\"a\":[3,{\"y\":\"ÿ\",\"z\":null}],\"b\":1}",
17
+ "digest": "894fec1144463457d74d1eb8599cf2e04a42723765ec1d1aabb77d658d860f2b"
18
+ },
19
+ {
20
+ "value": "plain",
21
+ "canonical": "\"plain\"",
22
+ "digest": "945603a8f587786b463c3f94fce115c0fae88fac2728cc96ddf5981cf7f61741"
23
+ },
24
+ {
25
+ "value": 42,
26
+ "canonical": "42",
27
+ "digest": "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049"
28
+ },
29
+ {
30
+ "value": [
31
+ 1,
32
+ 2,
33
+ 3
34
+ ],
35
+ "canonical": "[1,2,3]",
36
+ "digest": "a615eeaee21de5179de080de8c3052c8da901138406ba71c38c032845f7d54f4"
37
+ },
38
+ {
39
+ "value": {
40
+ "nested": {
41
+ "deep": {
42
+ "deeper": true
43
+ }
44
+ }
45
+ },
46
+ "canonical": "{\"nested\":{\"deep\":{\"deeper\":true}}}",
47
+ "digest": "d64d85fa17b84e33888c36e22c9543527721099faee1e070943096aa3c9ba4c2"
48
+ },
49
+ {
50
+ "value": {},
51
+ "canonical": "{}",
52
+ "digest": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
53
+ }
54
+ ],
55
+ "keyid": {
56
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEb81f6KaNmH1pNR7KbeIUo9DW65Y0Qsfdxqbtnhs7cg=\n-----END PUBLIC KEY-----\n",
57
+ "keyid": "0ecde3bf114156b895ceb4ea63daa345271e8ee979f47bc69cbf0266bb93f6f5"
58
+ },
59
+ "dsse": {
60
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEb81f6KaNmH1pNR7KbeIUo9DW65Y0Qsfdxqbtnhs7cg=\n-----END PUBLIC KEY-----\n",
61
+ "envelope": {
62
+ "payloadType": "application/vnd.example+json",
63
+ "payload": "eyJoZWxsbyI6IndvcmxkIn0=",
64
+ "signatures": [
65
+ {
66
+ "keyid": "0ecde3bf114156b895ceb4ea63daa345271e8ee979f47bc69cbf0266bb93f6f5",
67
+ "sig": "wkH8ufuyC3BJldxmhFUCImAOo38KyQO/uQMITvTjgikPeE9GIv6OwYEFtQnjcBNzY0+MRTn++dGeMwvVKzIBDw=="
68
+ }
69
+ ]
70
+ },
71
+ "payloadJson": "{\"hello\":\"world\"}",
72
+ "paeHex": "445353457631203238206170706c69636174696f6e2f766e642e6578616d706c652b6a736f6e203137207b2268656c6c6f223a22776f726c64227d",
73
+ "paeSha256": "8a321059588971b8abbbfeb3a5c7f629a14fc46d2ecf12895a79483dde78532e",
74
+ "expected": true
75
+ }
76
+ }
@@ -0,0 +1,200 @@
1
+ {
2
+ "version": "0.2",
3
+ "note": "leafHash = sha256(0x00 || leaf); nodeHash = sha256(0x01 || left || right). Proof hashes are hex, leaf-to-root for inclusion; RFC 9162 order for consistency.",
4
+ "leaves": [
5
+ "leaf-0",
6
+ "leaf-1",
7
+ "leaf-2",
8
+ "leaf-3",
9
+ "leaf-4",
10
+ "leaf-5",
11
+ "leaf-6"
12
+ ],
13
+ "leafHashes": [
14
+ "305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
15
+ "3145c409f259b7c53e32036090ff76751025a2498ba9823ef718cac50b4e616f",
16
+ "fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
17
+ "f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
18
+ "ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba",
19
+ "8f1593cb92f429d9340b9bbc1f0bb122adf8026c42a4a42142e2168931727236",
20
+ "676f3782f5b3a5fb4370ed49572cedc523f4a66322269c85f2af0509d17b0a4d"
21
+ ],
22
+ "roots": [
23
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
24
+ "305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
25
+ "60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
26
+ "cf763a041c81ceef1578a6083f75c61bef2e0014f2a3e683a97fcfca5be7f19a",
27
+ "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3",
28
+ "00d21829a5503145348abcf712513eacf2a274211ad83e970202bb5b6d80b286",
29
+ "160cf1a616e8792f9078a9665cb06520d95a33f467d0826f2310219d31383d73",
30
+ "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0"
31
+ ],
32
+ "inclusion": [
33
+ {
34
+ "leafIndex": 0,
35
+ "treeSize": 7,
36
+ "hashes": [
37
+ "3145c409f259b7c53e32036090ff76751025a2498ba9823ef718cac50b4e616f",
38
+ "bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
39
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
40
+ ],
41
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
42
+ "expected": true
43
+ },
44
+ {
45
+ "leafIndex": 1,
46
+ "treeSize": 7,
47
+ "hashes": [
48
+ "305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
49
+ "bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
50
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
51
+ ],
52
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
53
+ "expected": true
54
+ },
55
+ {
56
+ "leafIndex": 2,
57
+ "treeSize": 7,
58
+ "hashes": [
59
+ "f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
60
+ "60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
61
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
62
+ ],
63
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
64
+ "expected": true
65
+ },
66
+ {
67
+ "leafIndex": 3,
68
+ "treeSize": 7,
69
+ "hashes": [
70
+ "fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
71
+ "60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
72
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
73
+ ],
74
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
75
+ "expected": true
76
+ },
77
+ {
78
+ "leafIndex": 4,
79
+ "treeSize": 7,
80
+ "hashes": [
81
+ "8f1593cb92f429d9340b9bbc1f0bb122adf8026c42a4a42142e2168931727236",
82
+ "676f3782f5b3a5fb4370ed49572cedc523f4a66322269c85f2af0509d17b0a4d",
83
+ "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
84
+ ],
85
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
86
+ "expected": true
87
+ },
88
+ {
89
+ "leafIndex": 5,
90
+ "treeSize": 7,
91
+ "hashes": [
92
+ "ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba",
93
+ "676f3782f5b3a5fb4370ed49572cedc523f4a66322269c85f2af0509d17b0a4d",
94
+ "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
95
+ ],
96
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
97
+ "expected": true
98
+ },
99
+ {
100
+ "leafIndex": 6,
101
+ "treeSize": 7,
102
+ "hashes": [
103
+ "985bb5d36b927800876871da925a7e82abe83a9ddba5882920a007a55ea2b376",
104
+ "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
105
+ ],
106
+ "root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
107
+ "expected": true
108
+ }
109
+ ],
110
+ "consistency": [
111
+ {
112
+ "oldSize": 0,
113
+ "newSize": 7,
114
+ "oldRoot": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
115
+ "newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
116
+ "hashes": [],
117
+ "expected": true
118
+ },
119
+ {
120
+ "oldSize": 1,
121
+ "newSize": 7,
122
+ "oldRoot": "305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
123
+ "newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
124
+ "hashes": [
125
+ "3145c409f259b7c53e32036090ff76751025a2498ba9823ef718cac50b4e616f",
126
+ "bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
127
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
128
+ ],
129
+ "expected": true
130
+ },
131
+ {
132
+ "oldSize": 2,
133
+ "newSize": 5,
134
+ "oldRoot": "60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
135
+ "newRoot": "00d21829a5503145348abcf712513eacf2a274211ad83e970202bb5b6d80b286",
136
+ "hashes": [
137
+ "bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
138
+ "ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba"
139
+ ],
140
+ "expected": true
141
+ },
142
+ {
143
+ "oldSize": 3,
144
+ "newSize": 7,
145
+ "oldRoot": "cf763a041c81ceef1578a6083f75c61bef2e0014f2a3e683a97fcfca5be7f19a",
146
+ "newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
147
+ "hashes": [
148
+ "fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
149
+ "f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
150
+ "60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
151
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
152
+ ],
153
+ "expected": true
154
+ },
155
+ {
156
+ "oldSize": 4,
157
+ "newSize": 7,
158
+ "oldRoot": "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3",
159
+ "newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
160
+ "hashes": [
161
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
162
+ ],
163
+ "expected": true
164
+ },
165
+ {
166
+ "oldSize": 5,
167
+ "newSize": 6,
168
+ "oldRoot": "00d21829a5503145348abcf712513eacf2a274211ad83e970202bb5b6d80b286",
169
+ "newRoot": "160cf1a616e8792f9078a9665cb06520d95a33f467d0826f2310219d31383d73",
170
+ "hashes": [
171
+ "ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba",
172
+ "8f1593cb92f429d9340b9bbc1f0bb122adf8026c42a4a42142e2168931727236",
173
+ "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
174
+ ],
175
+ "expected": true
176
+ },
177
+ {
178
+ "oldSize": 7,
179
+ "newSize": 7,
180
+ "oldRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
181
+ "newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
182
+ "hashes": [],
183
+ "expected": true
184
+ },
185
+ {
186
+ "oldSize": 3,
187
+ "newSize": 7,
188
+ "oldRoot": "cf763a041c81ceef1578a6083f75c61bef2e0014f2a3e683a97fcfca5be7f19a",
189
+ "newRoot": "c5d3c9af8774ee745d99c20ddb54063f1e9abddf0900db1cfb9b3608eb50df4a",
190
+ "hashes": [
191
+ "fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
192
+ "f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
193
+ "2edcc4da09ec6712e53fa29d2e571f7996455891323073556dd0c5ab8d0264c6",
194
+ "8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
195
+ ],
196
+ "expected": false,
197
+ "description": "leaf 1 rewritten after the older head was taken"
198
+ }
199
+ ]
200
+ }