@secondlayer/shared 6.23.0 → 6.25.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.
Files changed (37) hide show
  1. package/dist/src/db/index.d.ts +730 -1
  2. package/dist/src/db/index.js +251 -1
  3. package/dist/src/db/index.js.map +4 -4
  4. package/dist/src/db/queries/chain-reorgs.d.ts +2 -0
  5. package/dist/src/db/queries/chain-reorgs.js +249 -1
  6. package/dist/src/db/queries/chain-reorgs.js.map +4 -4
  7. package/dist/src/db/queries/contracts.d.ts +2 -0
  8. package/dist/src/db/queries/integrity.d.ts +2 -0
  9. package/dist/src/db/queries/subgraph-gaps.d.ts +2 -0
  10. package/dist/src/db/queries/subgraph-operations.d.ts +2 -0
  11. package/dist/src/db/queries/subgraphs.d.ts +2 -0
  12. package/dist/src/db/queries/subgraphs.js +249 -1
  13. package/dist/src/db/queries/subgraphs.js.map +4 -4
  14. package/dist/src/db/queries/subscriptions.d.ts +2 -0
  15. package/dist/src/db/schema.d.ts +2 -0
  16. package/dist/src/errors.d.ts +5 -2
  17. package/dist/src/errors.js +8 -5
  18. package/dist/src/errors.js.map +3 -3
  19. package/dist/src/index.d.ts +772 -3
  20. package/dist/src/index.js +258 -5
  21. package/dist/src/index.js.map +6 -6
  22. package/dist/src/node/client.d.ts +52 -0
  23. package/dist/src/node/client.js +188 -1
  24. package/dist/src/node/client.js.map +5 -4
  25. package/dist/src/node/consensus.d.ts +38 -0
  26. package/dist/src/node/consensus.js +67 -0
  27. package/dist/src/node/consensus.js.map +10 -0
  28. package/dist/src/node/local-client.d.ts +2 -0
  29. package/dist/src/node/nakamoto.d.ts +90 -0
  30. package/dist/src/node/nakamoto.js +177 -0
  31. package/dist/src/node/nakamoto.js.map +10 -0
  32. package/dist/src/schemas/index.js.map +2 -2
  33. package/dist/src/schemas/subscriptions.d.ts +10 -1
  34. package/dist/src/schemas/subscriptions.js.map +2 -2
  35. package/dist/src/types.d.ts +2 -0
  36. package/migrations/0089_blocks_index_block_hash.ts +25 -0
  37. package/package.json +9 -1
@@ -1,3 +1,39 @@
1
+ interface RewardSetSigner {
2
+ /** 33-byte compressed secp256k1 public key (hex, no 0x). */
3
+ signing_key: string;
4
+ weight: number;
5
+ }
6
+ interface RewardSet {
7
+ signers: RewardSetSigner[];
8
+ total_weight: number;
9
+ }
10
+ interface NakamotoBlockHeader {
11
+ version: number;
12
+ chainLength: bigint;
13
+ burnSpent: bigint;
14
+ /** 20-byte consensus hash (hex). */
15
+ consensusHash: string;
16
+ /** 32-byte parent StacksBlockId (hex). */
17
+ parentBlockId: string;
18
+ /** 32-byte SHA512/256 merkle root over the block's txids (hex). */
19
+ txMerkleRoot: string;
20
+ /** 32-byte MARF root after applying this block (hex). */
21
+ stateIndexRoot: string;
22
+ timestamp: bigint;
23
+ /** 65-byte recoverable ECDSA miner signature (hex). */
24
+ minerSignature: string;
25
+ /** Per-signer recoverable ECDSA signatures, reward-set order (hex, 65B each). */
26
+ signerSignatures: string[];
27
+ /** Full serialized pox_treatment BitVec bytes (u16 bits ‖ u32 len ‖ data). */
28
+ poxTreatment: Uint8Array;
29
+ /**
30
+ * Exact bytes whose SHA512/256 IS the block_hash / signer_signature_hash:
31
+ * the header with the signer_signature vector omitted (header[0:206] ‖ pox).
32
+ */
33
+ signerSignatureHashPreimage: Uint8Array;
34
+ /** Offset at which the tx `Vec` begins (= total header byte length). */
35
+ headerByteLength: number;
36
+ }
1
37
  interface NodeInfo {
2
38
  peer_version: number;
3
39
  pox_consensus: string;
@@ -29,6 +65,22 @@ declare class StacksNodeClient {
29
65
  constructor(rpcUrl?: string);
30
66
  getInfo(): Promise<NodeInfo>;
31
67
  getBlock(height: number): Promise<BlockResponse | null>;
68
+ /**
69
+ * Fetch + parse a Nakamoto block by its index_block_hash. Returns the raw
70
+ * bytes, the parsed header, and the recomputed block_hash / index_block_hash
71
+ * (so a caller can cross-check the node's answer). Null on 404.
72
+ */
73
+ getNakamotoBlock(blockId: string): Promise<{
74
+ raw: Uint8Array
75
+ header: NakamotoBlockHeader
76
+ blockHash: string
77
+ indexBlockHash: string
78
+ } | null>;
79
+ /**
80
+ * Fetch the reward set (signer keys + weights) for a reward cycle from
81
+ * `/v3/stacker_set/{cycle}`. Null on 404 (cycle not yet computed).
82
+ */
83
+ getRewardSet(cycle: number): Promise<RewardSet | null>;
32
84
  isHealthy(): Promise<boolean>;
33
85
  getContractAbi(contractId: string): Promise<unknown>;
34
86
  /** Fetch a contract's Clarity source — used to parse declared `impl-trait`s. */
@@ -14,6 +14,154 @@ var __export = (target, all) => {
14
14
  });
15
15
  };
16
16
 
17
+ // src/node/nakamoto.ts
18
+ import { createHash } from "node:crypto";
19
+ function sha512_256(bytes) {
20
+ return createHash("sha512-256").update(bytes).digest();
21
+ }
22
+ var toHex = (b) => Buffer.from(b).toString("hex");
23
+ var fromHex = (h) => Uint8Array.from(Buffer.from(h.startsWith("0x") ? h.slice(2) : h, "hex"));
24
+ var PREFIX_LEN = 206;
25
+ var CONSENSUS_HASH_OFF = 17;
26
+ var TX_MERKLE_ROOT_OFF = 69;
27
+ var STATE_INDEX_ROOT_OFF = 101;
28
+ var TIMESTAMP_OFF = 133;
29
+ var MINER_SIG_OFF = 141;
30
+ var SIGNER_VEC_OFF = 206;
31
+ var SIG_LEN = 65;
32
+ function u32(b, off) {
33
+ return new DataView(b.buffer, b.byteOffset, b.byteLength).getUint32(off);
34
+ }
35
+ function u64(b, off) {
36
+ return new DataView(b.buffer, b.byteOffset, b.byteLength).getBigUint64(off);
37
+ }
38
+ function parseNakamotoBlockHeader(raw) {
39
+ if (raw.length < PREFIX_LEN + 4) {
40
+ throw new Error("raw block too short for a Nakamoto header");
41
+ }
42
+ const signerCount = u32(raw, SIGNER_VEC_OFF);
43
+ const sigsStart = SIGNER_VEC_OFF + 4;
44
+ const signerSignatures = [];
45
+ for (let i = 0;i < signerCount; i++) {
46
+ const off = sigsStart + i * SIG_LEN;
47
+ signerSignatures.push(toHex(raw.subarray(off, off + SIG_LEN)));
48
+ }
49
+ const poxOff = sigsStart + signerCount * SIG_LEN;
50
+ const poxDataLen = u32(raw, poxOff + 2);
51
+ const poxEnd = poxOff + 6 + poxDataLen;
52
+ const poxTreatment = raw.subarray(poxOff, poxEnd);
53
+ const preimage = new Uint8Array(PREFIX_LEN + poxTreatment.length);
54
+ preimage.set(raw.subarray(0, PREFIX_LEN), 0);
55
+ preimage.set(poxTreatment, PREFIX_LEN);
56
+ return {
57
+ version: raw[0],
58
+ chainLength: u64(raw, 1),
59
+ burnSpent: u64(raw, 9),
60
+ consensusHash: toHex(raw.subarray(CONSENSUS_HASH_OFF, CONSENSUS_HASH_OFF + 20)),
61
+ parentBlockId: toHex(raw.subarray(37, 69)),
62
+ txMerkleRoot: toHex(raw.subarray(TX_MERKLE_ROOT_OFF, TX_MERKLE_ROOT_OFF + 32)),
63
+ stateIndexRoot: toHex(raw.subarray(STATE_INDEX_ROOT_OFF, STATE_INDEX_ROOT_OFF + 32)),
64
+ timestamp: u64(raw, TIMESTAMP_OFF),
65
+ minerSignature: toHex(raw.subarray(MINER_SIG_OFF, MINER_SIG_OFF + SIG_LEN)),
66
+ signerSignatures,
67
+ poxTreatment,
68
+ signerSignatureHashPreimage: preimage,
69
+ headerByteLength: poxEnd
70
+ };
71
+ }
72
+ function nakamotoBlockHash(header) {
73
+ return toHex(sha512_256(header.signerSignatureHashPreimage));
74
+ }
75
+ function nakamotoBlockId(blockHashHex, consensusHashHex) {
76
+ const a = fromHex(blockHashHex);
77
+ const b = fromHex(consensusHashHex);
78
+ const buf = new Uint8Array(a.length + b.length);
79
+ buf.set(a, 0);
80
+ buf.set(b, a.length);
81
+ return toHex(sha512_256(buf));
82
+ }
83
+ function stacksTxid(rawTx) {
84
+ return toHex(sha512_256(rawTx));
85
+ }
86
+ var LEAF_TAG = 0;
87
+ var NODE_TAG = 1;
88
+ function tagged(tag, ...parts) {
89
+ const len = parts.reduce((n, p) => n + p.length, 1);
90
+ const buf = new Uint8Array(len);
91
+ buf[0] = tag;
92
+ let o = 1;
93
+ for (const p of parts) {
94
+ buf.set(p, o);
95
+ o += p.length;
96
+ }
97
+ return sha512_256(buf);
98
+ }
99
+ function txMerkleRoot(txidsHex) {
100
+ if (txidsHex.length === 0)
101
+ throw new Error("no transactions");
102
+ let level = txidsHex.map((t) => tagged(LEAF_TAG, fromHex(t)));
103
+ while (level.length > 1) {
104
+ if (level.length % 2 === 1)
105
+ level.push(level[level.length - 1]);
106
+ const next = [];
107
+ for (let i = 0;i < level.length; i += 2) {
108
+ next.push(tagged(NODE_TAG, level[i], level[i + 1]));
109
+ }
110
+ level = next;
111
+ }
112
+ return toHex(level[0]);
113
+ }
114
+ function txMerkleProof(txidsHex, index) {
115
+ if (index < 0 || index >= txidsHex.length) {
116
+ throw new Error("index out of range");
117
+ }
118
+ let level = txidsHex.map((t) => tagged(LEAF_TAG, fromHex(t)));
119
+ let idx = index;
120
+ const path = [];
121
+ while (level.length > 1) {
122
+ if (level.length % 2 === 1)
123
+ level.push(level[level.length - 1]);
124
+ const siblingIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
125
+ path.push({
126
+ position: idx % 2 === 0 ? "right" : "left",
127
+ hash: toHex(level[siblingIdx])
128
+ });
129
+ const next = [];
130
+ for (let i = 0;i < level.length; i += 2) {
131
+ next.push(tagged(NODE_TAG, level[i], level[i + 1]));
132
+ }
133
+ level = next;
134
+ idx = Math.floor(idx / 2);
135
+ }
136
+ return path;
137
+ }
138
+ function verifyTxMerkleProof(txidHex, path, txMerkleRootHex) {
139
+ let acc = tagged(LEAF_TAG, fromHex(txidHex));
140
+ for (const step of path) {
141
+ const sib = fromHex(step.hash);
142
+ acc = step.position === "right" ? tagged(NODE_TAG, acc, sib) : tagged(NODE_TAG, sib, acc);
143
+ }
144
+ const root = txMerkleRootHex.startsWith("0x") ? txMerkleRootHex.slice(2) : txMerkleRootHex;
145
+ return toHex(acc) === root;
146
+ }
147
+ async function fetchNakamotoBlock(opts) {
148
+ const id = opts.blockId.startsWith("0x") ? opts.blockId.slice(2) : opts.blockId;
149
+ const f = opts.fetchImpl ?? fetch;
150
+ const res = await f(`${opts.nodeUrl.replace(/\/+$/, "")}/v3/blocks/${id}`);
151
+ if (!res.ok) {
152
+ throw new Error(`/v3/blocks/${id} returned ${res.status}`);
153
+ }
154
+ const raw = new Uint8Array(await res.arrayBuffer());
155
+ const header = parseNakamotoBlockHeader(raw);
156
+ const blockHash = nakamotoBlockHash(header);
157
+ return {
158
+ raw,
159
+ header,
160
+ blockHash,
161
+ indexBlockHash: nakamotoBlockId(blockHash, header.consensusHash)
162
+ };
163
+ }
164
+
17
165
  // src/node/client.ts
18
166
  class StacksNodeClient {
19
167
  rpcUrl;
@@ -40,6 +188,45 @@ class StacksNodeClient {
40
188
  }
41
189
  return res.json();
42
190
  }
191
+ async getNakamotoBlock(blockId) {
192
+ const id = blockId.startsWith("0x") ? blockId.slice(2) : blockId;
193
+ const res = await fetch(`${this.rpcUrl}/v3/blocks/${id}`, {
194
+ signal: AbortSignal.timeout(30000)
195
+ });
196
+ if (res.status === 404)
197
+ return null;
198
+ if (!res.ok) {
199
+ throw new Error(`Node RPC /v3/blocks/${id} returned ${res.status}`);
200
+ }
201
+ const raw = new Uint8Array(await res.arrayBuffer());
202
+ const header = parseNakamotoBlockHeader(raw);
203
+ const blockHash = nakamotoBlockHash(header);
204
+ return {
205
+ raw,
206
+ header,
207
+ blockHash,
208
+ indexBlockHash: nakamotoBlockId(blockHash, header.consensusHash)
209
+ };
210
+ }
211
+ async getRewardSet(cycle) {
212
+ const res = await fetch(`${this.rpcUrl}/v3/stacker_set/${cycle}`, {
213
+ signal: AbortSignal.timeout(15000)
214
+ });
215
+ if (res.status === 404)
216
+ return null;
217
+ if (!res.ok) {
218
+ throw new Error(`Node RPC /v3/stacker_set/${cycle} returned ${res.status}`);
219
+ }
220
+ const body = await res.json();
221
+ const signers = body.stacker_set.signers.map((s) => ({
222
+ signing_key: s.signing_key.startsWith("0x") ? s.signing_key.slice(2) : s.signing_key,
223
+ weight: s.weight
224
+ }));
225
+ return {
226
+ signers,
227
+ total_weight: signers.reduce((sum, s) => sum + s.weight, 0)
228
+ };
229
+ }
43
230
  async isHealthy() {
44
231
  try {
45
232
  const info = await this.getInfo();
@@ -78,5 +265,5 @@ export {
78
265
  StacksNodeClient
79
266
  };
80
267
 
81
- //# debugId=1B7D01390D90430B64756E2164756E21
268
+ //# debugId=26D700D33629603864756E2164756E21
82
269
  //# sourceMappingURL=client.js.map
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/node/client.ts"],
3
+ "sources": ["../src/node/nakamoto.ts", "../src/node/client.ts"],
4
4
  "sourcesContent": [
5
- "export interface NodeInfo {\n\tpeer_version: number;\n\tpox_consensus: string;\n\tburn_block_height: number;\n\tstable_pox_consensus: string;\n\tstable_burn_block_height: number;\n\tserver_version: string;\n\tnetwork_id: number;\n\tparent_network_id: number;\n\tstacks_tip_height: number;\n\tstacks_tip: string;\n\tstacks_tip_consensus_hash: string;\n\tgenesis_chainstate_hash: string;\n}\n\nexport interface BlockResponse {\n\thash: string;\n\theight: number;\n\tparent_block_hash: string;\n\tburn_block_height: number;\n\tburn_block_hash: string;\n\tburn_block_time: number;\n\tindex_block_hash: string;\n\tparent_index_block_hash: string;\n\tminer_txid: string;\n\ttxs: string[];\n\t// Full block data varies by endpoint kept minimal for fetch use case\n}\n\nexport class StacksNodeClient {\n\tprivate rpcUrl: string;\n\n\tconstructor(rpcUrl?: string) {\n\t\tthis.rpcUrl =\n\t\t\trpcUrl || process.env.STACKS_NODE_RPC_URL || \"http://localhost:20443\";\n\t}\n\n\tasync getInfo(): Promise<NodeInfo> {\n\t\tconst res = await fetch(`${this.rpcUrl}/v2/info`, {\n\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Node RPC /v2/info returned ${res.status}`);\n\t\t}\n\t\treturn res.json() as Promise<NodeInfo>;\n\t}\n\n\tasync getBlock(height: number): Promise<BlockResponse | null> {\n\t\t// Stacks API v2 block-by-height endpoint\n\t\tconst res = await fetch(`${this.rpcUrl}/v2/blocks/${height}`, {\n\t\t\tsignal: AbortSignal.timeout(30_000),\n\t\t});\n\t\tif (res.status === 404) return null;\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Node RPC /v2/blocks/${height} returned ${res.status}`);\n\t\t}\n\t\treturn res.json() as Promise<BlockResponse>;\n\t}\n\n\tasync isHealthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst info = await this.getInfo();\n\t\t\treturn info.stacks_tip_height > 0;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync getContractAbi(contractId: string): Promise<unknown> {\n\t\tconst dotIdx = contractId.indexOf(\".\");\n\t\tconst address = contractId.slice(0, dotIdx);\n\t\tconst name = contractId.slice(dotIdx + 1);\n\t\tconst res = await fetch(\n\t\t\t`${this.rpcUrl}/v2/contracts/interface/${address}/${name}`,\n\t\t\t{\n\t\t\t\tsignal: AbortSignal.timeout(30_000),\n\t\t\t},\n\t\t);\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node RPC /v2/contracts/interface/${address}/${name} returned ${res.status}`,\n\t\t\t);\n\t\t}\n\t\treturn res.json();\n\t}\n\n\t/** Fetch a contract's Clarity source used to parse declared `impl-trait`s. */\n\tasync getContractSource(contractId: string): Promise<string | null> {\n\t\tconst dotIdx = contractId.indexOf(\".\");\n\t\tconst address = contractId.slice(0, dotIdx);\n\t\tconst name = contractId.slice(dotIdx + 1);\n\t\tconst res = await fetch(\n\t\t\t`${this.rpcUrl}/v2/contracts/source/${address}/${name}`,\n\t\t\t{ signal: AbortSignal.timeout(30_000) },\n\t\t);\n\t\tif (!res.ok) return null;\n\t\tconst body = (await res.json()) as { source?: string };\n\t\treturn body.source ?? null;\n\t}\n\n\tgetRpcUrl(): string {\n\t\treturn this.rpcUrl;\n\t}\n}\n"
5
+ "import { createHash } from \"node:crypto\";\n\n/**\n * Nakamoto block-header parsing + consensus hashing for trustless verification.\n *\n * Every constant here is verified bit-exact against mainnet `stacks-node\n * 3.4.0.0.3` (see docs/design/trustless-verification-proofs.md, Appendix). This\n * Building block for transaction-inclusion / block-canonicity proofs: fetch a\n * raw `/v3/blocks/{id}` body, parse the header, and recompute the block_hash,\n * index_block_hash, and tx_merkle_root the chain itself commits to.\n */\n\n/** SHA-512/256 — the hash Stacks uses everywhere (NOT truncated SHA-512). */\nexport function sha512_256(bytes: Uint8Array): Uint8Array {\n\treturn createHash(\"sha512-256\").update(bytes).digest();\n}\n\nconst toHex = (b: Uint8Array): string => Buffer.from(b).toString(\"hex\");\nconst fromHex = (h: string): Uint8Array =>\n\tUint8Array.from(Buffer.from(h.startsWith(\"0x\") ? h.slice(2) : h, \"hex\"));\n\n// Fixed-size header prefix: version..miner_signature, before signer_signature.\nconst PREFIX_LEN = 206;\nconst CONSENSUS_HASH_OFF = 17;\nconst TX_MERKLE_ROOT_OFF = 69;\nconst STATE_INDEX_ROOT_OFF = 101;\nconst TIMESTAMP_OFF = 133;\nconst MINER_SIG_OFF = 141;\nconst SIGNER_VEC_OFF = 206; // u32 count, then 65 bytes per signer\nconst SIG_LEN = 65;\n\nexport interface NakamotoBlockHeader {\n\tversion: number;\n\tchainLength: bigint;\n\tburnSpent: bigint;\n\t/** 20-byte consensus hash (hex). */\n\tconsensusHash: string;\n\t/** 32-byte parent StacksBlockId (hex). */\n\tparentBlockId: string;\n\t/** 32-byte SHA512/256 merkle root over the block's txids (hex). */\n\ttxMerkleRoot: string;\n\t/** 32-byte MARF root after applying this block (hex). */\n\tstateIndexRoot: string;\n\ttimestamp: bigint;\n\t/** 65-byte recoverable ECDSA miner signature (hex). */\n\tminerSignature: string;\n\t/** Per-signer recoverable ECDSA signatures, reward-set order (hex, 65B each). */\n\tsignerSignatures: string[];\n\t/** Full serialized pox_treatment BitVec bytes (u16 bits ‖ u32 len ‖ data). */\n\tpoxTreatment: Uint8Array;\n\t/**\n\t * Exact bytes whose SHA512/256 IS the block_hash / signer_signature_hash:\n\t * the header with the signer_signature vector omitted (header[0:206] ‖ pox).\n\t */\n\tsignerSignatureHashPreimage: Uint8Array;\n\t/** Offset at which the tx `Vec` begins (= total header byte length). */\n\theaderByteLength: number;\n}\n\nfunction u32(b: Uint8Array, off: number): number {\n\treturn new DataView(b.buffer, b.byteOffset, b.byteLength).getUint32(off);\n}\nfunction u64(b: Uint8Array, off: number): bigint {\n\treturn new DataView(b.buffer, b.byteOffset, b.byteLength).getBigUint64(off);\n}\n\n/** Parse the Nakamoto block header from the raw `/v3/blocks` body. */\nexport function parseNakamotoBlockHeader(raw: Uint8Array): NakamotoBlockHeader {\n\tif (raw.length < PREFIX_LEN + 4) {\n\t\tthrow new Error(\"raw block too short for a Nakamoto header\");\n\t}\n\tconst signerCount = u32(raw, SIGNER_VEC_OFF);\n\tconst sigsStart = SIGNER_VEC_OFF + 4;\n\tconst signerSignatures: string[] = [];\n\tfor (let i = 0; i < signerCount; i++) {\n\t\tconst off = sigsStart + i * SIG_LEN;\n\t\tsignerSignatures.push(toHex(raw.subarray(off, off + SIG_LEN)));\n\t}\n\t// pox_treatment: u16 num_bits ‖ u32 data_len ‖ data[data_len].\n\tconst poxOff = sigsStart + signerCount * SIG_LEN;\n\tconst poxDataLen = u32(raw, poxOff + 2);\n\tconst poxEnd = poxOff + 6 + poxDataLen;\n\tconst poxTreatment = raw.subarray(poxOff, poxEnd);\n\n\t// block_hash preimage = header minus signer_signature = prefix[0:206] ‖ pox.\n\tconst preimage = new Uint8Array(PREFIX_LEN + poxTreatment.length);\n\tpreimage.set(raw.subarray(0, PREFIX_LEN), 0);\n\tpreimage.set(poxTreatment, PREFIX_LEN);\n\n\treturn {\n\t\tversion: raw[0],\n\t\tchainLength: u64(raw, 1),\n\t\tburnSpent: u64(raw, 9),\n\t\tconsensusHash: toHex(\n\t\t\traw.subarray(CONSENSUS_HASH_OFF, CONSENSUS_HASH_OFF + 20),\n\t\t),\n\t\tparentBlockId: toHex(raw.subarray(37, 69)),\n\t\ttxMerkleRoot: toHex(\n\t\t\traw.subarray(TX_MERKLE_ROOT_OFF, TX_MERKLE_ROOT_OFF + 32),\n\t\t),\n\t\tstateIndexRoot: toHex(\n\t\t\traw.subarray(STATE_INDEX_ROOT_OFF, STATE_INDEX_ROOT_OFF + 32),\n\t\t),\n\t\ttimestamp: u64(raw, TIMESTAMP_OFF),\n\t\tminerSignature: toHex(raw.subarray(MINER_SIG_OFF, MINER_SIG_OFF + SIG_LEN)),\n\t\tsignerSignatures,\n\t\tpoxTreatment,\n\t\tsignerSignatureHashPreimage: preimage,\n\t\theaderByteLength: poxEnd,\n\t};\n}\n\n/**\n * block_hash (== signer_signature_hash): SHA512/256 over the header with the\n * signer_signature vector omitted. This is what each signer signs.\n */\nexport function nakamotoBlockHash(header: NakamotoBlockHeader): string {\n\treturn toHex(sha512_256(header.signerSignatureHashPreimage));\n}\n\n/** index_block_hash (StacksBlockId) = SHA512/256(block_hash ‖ consensus_hash). */\nexport function nakamotoBlockId(\n\tblockHashHex: string,\n\tconsensusHashHex: string,\n): string {\n\tconst a = fromHex(blockHashHex);\n\tconst b = fromHex(consensusHashHex);\n\tconst buf = new Uint8Array(a.length + b.length);\n\tbuf.set(a, 0);\n\tbuf.set(b, a.length);\n\treturn toHex(sha512_256(buf));\n}\n\n/** A Stacks txid = SHA512/256 of the transaction's consensus serialization. */\nexport function stacksTxid(rawTx: Uint8Array): string {\n\treturn toHex(sha512_256(rawTx));\n}\n\nconst LEAF_TAG = 0x00;\nconst NODE_TAG = 0x01;\n\nfunction tagged(tag: number, ...parts: Uint8Array[]): Uint8Array {\n\tconst len = parts.reduce((n, p) => n + p.length, 1);\n\tconst buf = new Uint8Array(len);\n\tbuf[0] = tag;\n\tlet o = 1;\n\tfor (const p of parts) {\n\t\tbuf.set(p, o);\n\t\to += p.length;\n\t}\n\treturn sha512_256(buf);\n}\n\n/**\n * tx_merkle_root over the block's txids (hex), reproducing the consensus rule:\n * leaf = H(0x00 ‖ txid), node = H(0x01 ‖ left ‖ right), odd level duplicates the\n * last node. Returns the root hex; throws on an empty tx list.\n */\nexport function txMerkleRoot(txidsHex: string[]): string {\n\tif (txidsHex.length === 0) throw new Error(\"no transactions\");\n\tlet level = txidsHex.map((t) => tagged(LEAF_TAG, fromHex(t)));\n\twhile (level.length > 1) {\n\t\tif (level.length % 2 === 1) level.push(level[level.length - 1]);\n\t\tconst next: Uint8Array[] = [];\n\t\tfor (let i = 0; i < level.length; i += 2) {\n\t\t\tnext.push(tagged(NODE_TAG, level[i], level[i + 1]));\n\t\t}\n\t\tlevel = next;\n\t}\n\treturn toHex(level[0]);\n}\n\n/** One authentication-path step: the sibling hash and which side it's on. */\nexport interface MerkleProofStep {\n\t/** Side the SIBLING is on relative to the accumulator. */\n\tposition: \"left\" | \"right\";\n\t/** Sibling node hash (hex). */\n\thash: string;\n}\n\n/**\n * Build the tx-inclusion authentication path for the tx at `index` in a block,\n * reproducing the consensus merkle tree (incl. duplicate-last-on-odd). The path\n * lets a verifier recompute `tx_merkle_root` from just the target txid.\n */\nexport function txMerkleProof(\n\ttxidsHex: string[],\n\tindex: number,\n): MerkleProofStep[] {\n\tif (index < 0 || index >= txidsHex.length) {\n\t\tthrow new Error(\"index out of range\");\n\t}\n\tlet level = txidsHex.map((t) => tagged(LEAF_TAG, fromHex(t)));\n\tlet idx = index;\n\tconst path: MerkleProofStep[] = [];\n\twhile (level.length > 1) {\n\t\tif (level.length % 2 === 1) level.push(level[level.length - 1]);\n\t\tconst siblingIdx = idx % 2 === 0 ? idx + 1 : idx - 1;\n\t\tpath.push({\n\t\t\tposition: idx % 2 === 0 ? \"right\" : \"left\",\n\t\t\thash: toHex(level[siblingIdx]),\n\t\t});\n\t\tconst next: Uint8Array[] = [];\n\t\tfor (let i = 0; i < level.length; i += 2) {\n\t\t\tnext.push(tagged(NODE_TAG, level[i], level[i + 1]));\n\t\t}\n\t\tlevel = next;\n\t\tidx = Math.floor(idx / 2);\n\t}\n\treturn path;\n}\n\n/**\n * Verify a tx-inclusion proof: fold the target `txid` (hex) up through `path`\n * and check it equals `txMerkleRoot` (hex). The verifier recomputes the txid\n * itself from the raw tx bytes, so nothing here is trusted.\n */\nexport function verifyTxMerkleProof(\n\ttxidHex: string,\n\tpath: MerkleProofStep[],\n\ttxMerkleRootHex: string,\n): boolean {\n\tlet acc = tagged(LEAF_TAG, fromHex(txidHex));\n\tfor (const step of path) {\n\t\tconst sib = fromHex(step.hash);\n\t\tacc =\n\t\t\tstep.position === \"right\"\n\t\t\t\t? tagged(NODE_TAG, acc, sib)\n\t\t\t\t: tagged(NODE_TAG, sib, acc);\n\t}\n\tconst root = txMerkleRootHex.startsWith(\"0x\")\n\t\t? txMerkleRootHex.slice(2)\n\t\t: txMerkleRootHex;\n\treturn toHex(acc) === root;\n}\n\n/**\n * Fetch and parse a Nakamoto block from a stacks-node. `blockId` is the\n * index_block_hash (with or without 0x). Returns the raw bytes + parsed header +\n * the recomputed block_hash / index_block_hash so a caller can cross-check.\n */\nexport async function fetchNakamotoBlock(opts: {\n\tnodeUrl: string;\n\tblockId: string;\n\tfetchImpl?: typeof fetch;\n}): Promise<{\n\traw: Uint8Array;\n\theader: NakamotoBlockHeader;\n\tblockHash: string;\n\tindexBlockHash: string;\n}> {\n\tconst id = opts.blockId.startsWith(\"0x\")\n\t\t? opts.blockId.slice(2)\n\t\t: opts.blockId;\n\tconst f = opts.fetchImpl ?? fetch;\n\tconst res = await f(`${opts.nodeUrl.replace(/\\/+$/, \"\")}/v3/blocks/${id}`);\n\tif (!res.ok) {\n\t\tthrow new Error(`/v3/blocks/${id} returned ${res.status}`);\n\t}\n\tconst raw = new Uint8Array(await res.arrayBuffer());\n\tconst header = parseNakamotoBlockHeader(raw);\n\tconst blockHash = nakamotoBlockHash(header);\n\treturn {\n\t\traw,\n\t\theader,\n\t\tblockHash,\n\t\tindexBlockHash: nakamotoBlockId(blockHash, header.consensusHash),\n\t};\n}\n",
6
+ "import type { RewardSet } from \"./consensus.ts\";\nimport {\n\ttype NakamotoBlockHeader,\n\tnakamotoBlockHash,\n\tnakamotoBlockId,\n\tparseNakamotoBlockHeader,\n} from \"./nakamoto.ts\";\n\nexport interface NodeInfo {\n\tpeer_version: number;\n\tpox_consensus: string;\n\tburn_block_height: number;\n\tstable_pox_consensus: string;\n\tstable_burn_block_height: number;\n\tserver_version: string;\n\tnetwork_id: number;\n\tparent_network_id: number;\n\tstacks_tip_height: number;\n\tstacks_tip: string;\n\tstacks_tip_consensus_hash: string;\n\tgenesis_chainstate_hash: string;\n}\n\nexport interface BlockResponse {\n\thash: string;\n\theight: number;\n\tparent_block_hash: string;\n\tburn_block_height: number;\n\tburn_block_hash: string;\n\tburn_block_time: number;\n\tindex_block_hash: string;\n\tparent_index_block_hash: string;\n\tminer_txid: string;\n\ttxs: string[];\n\t// Full block data varies by endpoint — kept minimal for fetch use case\n}\n\nexport class StacksNodeClient {\n\tprivate rpcUrl: string;\n\n\tconstructor(rpcUrl?: string) {\n\t\tthis.rpcUrl =\n\t\t\trpcUrl || process.env.STACKS_NODE_RPC_URL || \"http://localhost:20443\";\n\t}\n\n\tasync getInfo(): Promise<NodeInfo> {\n\t\tconst res = await fetch(`${this.rpcUrl}/v2/info`, {\n\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Node RPC /v2/info returned ${res.status}`);\n\t\t}\n\t\treturn res.json() as Promise<NodeInfo>;\n\t}\n\n\tasync getBlock(height: number): Promise<BlockResponse | null> {\n\t\t// Stacks API v2 block-by-height endpoint\n\t\tconst res = await fetch(`${this.rpcUrl}/v2/blocks/${height}`, {\n\t\t\tsignal: AbortSignal.timeout(30_000),\n\t\t});\n\t\tif (res.status === 404) return null;\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Node RPC /v2/blocks/${height} returned ${res.status}`);\n\t\t}\n\t\treturn res.json() as Promise<BlockResponse>;\n\t}\n\n\t/**\n\t * Fetch + parse a Nakamoto block by its index_block_hash. Returns the raw\n\t * bytes, the parsed header, and the recomputed block_hash / index_block_hash\n\t * (so a caller can cross-check the node's answer). Null on 404.\n\t */\n\tasync getNakamotoBlock(blockId: string): Promise<{\n\t\traw: Uint8Array;\n\t\theader: NakamotoBlockHeader;\n\t\tblockHash: string;\n\t\tindexBlockHash: string;\n\t} | null> {\n\t\tconst id = blockId.startsWith(\"0x\") ? blockId.slice(2) : blockId;\n\t\tconst res = await fetch(`${this.rpcUrl}/v3/blocks/${id}`, {\n\t\t\tsignal: AbortSignal.timeout(30_000),\n\t\t});\n\t\tif (res.status === 404) return null;\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Node RPC /v3/blocks/${id} returned ${res.status}`);\n\t\t}\n\t\tconst raw = new Uint8Array(await res.arrayBuffer());\n\t\tconst header = parseNakamotoBlockHeader(raw);\n\t\tconst blockHash = nakamotoBlockHash(header);\n\t\treturn {\n\t\t\traw,\n\t\t\theader,\n\t\t\tblockHash,\n\t\t\tindexBlockHash: nakamotoBlockId(blockHash, header.consensusHash),\n\t\t};\n\t}\n\n\t/**\n\t * Fetch the reward set (signer keys + weights) for a reward cycle from\n\t * `/v3/stacker_set/{cycle}`. Null on 404 (cycle not yet computed).\n\t */\n\tasync getRewardSet(cycle: number): Promise<RewardSet | null> {\n\t\tconst res = await fetch(`${this.rpcUrl}/v3/stacker_set/${cycle}`, {\n\t\t\tsignal: AbortSignal.timeout(15_000),\n\t\t});\n\t\tif (res.status === 404) return null;\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node RPC /v3/stacker_set/${cycle} returned ${res.status}`,\n\t\t\t);\n\t\t}\n\t\tconst body = (await res.json()) as {\n\t\t\tstacker_set: { signers: { signing_key: string; weight: number }[] };\n\t\t};\n\t\tconst signers = body.stacker_set.signers.map((s) => ({\n\t\t\tsigning_key: s.signing_key.startsWith(\"0x\")\n\t\t\t\t? s.signing_key.slice(2)\n\t\t\t\t: s.signing_key,\n\t\t\tweight: s.weight,\n\t\t}));\n\t\treturn {\n\t\t\tsigners,\n\t\t\ttotal_weight: signers.reduce((sum, s) => sum + s.weight, 0),\n\t\t};\n\t}\n\n\tasync isHealthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst info = await this.getInfo();\n\t\t\treturn info.stacks_tip_height > 0;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync getContractAbi(contractId: string): Promise<unknown> {\n\t\tconst dotIdx = contractId.indexOf(\".\");\n\t\tconst address = contractId.slice(0, dotIdx);\n\t\tconst name = contractId.slice(dotIdx + 1);\n\t\tconst res = await fetch(\n\t\t\t`${this.rpcUrl}/v2/contracts/interface/${address}/${name}`,\n\t\t\t{\n\t\t\t\tsignal: AbortSignal.timeout(30_000),\n\t\t\t},\n\t\t);\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node RPC /v2/contracts/interface/${address}/${name} returned ${res.status}`,\n\t\t\t);\n\t\t}\n\t\treturn res.json();\n\t}\n\n\t/** Fetch a contract's Clarity source — used to parse declared `impl-trait`s. */\n\tasync getContractSource(contractId: string): Promise<string | null> {\n\t\tconst dotIdx = contractId.indexOf(\".\");\n\t\tconst address = contractId.slice(0, dotIdx);\n\t\tconst name = contractId.slice(dotIdx + 1);\n\t\tconst res = await fetch(\n\t\t\t`${this.rpcUrl}/v2/contracts/source/${address}/${name}`,\n\t\t\t{ signal: AbortSignal.timeout(30_000) },\n\t\t);\n\t\tif (!res.ok) return null;\n\t\tconst body = (await res.json()) as { source?: string };\n\t\treturn body.source ?? null;\n\t}\n\n\tgetRpcUrl(): string {\n\t\treturn this.rpcUrl;\n\t}\n}\n"
6
7
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;AA6BO,MAAM,iBAAiB;AAAA,EACrB;AAAA,EAER,WAAW,CAAC,QAAiB;AAAA,IAC5B,KAAK,SACJ,UAAU,QAAQ,IAAI,uBAAuB;AAAA;AAAA,OAGzC,QAAO,GAAsB;AAAA,IAClC,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK,kBAAkB;AAAA,MACjD,QAAQ,YAAY,QAAQ,GAAM;AAAA,IACnC,CAAC;AAAA,IACD,IAAI,CAAC,IAAI,IAAI;AAAA,MACZ,MAAM,IAAI,MAAM,8BAA8B,IAAI,QAAQ;AAAA,IAC3D;AAAA,IACA,OAAO,IAAI,KAAK;AAAA;AAAA,OAGX,SAAQ,CAAC,QAA+C;AAAA,IAE7D,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK,oBAAoB,UAAU;AAAA,MAC7D,QAAQ,YAAY,QAAQ,KAAM;AAAA,IACnC,CAAC;AAAA,IACD,IAAI,IAAI,WAAW;AAAA,MAAK,OAAO;AAAA,IAC/B,IAAI,CAAC,IAAI,IAAI;AAAA,MACZ,MAAM,IAAI,MAAM,uBAAuB,mBAAmB,IAAI,QAAQ;AAAA,IACvE;AAAA,IACA,OAAO,IAAI,KAAK;AAAA;AAAA,OAGX,UAAS,GAAqB;AAAA,IACnC,IAAI;AAAA,MACH,MAAM,OAAO,MAAM,KAAK,QAAQ;AAAA,MAChC,OAAO,KAAK,oBAAoB;AAAA,MAC/B,MAAM;AAAA,MACP,OAAO;AAAA;AAAA;AAAA,OAIH,eAAc,CAAC,YAAsC;AAAA,IAC1D,MAAM,SAAS,WAAW,QAAQ,GAAG;AAAA,IACrC,MAAM,UAAU,WAAW,MAAM,GAAG,MAAM;AAAA,IAC1C,MAAM,OAAO,WAAW,MAAM,SAAS,CAAC;AAAA,IACxC,MAAM,MAAM,MAAM,MACjB,GAAG,KAAK,iCAAiC,WAAW,QACpD;AAAA,MACC,QAAQ,YAAY,QAAQ,KAAM;AAAA,IACnC,CACD;AAAA,IACA,IAAI,CAAC,IAAI,IAAI;AAAA,MACZ,MAAM,IAAI,MACT,oCAAoC,WAAW,iBAAiB,IAAI,QACrE;AAAA,IACD;AAAA,IACA,OAAO,IAAI,KAAK;AAAA;AAAA,OAIX,kBAAiB,CAAC,YAA4C;AAAA,IACnE,MAAM,SAAS,WAAW,QAAQ,GAAG;AAAA,IACrC,MAAM,UAAU,WAAW,MAAM,GAAG,MAAM;AAAA,IAC1C,MAAM,OAAO,WAAW,MAAM,SAAS,CAAC;AAAA,IACxC,MAAM,MAAM,MAAM,MACjB,GAAG,KAAK,8BAA8B,WAAW,QACjD,EAAE,QAAQ,YAAY,QAAQ,KAAM,EAAE,CACvC;AAAA,IACA,IAAI,CAAC,IAAI;AAAA,MAAI,OAAO;AAAA,IACpB,MAAM,OAAQ,MAAM,IAAI,KAAK;AAAA,IAC7B,OAAO,KAAK,UAAU;AAAA;AAAA,EAGvB,SAAS,GAAW;AAAA,IACnB,OAAO,KAAK;AAAA;AAEd;",
8
- "debugId": "1B7D01390D90430B64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "26D700D33629603864756E2164756E21",
9
10
  "names": []
10
11
  }
@@ -0,0 +1,38 @@
1
+ /** Mainnet PoX schedule (from /v2/pox) — overridable for other networks. */
2
+ declare const MAINNET_FIRST_BURN_HEIGHT = 666050;
3
+ declare const MAINNET_REWARD_CYCLE_LENGTH = 2100;
4
+ interface RewardSetSigner {
5
+ /** 33-byte compressed secp256k1 public key (hex, no 0x). */
6
+ signing_key: string;
7
+ weight: number;
8
+ }
9
+ interface RewardSet {
10
+ signers: RewardSetSigner[];
11
+ total_weight: number;
12
+ }
13
+ interface SignerVerification {
14
+ /** Summed weight of distinct reward-set signers that signed the block. */
15
+ signedWeight: number;
16
+ totalWeight: number;
17
+ /** floor(total_weight * 7 / 10). */
18
+ threshold: number;
19
+ thresholdMet: boolean;
20
+ /** Count of distinct reward-set keys that signed. */
21
+ matchedSigners: number;
22
+ }
23
+ /** Reward cycle for a burn block height. Defaults to the mainnet PoX schedule. */
24
+ declare function rewardCycle(burnBlockHeight: number, opts?: {
25
+ firstBurnHeight?: number
26
+ cycleLength?: number
27
+ }): number;
28
+ /** Recover the signer pubkey (compressed hex) from a 65-byte VRS recoverable
29
+ * ECDSA signature over the block_hash. */
30
+ declare function recoverSignerKey(blockHashHex: string, vrsSignatureHex: string): string;
31
+ /**
32
+ * Recover each header signer signature, match it to the reward set, and sum the
33
+ * distinct matched signers' weights against the 70% threshold. A signature that
34
+ * fails to recover or whose key isn't in the set simply doesn't count — never
35
+ * a false positive.
36
+ */
37
+ declare function verifySignerSignatures(blockHashHex: string, signerSignaturesHex: string[], rewardSet: RewardSet): SignerVerification;
38
+ export { verifySignerSignatures, rewardCycle, recoverSignerKey, SignerVerification, RewardSetSigner, RewardSet, MAINNET_REWARD_CYCLE_LENGTH, MAINNET_FIRST_BURN_HEIGHT };
@@ -0,0 +1,67 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/node/consensus.ts
18
+ import { recoverPublicKey } from "@secondlayer/stacks/utils";
19
+ var strip = (h) => h.startsWith("0x") ? h.slice(2) : h;
20
+ var MAINNET_FIRST_BURN_HEIGHT = 666050;
21
+ var MAINNET_REWARD_CYCLE_LENGTH = 2100;
22
+ function rewardCycle(burnBlockHeight, opts = {}) {
23
+ const first = opts.firstBurnHeight ?? MAINNET_FIRST_BURN_HEIGHT;
24
+ const length = opts.cycleLength ?? MAINNET_REWARD_CYCLE_LENGTH;
25
+ return Math.floor((burnBlockHeight - first) / length);
26
+ }
27
+ function recoverSignerKey(blockHashHex, vrsSignatureHex) {
28
+ return strip(recoverPublicKey(strip(blockHashHex), strip(vrsSignatureHex), true));
29
+ }
30
+ function verifySignerSignatures(blockHashHex, signerSignaturesHex, rewardSet) {
31
+ const byKey = new Map(rewardSet.signers.map((s) => [strip(s.signing_key), s.weight]));
32
+ const seen = new Set;
33
+ let signedWeight = 0;
34
+ let matchedSigners = 0;
35
+ for (const sig of signerSignaturesHex) {
36
+ let pubkey;
37
+ try {
38
+ pubkey = recoverSignerKey(blockHashHex, sig);
39
+ } catch {
40
+ continue;
41
+ }
42
+ const weight = byKey.get(pubkey);
43
+ if (weight !== undefined && !seen.has(pubkey)) {
44
+ seen.add(pubkey);
45
+ signedWeight += weight;
46
+ matchedSigners += 1;
47
+ }
48
+ }
49
+ const threshold = Math.floor(rewardSet.total_weight * 7 / 10);
50
+ return {
51
+ signedWeight,
52
+ totalWeight: rewardSet.total_weight,
53
+ threshold,
54
+ thresholdMet: signedWeight >= threshold,
55
+ matchedSigners
56
+ };
57
+ }
58
+ export {
59
+ verifySignerSignatures,
60
+ rewardCycle,
61
+ recoverSignerKey,
62
+ MAINNET_REWARD_CYCLE_LENGTH,
63
+ MAINNET_FIRST_BURN_HEIGHT
64
+ };
65
+
66
+ //# debugId=1937481E7D2EF9AA64756E2164756E21
67
+ //# sourceMappingURL=consensus.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/node/consensus.ts"],
4
+ "sourcesContent": [
5
+ "import { recoverPublicKey } from \"@secondlayer/stacks/utils\";\n\n/**\n * Consensus verification: do ≥70% of a reward cycle's signer weight attest to a\n * Nakamoto block? Each header `signer_signature` is a recoverable ECDSA over the\n * block_hash; recover the pubkey, match it to the cycle's reward set, and sum the\n * matched signers' weights. Verified bit-exact against mainnet 3.4.0.0.3 (see\n * docs/design/trustless-verification-proofs.md, signer-signature appendix).\n */\nconst strip = (h: string): string => (h.startsWith(\"0x\") ? h.slice(2) : h);\n\n/** Mainnet PoX schedule (from /v2/pox) — overridable for other networks. */\nexport const MAINNET_FIRST_BURN_HEIGHT = 666_050;\nexport const MAINNET_REWARD_CYCLE_LENGTH = 2100;\n\nexport interface RewardSetSigner {\n\t/** 33-byte compressed secp256k1 public key (hex, no 0x). */\n\tsigning_key: string;\n\tweight: number;\n}\n\nexport interface RewardSet {\n\tsigners: RewardSetSigner[];\n\ttotal_weight: number;\n}\n\nexport interface SignerVerification {\n\t/** Summed weight of distinct reward-set signers that signed the block. */\n\tsignedWeight: number;\n\ttotalWeight: number;\n\t/** floor(total_weight * 7 / 10). */\n\tthreshold: number;\n\tthresholdMet: boolean;\n\t/** Count of distinct reward-set keys that signed. */\n\tmatchedSigners: number;\n}\n\n/** Reward cycle for a burn block height. Defaults to the mainnet PoX schedule. */\nexport function rewardCycle(\n\tburnBlockHeight: number,\n\topts: { firstBurnHeight?: number; cycleLength?: number } = {},\n): number {\n\tconst first = opts.firstBurnHeight ?? MAINNET_FIRST_BURN_HEIGHT;\n\tconst length = opts.cycleLength ?? MAINNET_REWARD_CYCLE_LENGTH;\n\treturn Math.floor((burnBlockHeight - first) / length);\n}\n\n/** Recover the signer pubkey (compressed hex) from a 65-byte VRS recoverable\n * ECDSA signature over the block_hash. */\nexport function recoverSignerKey(\n\tblockHashHex: string,\n\tvrsSignatureHex: string,\n): string {\n\treturn strip(\n\t\trecoverPublicKey(strip(blockHashHex), strip(vrsSignatureHex), true),\n\t);\n}\n\n/**\n * Recover each header signer signature, match it to the reward set, and sum the\n * distinct matched signers' weights against the 70% threshold. A signature that\n * fails to recover or whose key isn't in the set simply doesn't count — never\n * a false positive.\n */\nexport function verifySignerSignatures(\n\tblockHashHex: string,\n\tsignerSignaturesHex: string[],\n\trewardSet: RewardSet,\n): SignerVerification {\n\tconst byKey = new Map<string, number>(\n\t\trewardSet.signers.map((s) => [strip(s.signing_key), s.weight]),\n\t);\n\tconst seen = new Set<string>();\n\tlet signedWeight = 0;\n\tlet matchedSigners = 0;\n\tfor (const sig of signerSignaturesHex) {\n\t\tlet pubkey: string;\n\t\ttry {\n\t\t\tpubkey = recoverSignerKey(blockHashHex, sig);\n\t\t} catch {\n\t\t\tcontinue; // malformed signature → no credit\n\t\t}\n\t\tconst weight = byKey.get(pubkey);\n\t\tif (weight !== undefined && !seen.has(pubkey)) {\n\t\t\tseen.add(pubkey);\n\t\t\tsignedWeight += weight;\n\t\t\tmatchedSigners += 1;\n\t\t}\n\t}\n\tconst threshold = Math.floor((rewardSet.total_weight * 7) / 10);\n\treturn {\n\t\tsignedWeight,\n\t\ttotalWeight: rewardSet.total_weight,\n\t\tthreshold,\n\t\tthresholdMet: signedWeight >= threshold,\n\t\tmatchedSigners,\n\t};\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AASA,IAAM,QAAQ,CAAC,MAAuB,EAAE,WAAW,IAAI,IAAI,EAAE,MAAM,CAAC,IAAI;AAGjE,IAAM,4BAA4B;AAClC,IAAM,8BAA8B;AAyBpC,SAAS,WAAW,CAC1B,iBACA,OAA2D,CAAC,GACnD;AAAA,EACT,MAAM,QAAQ,KAAK,mBAAmB;AAAA,EACtC,MAAM,SAAS,KAAK,eAAe;AAAA,EACnC,OAAO,KAAK,OAAO,kBAAkB,SAAS,MAAM;AAAA;AAK9C,SAAS,gBAAgB,CAC/B,cACA,iBACS;AAAA,EACT,OAAO,MACN,iBAAiB,MAAM,YAAY,GAAG,MAAM,eAAe,GAAG,IAAI,CACnE;AAAA;AASM,SAAS,sBAAsB,CACrC,cACA,qBACA,WACqB;AAAA,EACrB,MAAM,QAAQ,IAAI,IACjB,UAAU,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,EAAE,MAAM,CAAC,CAC9D;AAAA,EACA,MAAM,OAAO,IAAI;AAAA,EACjB,IAAI,eAAe;AAAA,EACnB,IAAI,iBAAiB;AAAA,EACrB,WAAW,OAAO,qBAAqB;AAAA,IACtC,IAAI;AAAA,IACJ,IAAI;AAAA,MACH,SAAS,iBAAiB,cAAc,GAAG;AAAA,MAC1C,MAAM;AAAA,MACP;AAAA;AAAA,IAED,MAAM,SAAS,MAAM,IAAI,MAAM;AAAA,IAC/B,IAAI,WAAW,aAAa,CAAC,KAAK,IAAI,MAAM,GAAG;AAAA,MAC9C,KAAK,IAAI,MAAM;AAAA,MACf,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,IACnB;AAAA,EACD;AAAA,EACA,MAAM,YAAY,KAAK,MAAO,UAAU,eAAe,IAAK,EAAE;AAAA,EAC9D,OAAO;AAAA,IACN;AAAA,IACA,aAAa,UAAU;AAAA,IACvB;AAAA,IACA,cAAc,gBAAgB;AAAA,IAC9B;AAAA,EACD;AAAA;",
8
+ "debugId": "1937481E7D2EF9AA64756E2164756E21",
9
+ "names": []
10
+ }
@@ -6,6 +6,8 @@ interface BlocksTable {
6
6
  parent_hash: string;
7
7
  burn_block_height: number;
8
8
  burn_block_hash: ColumnType<string | null, string | null | undefined, string | null>;
9
+ /** Nakamoto StacksBlockId. Null on rows ingested before it was persisted. */
10
+ index_block_hash: ColumnType<string | null, string | null | undefined, string | null>;
9
11
  timestamp: number;
10
12
  canonical: Generated<boolean>;
11
13
  created_at: Generated<Date>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Nakamoto block-header parsing + consensus hashing for trustless verification.
3
+ *
4
+ * Every constant here is verified bit-exact against mainnet `stacks-node
5
+ * 3.4.0.0.3` (see docs/design/trustless-verification-proofs.md, Appendix). This
6
+ * Building block for transaction-inclusion / block-canonicity proofs: fetch a
7
+ * raw `/v3/blocks/{id}` body, parse the header, and recompute the block_hash,
8
+ * index_block_hash, and tx_merkle_root the chain itself commits to.
9
+ */
10
+ /** SHA-512/256 — the hash Stacks uses everywhere (NOT truncated SHA-512). */
11
+ declare function sha512_256(bytes: Uint8Array): Uint8Array;
12
+ interface NakamotoBlockHeader {
13
+ version: number;
14
+ chainLength: bigint;
15
+ burnSpent: bigint;
16
+ /** 20-byte consensus hash (hex). */
17
+ consensusHash: string;
18
+ /** 32-byte parent StacksBlockId (hex). */
19
+ parentBlockId: string;
20
+ /** 32-byte SHA512/256 merkle root over the block's txids (hex). */
21
+ txMerkleRoot: string;
22
+ /** 32-byte MARF root after applying this block (hex). */
23
+ stateIndexRoot: string;
24
+ timestamp: bigint;
25
+ /** 65-byte recoverable ECDSA miner signature (hex). */
26
+ minerSignature: string;
27
+ /** Per-signer recoverable ECDSA signatures, reward-set order (hex, 65B each). */
28
+ signerSignatures: string[];
29
+ /** Full serialized pox_treatment BitVec bytes (u16 bits ‖ u32 len ‖ data). */
30
+ poxTreatment: Uint8Array;
31
+ /**
32
+ * Exact bytes whose SHA512/256 IS the block_hash / signer_signature_hash:
33
+ * the header with the signer_signature vector omitted (header[0:206] ‖ pox).
34
+ */
35
+ signerSignatureHashPreimage: Uint8Array;
36
+ /** Offset at which the tx `Vec` begins (= total header byte length). */
37
+ headerByteLength: number;
38
+ }
39
+ /** Parse the Nakamoto block header from the raw `/v3/blocks` body. */
40
+ declare function parseNakamotoBlockHeader(raw: Uint8Array): NakamotoBlockHeader;
41
+ /**
42
+ * block_hash (== signer_signature_hash): SHA512/256 over the header with the
43
+ * signer_signature vector omitted. This is what each signer signs.
44
+ */
45
+ declare function nakamotoBlockHash(header: NakamotoBlockHeader): string;
46
+ /** index_block_hash (StacksBlockId) = SHA512/256(block_hash ‖ consensus_hash). */
47
+ declare function nakamotoBlockId(blockHashHex: string, consensusHashHex: string): string;
48
+ /** A Stacks txid = SHA512/256 of the transaction's consensus serialization. */
49
+ declare function stacksTxid(rawTx: Uint8Array): string;
50
+ /**
51
+ * tx_merkle_root over the block's txids (hex), reproducing the consensus rule:
52
+ * leaf = H(0x00 ‖ txid), node = H(0x01 ‖ left ‖ right), odd level duplicates the
53
+ * last node. Returns the root hex; throws on an empty tx list.
54
+ */
55
+ declare function txMerkleRoot(txidsHex: string[]): string;
56
+ /** One authentication-path step: the sibling hash and which side it's on. */
57
+ interface MerkleProofStep {
58
+ /** Side the SIBLING is on relative to the accumulator. */
59
+ position: "left" | "right";
60
+ /** Sibling node hash (hex). */
61
+ hash: string;
62
+ }
63
+ /**
64
+ * Build the tx-inclusion authentication path for the tx at `index` in a block,
65
+ * reproducing the consensus merkle tree (incl. duplicate-last-on-odd). The path
66
+ * lets a verifier recompute `tx_merkle_root` from just the target txid.
67
+ */
68
+ declare function txMerkleProof(txidsHex: string[], index: number): MerkleProofStep[];
69
+ /**
70
+ * Verify a tx-inclusion proof: fold the target `txid` (hex) up through `path`
71
+ * and check it equals `txMerkleRoot` (hex). The verifier recomputes the txid
72
+ * itself from the raw tx bytes, so nothing here is trusted.
73
+ */
74
+ declare function verifyTxMerkleProof(txidHex: string, path: MerkleProofStep[], txMerkleRootHex: string): boolean;
75
+ /**
76
+ * Fetch and parse a Nakamoto block from a stacks-node. `blockId` is the
77
+ * index_block_hash (with or without 0x). Returns the raw bytes + parsed header +
78
+ * the recomputed block_hash / index_block_hash so a caller can cross-check.
79
+ */
80
+ declare function fetchNakamotoBlock(opts: {
81
+ nodeUrl: string
82
+ blockId: string
83
+ fetchImpl?: typeof fetch
84
+ }): Promise<{
85
+ raw: Uint8Array
86
+ header: NakamotoBlockHeader
87
+ blockHash: string
88
+ indexBlockHash: string
89
+ }>;
90
+ export { verifyTxMerkleProof, txMerkleRoot, txMerkleProof, stacksTxid, sha512_256, parseNakamotoBlockHeader, nakamotoBlockId, nakamotoBlockHash, fetchNakamotoBlock, NakamotoBlockHeader, MerkleProofStep };