@forestrie/merklelog 0.1.0 → 0.1.1

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.d.ts CHANGED
@@ -15,7 +15,7 @@ export { parseV2StorageObjectPath } from "./massifs/v2storagepaths.js";
15
15
  export { openMassifNodeStore } from "./massifs/nodestore.js";
16
16
  export type { MassifNodeStore } from "./massifs/nodestore.js";
17
17
  export { openMassifLeafIndex, MissingIndexError } from "./massifs/leafindex.js";
18
- export type { MassifLeafIndex } from "./massifs/leafindex.js";
18
+ export type { MassifLeafIndex, LocatedLeaf } from "./massifs/leafindex.js";
19
19
  export { mmrIndex, massifFirstLeaf, leafMinusSpurSum } from "./mmr/index.js";
20
20
  export { heightIndex, height, mmrIndexFromLeafIndex, leafIndex, mmrPosition, mmrSizeFromHeightIndex, leafCount, leafCountFromHeightIndex, } from "./mmr/math.js";
21
21
  export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, } from "./mmr/algorithms.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,SAAS,EACT,KAAK,EACL,KAAK,EACL,OAAO,EACP,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,6BAA6B,EAC7B,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACL,WAAW,EACX,MAAM,EACN,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,sBAAsB,EACtB,SAAS,EACT,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EACL,cAAc,EACd,cAAc,EACd,WAAW,EACX,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,SAAS,EACT,KAAK,EACL,KAAK,EACL,OAAO,EACP,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,6BAA6B,EAC7B,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG3E,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACL,WAAW,EACX,MAAM,EACN,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,sBAAsB,EACtB,SAAS,EACT,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EACL,cAAc,EACd,cAAc,EACd,WAAW,EACX,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
@@ -26,6 +26,15 @@
26
26
  export declare class MissingIndexError extends Error {
27
27
  constructor(message: string);
28
28
  }
29
+ /**
30
+ * A located leaf: its MMR index plus the 8-byte idtimestamp (big-endian) the
31
+ * urkle leaf record is keyed by. Together these are the two halves of the
32
+ * permanent entry id (`idtimestamp_be8 || mmrIndex_be8`).
33
+ */
34
+ export interface LocatedLeaf {
35
+ mmrIndex: bigint;
36
+ idtimestampBe8: Uint8Array;
37
+ }
29
38
  /** Content-hash leaf lookup over one massif blob (FOR-373). */
30
39
  export interface MassifLeafIndex {
31
40
  /**
@@ -34,6 +43,12 @@ export interface MassifLeafIndex {
34
43
  * bytes.
35
44
  */
36
45
  findByContentHash(h: Uint8Array): bigint | null;
46
+ /**
47
+ * Like {@link findByContentHash} but also recovers the leaf's idtimestamp
48
+ * (the 8-byte urkle leaf key), so a caller can reconstruct the full entry id
49
+ * offline. Returns `null` when the hash is not present. `h` must be 32 bytes.
50
+ */
51
+ findLeafByContentHash(h: Uint8Array): LocatedLeaf | null;
37
52
  /** Number of populated leaves in this blob. */
38
53
  leafCount: number;
39
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"leafindex.d.ts","sourceRoot":"","sources":["../../src/massifs/leafindex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAeH;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IAChD,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB;AAkBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,UAAU,GAAG,eAAe,CA+D5E"}
1
+ {"version":3,"file":"leafindex.d.ts","sourceRoot":"","sources":["../../src/massifs/leafindex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,UAAU,CAAC;CAC5B;AAED,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,qBAAqB,CAAC,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IACzD,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB;AAkBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,UAAU,GAAG,eAAe,CAgF5E"}
@@ -27,6 +27,7 @@ import { massifLogEntries } from "./massiflogentries.js";
27
27
  import { firstMMRSize, peaksBitmap } from "../mmr/proof.js";
28
28
  import { mmrIndex as mmrIndexFromLeafIndex } from "../mmr/index.js";
29
29
  const HASH_BYTES = Urkle.LeafValueBytes; // 32
30
+ const KEY_BYTES = Urkle.LeafKeyBytes; // 8 (idtimestamp, big-endian)
30
31
  const VALUE_OFFSET = Urkle.LeafValueOffset; // 8 (after the 8-byte key)
31
32
  const RECORD_BYTES = Urkle.LeafRecordBytes; // 128
32
33
  /**
@@ -98,7 +99,8 @@ export function openMassifLeafIndex(massifBytes) {
98
99
  break;
99
100
  populated = ordinal + 1;
100
101
  }
101
- const findByContentHash = (h) => {
102
+ /** Ordinal of the populated leaf whose value column equals `h`, or -1. */
103
+ const ordinalForContentHash = (h) => {
102
104
  if (h.length !== HASH_BYTES) {
103
105
  throw new Error(`content hash must be ${HASH_BYTES} bytes, got ${h.length}`);
104
106
  }
@@ -107,11 +109,28 @@ export function openMassifLeafIndex(massifBytes) {
107
109
  const valueOff = recordOff + VALUE_OFFSET;
108
110
  if (valueOff + HASH_BYTES > massifBytes.byteLength)
109
111
  break;
110
- if (equals32(massifBytes, valueOff, h)) {
111
- return mmrIndexFromLeafIndex(firstLeafIndex + BigInt(ordinal));
112
- }
112
+ if (equals32(massifBytes, valueOff, h))
113
+ return ordinal;
113
114
  }
114
- return null;
115
+ return -1;
116
+ };
117
+ const findByContentHash = (h) => {
118
+ const ordinal = ordinalForContentHash(h);
119
+ if (ordinal < 0)
120
+ return null;
121
+ return mmrIndexFromLeafIndex(firstLeafIndex + BigInt(ordinal));
122
+ };
123
+ const findLeafByContentHash = (h) => {
124
+ const ordinal = ordinalForContentHash(h);
125
+ if (ordinal < 0)
126
+ return null;
127
+ // The leaf record is `[key(8) || value(32) || …]`; the key IS the
128
+ // big-endian idtimestamp the sealer assigned (go-merklelog urkle leafrecord).
129
+ const keyOff = leafTableStart + ordinal * RECORD_BYTES;
130
+ return {
131
+ mmrIndex: mmrIndexFromLeafIndex(firstLeafIndex + BigInt(ordinal)),
132
+ idtimestampBe8: massifBytes.slice(keyOff, keyOff + KEY_BYTES),
133
+ };
115
134
  };
116
- return { findByContentHash, leafCount: populated };
135
+ return { findByContentHash, findLeafByContentHash, leafCount: populated };
117
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestrie/merklelog",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "MMR (merkle mountain range) proofs and massif storage format for Forestrie transparency logs",
package/src/index.ts CHANGED
@@ -51,7 +51,7 @@ export type { MassifNodeStore } from "./massifs/nodestore.js";
51
51
 
52
52
  // Content-hash leaf lookup over the massif index region (FOR-373)
53
53
  export { openMassifLeafIndex, MissingIndexError } from "./massifs/leafindex.js";
54
- export type { MassifLeafIndex } from "./massifs/leafindex.js";
54
+ export type { MassifLeafIndex, LocatedLeaf } from "./massifs/leafindex.js";
55
55
 
56
56
  // Export mmr module
57
57
  export { mmrIndex, massifFirstLeaf, leafMinusSpurSum } from "./mmr/index.js";
@@ -29,6 +29,7 @@ import { firstMMRSize, peaksBitmap } from "../mmr/proof.js";
29
29
  import { mmrIndex as mmrIndexFromLeafIndex } from "../mmr/index.js";
30
30
 
31
31
  const HASH_BYTES = Urkle.LeafValueBytes; // 32
32
+ const KEY_BYTES = Urkle.LeafKeyBytes; // 8 (idtimestamp, big-endian)
32
33
  const VALUE_OFFSET = Urkle.LeafValueOffset; // 8 (after the 8-byte key)
33
34
  const RECORD_BYTES = Urkle.LeafRecordBytes; // 128
34
35
 
@@ -44,6 +45,16 @@ export class MissingIndexError extends Error {
44
45
  }
45
46
  }
46
47
 
48
+ /**
49
+ * A located leaf: its MMR index plus the 8-byte idtimestamp (big-endian) the
50
+ * urkle leaf record is keyed by. Together these are the two halves of the
51
+ * permanent entry id (`idtimestamp_be8 || mmrIndex_be8`).
52
+ */
53
+ export interface LocatedLeaf {
54
+ mmrIndex: bigint;
55
+ idtimestampBe8: Uint8Array;
56
+ }
57
+
47
58
  /** Content-hash leaf lookup over one massif blob (FOR-373). */
48
59
  export interface MassifLeafIndex {
49
60
  /**
@@ -52,6 +63,12 @@ export interface MassifLeafIndex {
52
63
  * bytes.
53
64
  */
54
65
  findByContentHash(h: Uint8Array): bigint | null;
66
+ /**
67
+ * Like {@link findByContentHash} but also recovers the leaf's idtimestamp
68
+ * (the 8-byte urkle leaf key), so a caller can reconstruct the full entry id
69
+ * offline. Returns `null` when the hash is not present. `h` must be 32 bytes.
70
+ */
71
+ findLeafByContentHash(h: Uint8Array): LocatedLeaf | null;
55
72
  /** Number of populated leaves in this blob. */
56
73
  leafCount: number;
57
74
  }
@@ -123,7 +140,8 @@ export function openMassifLeafIndex(massifBytes: Uint8Array): MassifLeafIndex {
123
140
  populated = ordinal + 1;
124
141
  }
125
142
 
126
- const findByContentHash = (h: Uint8Array): bigint | null => {
143
+ /** Ordinal of the populated leaf whose value column equals `h`, or -1. */
144
+ const ordinalForContentHash = (h: Uint8Array): number => {
127
145
  if (h.length !== HASH_BYTES) {
128
146
  throw new Error(
129
147
  `content hash must be ${HASH_BYTES} bytes, got ${h.length}`,
@@ -133,12 +151,28 @@ export function openMassifLeafIndex(massifBytes: Uint8Array): MassifLeafIndex {
133
151
  const recordOff = leafTableStart + ordinal * RECORD_BYTES;
134
152
  const valueOff = recordOff + VALUE_OFFSET;
135
153
  if (valueOff + HASH_BYTES > massifBytes.byteLength) break;
136
- if (equals32(massifBytes, valueOff, h)) {
137
- return mmrIndexFromLeafIndex(firstLeafIndex + BigInt(ordinal));
138
- }
154
+ if (equals32(massifBytes, valueOff, h)) return ordinal;
139
155
  }
140
- return null;
156
+ return -1;
157
+ };
158
+
159
+ const findByContentHash = (h: Uint8Array): bigint | null => {
160
+ const ordinal = ordinalForContentHash(h);
161
+ if (ordinal < 0) return null;
162
+ return mmrIndexFromLeafIndex(firstLeafIndex + BigInt(ordinal));
163
+ };
164
+
165
+ const findLeafByContentHash = (h: Uint8Array): LocatedLeaf | null => {
166
+ const ordinal = ordinalForContentHash(h);
167
+ if (ordinal < 0) return null;
168
+ // The leaf record is `[key(8) || value(32) || …]`; the key IS the
169
+ // big-endian idtimestamp the sealer assigned (go-merklelog urkle leafrecord).
170
+ const keyOff = leafTableStart + ordinal * RECORD_BYTES;
171
+ return {
172
+ mmrIndex: mmrIndexFromLeafIndex(firstLeafIndex + BigInt(ordinal)),
173
+ idtimestampBe8: massifBytes.slice(keyOff, keyOff + KEY_BYTES),
174
+ };
141
175
  };
142
176
 
143
- return { findByContentHash, leafCount: populated };
177
+ return { findByContentHash, findLeafByContentHash, leafCount: populated };
144
178
  }