@inversealtruism/csd-codec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InverseAltruism
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/dist/index.cjs ADDED
@@ -0,0 +1,444 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CHAIN_ID: () => CHAIN_ID,
24
+ CHAIN_ID_HASH: () => CHAIN_ID_HASH,
25
+ COIN: () => COIN,
26
+ EPOCH_LEN: () => EPOCH_LEN,
27
+ GENESIS_HASH: () => GENESIS_HASH,
28
+ GENESIS_TIME: () => GENESIS_TIME,
29
+ HALVING_INTERVAL: () => HALVING_INTERVAL,
30
+ INITIAL_BITS: () => INITIAL_BITS,
31
+ INITIAL_REWARD: () => INITIAL_REWARD,
32
+ LWMA_SOLVETIME_MAX_FACTOR: () => LWMA_SOLVETIME_MAX_FACTOR,
33
+ LWMA_WINDOW: () => LWMA_WINDOW,
34
+ MAX_FUTURE_DRIFT_SECS: () => MAX_FUTURE_DRIFT_SECS,
35
+ MAX_HALVINGS: () => MAX_HALVINGS,
36
+ MAX_TX_INPUTS: () => MAX_TX_INPUTS,
37
+ MAX_TX_OUTPUTS: () => MAX_TX_OUTPUTS,
38
+ MIN_BLOCK_SPACING_SECS: () => MIN_BLOCK_SPACING_SECS,
39
+ MIN_FEE_ATTEST: () => MIN_FEE_ATTEST,
40
+ MIN_FEE_PROPOSE: () => MIN_FEE_PROPOSE,
41
+ MTP_WINDOW: () => MTP_WINDOW,
42
+ POW_LIMIT_BITS: () => POW_LIMIT_BITS,
43
+ TARGET_BLOCK_SECS: () => TARGET_BLOCK_SECS,
44
+ bigIntToTarget: () => bigIntToTarget,
45
+ bitsToTarget: () => bitsToTarget,
46
+ blockReward: () => blockReward,
47
+ bytesToHex: () => import_utils.bytesToHex,
48
+ canonicalJson: () => canonicalJson,
49
+ concatBytes: () => import_utils.concatBytes,
50
+ deserialize: () => deserialize,
51
+ hb: () => hb,
52
+ hbFixed: () => hbFixed,
53
+ headerHash: () => headerHash,
54
+ headerHashBytes: () => headerHashBytes,
55
+ hexToBytes: () => import_utils.hexToBytes,
56
+ hx: () => hx,
57
+ lenBytes: () => lenBytes,
58
+ merkleBranch: () => merkleBranch,
59
+ merkleRoot: () => merkleRoot,
60
+ payloadHash: () => payloadHash,
61
+ powOk: () => powOk,
62
+ serialize: () => serialize,
63
+ serializeHeader: () => serializeHeader,
64
+ sha256d: () => sha256d,
65
+ sighash: () => sighash,
66
+ strip0x: () => strip0x,
67
+ strippedTx: () => strippedTx,
68
+ taggedHash: () => taggedHash,
69
+ targetToBigInt: () => targetToBigInt,
70
+ targetToBits: () => targetToBits,
71
+ txid: () => txid,
72
+ u32: () => u32,
73
+ u64: () => u64,
74
+ utf8ToBytes: () => import_utils.utf8ToBytes,
75
+ verifyContentBytes: () => verifyContentBytes,
76
+ verifyMerkleProof: () => verifyMerkleProof,
77
+ workForBits: () => workForBits
78
+ });
79
+ module.exports = __toCommonJS(index_exports);
80
+
81
+ // src/bytes.ts
82
+ var import_utils = require("@noble/hashes/utils");
83
+ var import_sha256 = require("@noble/hashes/sha256");
84
+ var strip0x = (h) => h.startsWith("0x") ? h.slice(2) : h;
85
+ var hb = (h) => (0, import_utils.hexToBytes)(strip0x(h));
86
+ var hx = (b) => "0x" + (0, import_utils.bytesToHex)(b);
87
+ var sha256d = (b) => (0, import_sha256.sha256)((0, import_sha256.sha256)(b));
88
+ function u32(n) {
89
+ const b = new Uint8Array(4);
90
+ new DataView(b.buffer).setUint32(0, n >>> 0, true);
91
+ return b;
92
+ }
93
+ function u64(n) {
94
+ const b = new Uint8Array(8);
95
+ new DataView(b.buffer).setBigUint64(0, BigInt(n), true);
96
+ return b;
97
+ }
98
+ var lenBytes = (b) => (0, import_utils.concatBytes)(u64(b.length), b);
99
+ function hbFixed(h, n) {
100
+ const b = hb(h);
101
+ if (b.length !== n) throw new Error(`expected a ${n}-byte (0x\u2026${n * 2}-hex) field, got ${b.length} bytes`);
102
+ return b;
103
+ }
104
+
105
+ // src/params.ts
106
+ var import_utils2 = require("@noble/hashes/utils");
107
+ var CHAIN_ID = "compute-substrate-mainnet";
108
+ var CHAIN_ID_HASH = (0, import_utils2.hexToBytes)("1b17c7b04d05394674ca2c8e24f7433e251a1973cac2000c7b60966546e0b875");
109
+ var GENESIS_HASH = "0x00000052c2821f71b19c3d79dfabfb12d4076ba15d83b47d008e582aad6c0d52";
110
+ var GENESIS_TIME = 1777474800;
111
+ var TARGET_BLOCK_SECS = 120;
112
+ var INITIAL_BITS = 503382015;
113
+ var POW_LIMIT_BITS = 503382015;
114
+ var LWMA_WINDOW = 45;
115
+ var LWMA_SOLVETIME_MAX_FACTOR = 12;
116
+ var MAX_FUTURE_DRIFT_SECS = 2 * 60 * 60;
117
+ var MTP_WINDOW = 11;
118
+ var MIN_BLOCK_SPACING_SECS = 60;
119
+ var EPOCH_LEN = 30;
120
+ var COIN = 1e8;
121
+ var INITIAL_REWARD = 50 * COIN;
122
+ var HALVING_INTERVAL = 1051200;
123
+ var MAX_HALVINGS = 64;
124
+ var MIN_FEE_PROPOSE = 25e6;
125
+ var MIN_FEE_ATTEST = 5e6;
126
+ var MAX_TX_INPUTS = 512;
127
+ var MAX_TX_OUTPUTS = 512;
128
+ function blockReward(height) {
129
+ const halvings = Math.floor(height / HALVING_INTERVAL);
130
+ if (halvings >= MAX_HALVINGS) return 0;
131
+ return Math.floor(INITIAL_REWARD / 2 ** halvings);
132
+ }
133
+
134
+ // src/tx.ts
135
+ var import_sha2562 = require("@noble/hashes/sha256");
136
+ var import_utils3 = require("@noble/hashes/utils");
137
+ var COINBASE_TXID = "0x" + "00".repeat(32);
138
+ var COINBASE_VOUT = 4294967295;
139
+ var isCoinbaseInput = (i) => i.prevTxid === COINBASE_TXID && i.vout === COINBASE_VOUT;
140
+ function strippedTx(tx) {
141
+ return { ...tx, inputs: tx.inputs.map((i) => isCoinbaseInput(i) ? i : { ...i, scriptSig: "0x" }) };
142
+ }
143
+ function serializeApp(app) {
144
+ if (app.type === "None") return u32(0);
145
+ if (app.type === "Propose")
146
+ return (0, import_utils3.concatBytes)(u32(1), lenBytes((0, import_utils3.utf8ToBytes)(app.domain)), hbFixed(app.payloadHash, 32), lenBytes((0, import_utils3.utf8ToBytes)(app.uri)), u64(app.expiresEpoch));
147
+ return (0, import_utils3.concatBytes)(u32(2), hbFixed(app.proposalId, 32), u32(app.score), u32(app.confidence));
148
+ }
149
+ function serialize(tx) {
150
+ const parts = [u32(tx.version), u64(tx.inputs.length)];
151
+ for (const i of tx.inputs) parts.push(hbFixed(i.prevTxid, 32), u32(i.vout), lenBytes(hb(i.scriptSig)));
152
+ parts.push(u64(tx.outputs.length));
153
+ for (const o of tx.outputs) parts.push(u64(o.value), hbFixed(o.scriptPubkey, 20));
154
+ parts.push(u32(tx.locktime), serializeApp(tx.app));
155
+ return (0, import_utils3.concatBytes)(...parts);
156
+ }
157
+ function txid(tx) {
158
+ return hx(sha256d(serialize(strippedTx(tx))));
159
+ }
160
+ function taggedHash(tag, msg) {
161
+ const t = (0, import_sha2562.sha256)((0, import_utils3.utf8ToBytes)(tag));
162
+ return (0, import_sha2562.sha256)((0, import_utils3.concatBytes)(t, t, msg));
163
+ }
164
+ function sighash(tx) {
165
+ return hx(sha256d(taggedHash("CSD_SIG_V1", (0, import_utils3.concatBytes)(serialize(strippedTx(tx)), CHAIN_ID_HASH))));
166
+ }
167
+ var Reader = class {
168
+ constructor(b, dv = new DataView(b.buffer, b.byteOffset, b.byteLength)) {
169
+ this.b = b;
170
+ this.dv = dv;
171
+ }
172
+ b;
173
+ dv;
174
+ o = 0;
175
+ u32() {
176
+ const v = this.dv.getUint32(this.o, true);
177
+ this.o += 4;
178
+ return v;
179
+ }
180
+ u64() {
181
+ const v = this.dv.getBigUint64(this.o, true);
182
+ this.o += 8;
183
+ return v;
184
+ }
185
+ take(n) {
186
+ const v = this.b.subarray(this.o, this.o + n);
187
+ if (v.length !== n) throw new Error("unexpected end of bytes");
188
+ this.o += n;
189
+ return v;
190
+ }
191
+ vec() {
192
+ return this.take(Number(this.u64()));
193
+ }
194
+ fixedHex(n) {
195
+ return hx(this.take(n));
196
+ }
197
+ str() {
198
+ return new TextDecoder().decode(this.vec());
199
+ }
200
+ get offset() {
201
+ return this.o;
202
+ }
203
+ get length() {
204
+ return this.b.length;
205
+ }
206
+ };
207
+ function readApp(r) {
208
+ const tag = r.u32();
209
+ if (tag === 0) return { type: "None" };
210
+ if (tag === 1) return { type: "Propose", domain: r.str(), payloadHash: r.fixedHex(32), uri: r.str(), expiresEpoch: r.u64() };
211
+ if (tag === 2) return { type: "Attest", proposalId: r.fixedHex(32), score: r.u32(), confidence: r.u32() };
212
+ throw new Error(`unknown AppPayload variant ${tag}`);
213
+ }
214
+ function deserialize(bytes) {
215
+ const r = new Reader(bytes);
216
+ const version = r.u32();
217
+ const nIn = Number(r.u64());
218
+ const inputs = [];
219
+ for (let i = 0; i < nIn; i++) inputs.push({ prevTxid: r.fixedHex(32), vout: r.u32(), scriptSig: hx(r.vec()) });
220
+ const nOut = Number(r.u64());
221
+ const outputs = [];
222
+ for (let i = 0; i < nOut; i++) outputs.push({ value: r.u64(), scriptPubkey: r.fixedHex(20) });
223
+ const locktime = r.u32();
224
+ const app = readApp(r);
225
+ return { version, inputs, outputs, locktime, app };
226
+ }
227
+
228
+ // src/header.ts
229
+ function serializeHeader(h) {
230
+ const buf = new Uint8Array(84);
231
+ buf.set(u32(h.version), 0);
232
+ buf.set(hbFixed(h.prev, 32), 4);
233
+ buf.set(hbFixed(h.merkle, 32), 36);
234
+ buf.set(u64(h.time), 68);
235
+ buf.set(u32(h.bits), 76);
236
+ buf.set(u32(h.nonce), 80);
237
+ return buf;
238
+ }
239
+ function headerHash(h) {
240
+ return hx(sha256d(serializeHeader(h)));
241
+ }
242
+ function headerHashBytes(h) {
243
+ return sha256d(serializeHeader(h));
244
+ }
245
+ function bitsToTarget(bits) {
246
+ const exp = bits >>> 24 & 255;
247
+ const mant = bits & 16777215;
248
+ const out = new Uint8Array(32);
249
+ if (exp === 0 || mant === 0) return out;
250
+ if ((mant & 8388608) !== 0) return out;
251
+ if (exp > 32) return out;
252
+ let target;
253
+ if (exp <= 3) target = BigInt(mant) >> BigInt(8 * (3 - exp));
254
+ else target = BigInt(mant) << BigInt(8 * (exp - 3));
255
+ if (target === 0n) return out;
256
+ if (target >= 1n << 256n) return out;
257
+ for (let i = 31; i >= 0 && target > 0n; i--) {
258
+ out[i] = Number(target & 0xffn);
259
+ target >>= 8n;
260
+ }
261
+ return out;
262
+ }
263
+ function targetToBigInt(target) {
264
+ let v = 0n;
265
+ for (const byte of target) v = v << 8n | BigInt(byte);
266
+ return v;
267
+ }
268
+ function bigIntToTarget(x) {
269
+ const out = new Uint8Array(32);
270
+ for (let i = 31; i >= 0 && x > 0n; i--) {
271
+ out[i] = Number(x & 0xffn);
272
+ x >>= 8n;
273
+ }
274
+ return out;
275
+ }
276
+ function minBE(x) {
277
+ if (x === 0n) return [];
278
+ const b = [];
279
+ while (x > 0n) {
280
+ b.unshift(Number(x & 0xffn));
281
+ x >>= 8n;
282
+ }
283
+ return b;
284
+ }
285
+ function targetToBits(target) {
286
+ const x = targetToBigInt(target);
287
+ if (x === 0n) return 0;
288
+ const bytes = minBE(x);
289
+ let exp = bytes.length;
290
+ let mant;
291
+ if (exp <= 3) {
292
+ const shift = BigInt(8 * (3 - exp));
293
+ mant = Number(x << shift & 0xffffffffn) & 16777215;
294
+ } else {
295
+ mant = (bytes[0] << 16 | bytes[1] << 8 | bytes[2]) >>> 0;
296
+ }
297
+ if ((mant & 8388608) !== 0) {
298
+ mant >>= 8;
299
+ exp += 1;
300
+ }
301
+ mant &= 16777215;
302
+ return (exp << 24 | mant) >>> 0;
303
+ }
304
+ function powOk(headerHashBE, bits) {
305
+ const target = bitsToTarget(bits);
306
+ if (target.every((b) => b === 0)) return false;
307
+ return targetToBigInt(headerHashBE) <= targetToBigInt(target);
308
+ }
309
+ function workForBits(bits) {
310
+ const target = targetToBigInt(bitsToTarget(bits));
311
+ if (target === 0n) return 0n;
312
+ return (1n << 256n) / (target + 1n);
313
+ }
314
+ function merkleRoot(txidsHex) {
315
+ if (txidsHex.length === 0) return "0x" + "00".repeat(32);
316
+ let layer = txidsHex.map(hb);
317
+ while (layer.length > 1) {
318
+ const next = [];
319
+ for (let i = 0; i < layer.length; i += 2) {
320
+ const left = layer[i];
321
+ const right = i + 1 < layer.length ? layer[i + 1] : layer[i];
322
+ const buf = new Uint8Array(64);
323
+ buf.set(left, 0);
324
+ buf.set(right, 32);
325
+ next.push(sha256d(buf));
326
+ }
327
+ layer = next;
328
+ }
329
+ return hx(layer[0]);
330
+ }
331
+ function verifyMerkleProof(txidHex, pos, branchHex, merkleRootHex) {
332
+ let cur = hb(txidHex);
333
+ let idx = pos;
334
+ for (const sibHex of branchHex) {
335
+ const sib = hb(sibHex);
336
+ const buf = new Uint8Array(64);
337
+ if (idx & 1) {
338
+ buf.set(sib, 0);
339
+ buf.set(cur, 32);
340
+ } else {
341
+ buf.set(cur, 0);
342
+ buf.set(sib, 32);
343
+ }
344
+ cur = sha256d(buf);
345
+ idx >>= 1;
346
+ }
347
+ return hx(cur).toLowerCase() === merkleRootHex.toLowerCase();
348
+ }
349
+ function merkleBranch(txidsHex, pos) {
350
+ let layer = txidsHex.map(hb);
351
+ const branch = [];
352
+ let idx = pos;
353
+ while (layer.length > 1) {
354
+ const sibIdx = idx ^ 1;
355
+ const sib = sibIdx < layer.length ? layer[sibIdx] : layer[idx];
356
+ branch.push(hx(sib));
357
+ const next = [];
358
+ for (let i = 0; i < layer.length; i += 2) {
359
+ const left = layer[i];
360
+ const right = i + 1 < layer.length ? layer[i + 1] : layer[i];
361
+ const buf = new Uint8Array(64);
362
+ buf.set(left, 0);
363
+ buf.set(right, 32);
364
+ next.push(sha256d(buf));
365
+ }
366
+ layer = next;
367
+ idx >>= 1;
368
+ }
369
+ return branch;
370
+ }
371
+
372
+ // src/content.ts
373
+ var import_sha2563 = require("@noble/hashes/sha256");
374
+ var import_utils4 = require("@noble/hashes/utils");
375
+ function canonicalJson(v) {
376
+ if (v === null || typeof v !== "object") return JSON.stringify(v);
377
+ if (Array.isArray(v)) return "[" + v.map(canonicalJson).join(",") + "]";
378
+ const o = v;
379
+ return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + canonicalJson(o[k])).join(",") + "}";
380
+ }
381
+ function payloadHash(content) {
382
+ return "0x" + (0, import_utils4.bytesToHex)((0, import_sha2563.sha256)((0, import_utils4.utf8ToBytes)(canonicalJson(content))));
383
+ }
384
+ function verifyContentBytes(bytes, payloadHashHex) {
385
+ return "0x" + (0, import_utils4.bytesToHex)((0, import_sha2563.sha256)(bytes)) === payloadHashHex.toLowerCase();
386
+ }
387
+ // Annotate the CommonJS export names for ESM import in node:
388
+ 0 && (module.exports = {
389
+ CHAIN_ID,
390
+ CHAIN_ID_HASH,
391
+ COIN,
392
+ EPOCH_LEN,
393
+ GENESIS_HASH,
394
+ GENESIS_TIME,
395
+ HALVING_INTERVAL,
396
+ INITIAL_BITS,
397
+ INITIAL_REWARD,
398
+ LWMA_SOLVETIME_MAX_FACTOR,
399
+ LWMA_WINDOW,
400
+ MAX_FUTURE_DRIFT_SECS,
401
+ MAX_HALVINGS,
402
+ MAX_TX_INPUTS,
403
+ MAX_TX_OUTPUTS,
404
+ MIN_BLOCK_SPACING_SECS,
405
+ MIN_FEE_ATTEST,
406
+ MIN_FEE_PROPOSE,
407
+ MTP_WINDOW,
408
+ POW_LIMIT_BITS,
409
+ TARGET_BLOCK_SECS,
410
+ bigIntToTarget,
411
+ bitsToTarget,
412
+ blockReward,
413
+ bytesToHex,
414
+ canonicalJson,
415
+ concatBytes,
416
+ deserialize,
417
+ hb,
418
+ hbFixed,
419
+ headerHash,
420
+ headerHashBytes,
421
+ hexToBytes,
422
+ hx,
423
+ lenBytes,
424
+ merkleBranch,
425
+ merkleRoot,
426
+ payloadHash,
427
+ powOk,
428
+ serialize,
429
+ serializeHeader,
430
+ sha256d,
431
+ sighash,
432
+ strip0x,
433
+ strippedTx,
434
+ taggedHash,
435
+ targetToBigInt,
436
+ targetToBits,
437
+ txid,
438
+ u32,
439
+ u64,
440
+ utf8ToBytes,
441
+ verifyContentBytes,
442
+ verifyMerkleProof,
443
+ workForBits
444
+ });
@@ -0,0 +1,133 @@
1
+ export { bytesToHex, concatBytes, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
2
+
3
+ declare const strip0x: (h: string) => string;
4
+ declare const hb: (h: string) => Uint8Array;
5
+ declare const hx: (b: Uint8Array) => string;
6
+ declare const sha256d: (b: Uint8Array) => Uint8Array;
7
+ declare function u32(n: number): Uint8Array;
8
+ declare function u64(n: number | bigint): Uint8Array;
9
+ declare const lenBytes: (b: Uint8Array) => Uint8Array;
10
+ declare function hbFixed(h: string, n: number): Uint8Array;
11
+
12
+ /** CHAIN_ID = "compute-substrate-mainnet". */
13
+ declare const CHAIN_ID = "compute-substrate-mainnet";
14
+ /** sha256(CHAIN_ID) — domain-separation tail in the sighash preimage. */
15
+ declare const CHAIN_ID_HASH: Uint8Array;
16
+ /** Genesis block header hash (the chain's anchor; pin this in the light client). */
17
+ declare const GENESIS_HASH = "0x00000052c2821f71b19c3d79dfabfb12d4076ba15d83b47d008e582aad6c0d52";
18
+ declare const GENESIS_TIME = 1777474800;
19
+ /** PoW / difficulty. */
20
+ declare const TARGET_BLOCK_SECS = 120;
21
+ declare const INITIAL_BITS = 503382015;
22
+ declare const POW_LIMIT_BITS = 503382015;
23
+ declare const LWMA_WINDOW = 45;
24
+ declare const LWMA_SOLVETIME_MAX_FACTOR = 12;
25
+ declare const MAX_FUTURE_DRIFT_SECS: number;
26
+ declare const MTP_WINDOW = 11;
27
+ declare const MIN_BLOCK_SPACING_SECS = 60;
28
+ /** App layer. */
29
+ declare const EPOCH_LEN = 30;
30
+ /** Supply / fees (base units; 1 CSD = COIN). */
31
+ declare const COIN = 100000000;
32
+ declare const INITIAL_REWARD: number;
33
+ declare const HALVING_INTERVAL = 1051200;
34
+ declare const MAX_HALVINGS = 64;
35
+ declare const MIN_FEE_PROPOSE = 25000000;
36
+ declare const MIN_FEE_ATTEST = 5000000;
37
+ /** Consensus tx limits. */
38
+ declare const MAX_TX_INPUTS = 512;
39
+ declare const MAX_TX_OUTPUTS = 512;
40
+ /** block_reward(height) — INITIAL_REWARD halved every HALVING_INTERVAL, 0 after MAX_HALVINGS. */
41
+ declare function blockReward(height: number): number;
42
+
43
+ type App = {
44
+ type: "None";
45
+ } | {
46
+ type: "Propose";
47
+ domain: string;
48
+ payloadHash: string;
49
+ uri: string;
50
+ expiresEpoch: number | bigint;
51
+ } | {
52
+ type: "Attest";
53
+ proposalId: string;
54
+ score: number;
55
+ confidence: number;
56
+ };
57
+ interface TxInput {
58
+ prevTxid: string;
59
+ vout: number;
60
+ scriptSig: string;
61
+ }
62
+ interface TxOutput {
63
+ value: number | bigint;
64
+ scriptPubkey: string;
65
+ }
66
+ interface Tx {
67
+ version: number;
68
+ inputs: TxInput[];
69
+ outputs: TxOutput[];
70
+ locktime: number;
71
+ app: App;
72
+ }
73
+ /** Strip script_sig from every non-coinbase input (the txid/sighash preimage). */
74
+ declare function strippedTx(tx: Tx): Tx;
75
+ /** Consensus bincode serialization of a transaction (as-is — does NOT strip). */
76
+ declare function serialize(tx: Tx): Uint8Array;
77
+ /** txid = sha256d(bincode(stripped_tx)). */
78
+ declare function txid(tx: Tx): string;
79
+ /** tagged_hash(tag, msg) = sha256( sha256(tag) ‖ sha256(tag) ‖ msg ) — the frozen CSD construction. */
80
+ declare function taggedHash(tag: string, msg: Uint8Array): Uint8Array;
81
+ /** sighash = sha256d( tagged_hash("CSD_SIG_V1", bincode(stripped_tx) ‖ CHAIN_ID_HASH) ). */
82
+ declare function sighash(tx: Tx): string;
83
+ /** Parse consensus bincode bytes back into a Tx (mirror of `serialize`). */
84
+ declare function deserialize(bytes: Uint8Array): Tx;
85
+
86
+ interface BlockHeader {
87
+ version: number;
88
+ prev: string;
89
+ merkle: string;
90
+ time: number | bigint;
91
+ bits: number;
92
+ nonce: number;
93
+ }
94
+ /** Exact 84-byte LE header serialization. */
95
+ declare function serializeHeader(h: BlockHeader): Uint8Array;
96
+ /** Header hash = sha256d(serialize_header). Returned 0x-hex (big-endian byte order as hashed). */
97
+ declare function headerHash(h: BlockHeader): string;
98
+ declare function headerHashBytes(h: BlockHeader): Uint8Array;
99
+ /**
100
+ * Decode Bitcoin-style compact `bits` to a 256-bit target as a 32-byte big-endian array.
101
+ * Mirrors pow.rs bits_to_target_bytes: returns all-zero (an impossible/invalid target) for
102
+ * the rejection cases (exp 0, mant 0, sign bit set, exp>32, overflow).
103
+ */
104
+ declare function bitsToTarget(bits: number): Uint8Array;
105
+ declare function targetToBigInt(target: Uint8Array): bigint;
106
+ /** A 256-bit target value → 32-byte big-endian array (right-aligned; matches biguint_to_target_bytes). */
107
+ declare function bigIntToTarget(x: bigint): Uint8Array;
108
+ /**
109
+ * Encode a 32-byte BE target back to canonical compact `bits` (port of pow.rs
110
+ * target_bytes_to_bits). Needed by the LWMA re-derivation in @inversealtruism/csd-light.
111
+ */
112
+ declare function targetToBits(target: Uint8Array): number;
113
+ /** PoW validity: header hash ≤ target(bits), both compared as 32-byte big-endian. */
114
+ declare function powOk(headerHashBE: Uint8Array, bits: number): boolean;
115
+ /** Chainwork contributed by a header at difficulty `bits`: 2^256 / (target + 1). */
116
+ declare function workForBits(bits: number): bigint;
117
+ /** Compute the tx-merkle root from the ordered list of txids (0x-hex). */
118
+ declare function merkleRoot(txidsHex: string[]): string;
119
+ /**
120
+ * Verify a merkle inclusion proof (Electrum format): fold `txid` up the branch and assert it
121
+ * equals `merkleRootHex`. `pos` is the tx index in the block; its bits select sibling side.
122
+ */
123
+ declare function verifyMerkleProof(txidHex: string, pos: number, branchHex: string[], merkleRootHex: string): boolean;
124
+ /** Build the merkle branch for tx at index `pos` from the full ordered txid list. */
125
+ declare function merkleBranch(txidsHex: string[], pos: number): string[];
126
+
127
+ declare function canonicalJson(v: unknown): string;
128
+ /** payload_hash for a content record (0x-hex sha256 of its canonical JSON). */
129
+ declare function payloadHash(content: unknown): string;
130
+ /** Verify served bytes match an on-chain payload_hash (self-certification). */
131
+ declare function verifyContentBytes(bytes: Uint8Array, payloadHashHex: string): boolean;
132
+
133
+ export { type App, type BlockHeader, CHAIN_ID, CHAIN_ID_HASH, COIN, EPOCH_LEN, GENESIS_HASH, GENESIS_TIME, HALVING_INTERVAL, INITIAL_BITS, INITIAL_REWARD, LWMA_SOLVETIME_MAX_FACTOR, LWMA_WINDOW, MAX_FUTURE_DRIFT_SECS, MAX_HALVINGS, MAX_TX_INPUTS, MAX_TX_OUTPUTS, MIN_BLOCK_SPACING_SECS, MIN_FEE_ATTEST, MIN_FEE_PROPOSE, MTP_WINDOW, POW_LIMIT_BITS, TARGET_BLOCK_SECS, type Tx, type TxInput, type TxOutput, bigIntToTarget, bitsToTarget, blockReward, canonicalJson, deserialize, hb, hbFixed, headerHash, headerHashBytes, hx, lenBytes, merkleBranch, merkleRoot, payloadHash, powOk, serialize, serializeHeader, sha256d, sighash, strip0x, strippedTx, taggedHash, targetToBigInt, targetToBits, txid, u32, u64, verifyContentBytes, verifyMerkleProof, workForBits };
@@ -0,0 +1,133 @@
1
+ export { bytesToHex, concatBytes, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
2
+
3
+ declare const strip0x: (h: string) => string;
4
+ declare const hb: (h: string) => Uint8Array;
5
+ declare const hx: (b: Uint8Array) => string;
6
+ declare const sha256d: (b: Uint8Array) => Uint8Array;
7
+ declare function u32(n: number): Uint8Array;
8
+ declare function u64(n: number | bigint): Uint8Array;
9
+ declare const lenBytes: (b: Uint8Array) => Uint8Array;
10
+ declare function hbFixed(h: string, n: number): Uint8Array;
11
+
12
+ /** CHAIN_ID = "compute-substrate-mainnet". */
13
+ declare const CHAIN_ID = "compute-substrate-mainnet";
14
+ /** sha256(CHAIN_ID) — domain-separation tail in the sighash preimage. */
15
+ declare const CHAIN_ID_HASH: Uint8Array;
16
+ /** Genesis block header hash (the chain's anchor; pin this in the light client). */
17
+ declare const GENESIS_HASH = "0x00000052c2821f71b19c3d79dfabfb12d4076ba15d83b47d008e582aad6c0d52";
18
+ declare const GENESIS_TIME = 1777474800;
19
+ /** PoW / difficulty. */
20
+ declare const TARGET_BLOCK_SECS = 120;
21
+ declare const INITIAL_BITS = 503382015;
22
+ declare const POW_LIMIT_BITS = 503382015;
23
+ declare const LWMA_WINDOW = 45;
24
+ declare const LWMA_SOLVETIME_MAX_FACTOR = 12;
25
+ declare const MAX_FUTURE_DRIFT_SECS: number;
26
+ declare const MTP_WINDOW = 11;
27
+ declare const MIN_BLOCK_SPACING_SECS = 60;
28
+ /** App layer. */
29
+ declare const EPOCH_LEN = 30;
30
+ /** Supply / fees (base units; 1 CSD = COIN). */
31
+ declare const COIN = 100000000;
32
+ declare const INITIAL_REWARD: number;
33
+ declare const HALVING_INTERVAL = 1051200;
34
+ declare const MAX_HALVINGS = 64;
35
+ declare const MIN_FEE_PROPOSE = 25000000;
36
+ declare const MIN_FEE_ATTEST = 5000000;
37
+ /** Consensus tx limits. */
38
+ declare const MAX_TX_INPUTS = 512;
39
+ declare const MAX_TX_OUTPUTS = 512;
40
+ /** block_reward(height) — INITIAL_REWARD halved every HALVING_INTERVAL, 0 after MAX_HALVINGS. */
41
+ declare function blockReward(height: number): number;
42
+
43
+ type App = {
44
+ type: "None";
45
+ } | {
46
+ type: "Propose";
47
+ domain: string;
48
+ payloadHash: string;
49
+ uri: string;
50
+ expiresEpoch: number | bigint;
51
+ } | {
52
+ type: "Attest";
53
+ proposalId: string;
54
+ score: number;
55
+ confidence: number;
56
+ };
57
+ interface TxInput {
58
+ prevTxid: string;
59
+ vout: number;
60
+ scriptSig: string;
61
+ }
62
+ interface TxOutput {
63
+ value: number | bigint;
64
+ scriptPubkey: string;
65
+ }
66
+ interface Tx {
67
+ version: number;
68
+ inputs: TxInput[];
69
+ outputs: TxOutput[];
70
+ locktime: number;
71
+ app: App;
72
+ }
73
+ /** Strip script_sig from every non-coinbase input (the txid/sighash preimage). */
74
+ declare function strippedTx(tx: Tx): Tx;
75
+ /** Consensus bincode serialization of a transaction (as-is — does NOT strip). */
76
+ declare function serialize(tx: Tx): Uint8Array;
77
+ /** txid = sha256d(bincode(stripped_tx)). */
78
+ declare function txid(tx: Tx): string;
79
+ /** tagged_hash(tag, msg) = sha256( sha256(tag) ‖ sha256(tag) ‖ msg ) — the frozen CSD construction. */
80
+ declare function taggedHash(tag: string, msg: Uint8Array): Uint8Array;
81
+ /** sighash = sha256d( tagged_hash("CSD_SIG_V1", bincode(stripped_tx) ‖ CHAIN_ID_HASH) ). */
82
+ declare function sighash(tx: Tx): string;
83
+ /** Parse consensus bincode bytes back into a Tx (mirror of `serialize`). */
84
+ declare function deserialize(bytes: Uint8Array): Tx;
85
+
86
+ interface BlockHeader {
87
+ version: number;
88
+ prev: string;
89
+ merkle: string;
90
+ time: number | bigint;
91
+ bits: number;
92
+ nonce: number;
93
+ }
94
+ /** Exact 84-byte LE header serialization. */
95
+ declare function serializeHeader(h: BlockHeader): Uint8Array;
96
+ /** Header hash = sha256d(serialize_header). Returned 0x-hex (big-endian byte order as hashed). */
97
+ declare function headerHash(h: BlockHeader): string;
98
+ declare function headerHashBytes(h: BlockHeader): Uint8Array;
99
+ /**
100
+ * Decode Bitcoin-style compact `bits` to a 256-bit target as a 32-byte big-endian array.
101
+ * Mirrors pow.rs bits_to_target_bytes: returns all-zero (an impossible/invalid target) for
102
+ * the rejection cases (exp 0, mant 0, sign bit set, exp>32, overflow).
103
+ */
104
+ declare function bitsToTarget(bits: number): Uint8Array;
105
+ declare function targetToBigInt(target: Uint8Array): bigint;
106
+ /** A 256-bit target value → 32-byte big-endian array (right-aligned; matches biguint_to_target_bytes). */
107
+ declare function bigIntToTarget(x: bigint): Uint8Array;
108
+ /**
109
+ * Encode a 32-byte BE target back to canonical compact `bits` (port of pow.rs
110
+ * target_bytes_to_bits). Needed by the LWMA re-derivation in @inversealtruism/csd-light.
111
+ */
112
+ declare function targetToBits(target: Uint8Array): number;
113
+ /** PoW validity: header hash ≤ target(bits), both compared as 32-byte big-endian. */
114
+ declare function powOk(headerHashBE: Uint8Array, bits: number): boolean;
115
+ /** Chainwork contributed by a header at difficulty `bits`: 2^256 / (target + 1). */
116
+ declare function workForBits(bits: number): bigint;
117
+ /** Compute the tx-merkle root from the ordered list of txids (0x-hex). */
118
+ declare function merkleRoot(txidsHex: string[]): string;
119
+ /**
120
+ * Verify a merkle inclusion proof (Electrum format): fold `txid` up the branch and assert it
121
+ * equals `merkleRootHex`. `pos` is the tx index in the block; its bits select sibling side.
122
+ */
123
+ declare function verifyMerkleProof(txidHex: string, pos: number, branchHex: string[], merkleRootHex: string): boolean;
124
+ /** Build the merkle branch for tx at index `pos` from the full ordered txid list. */
125
+ declare function merkleBranch(txidsHex: string[], pos: number): string[];
126
+
127
+ declare function canonicalJson(v: unknown): string;
128
+ /** payload_hash for a content record (0x-hex sha256 of its canonical JSON). */
129
+ declare function payloadHash(content: unknown): string;
130
+ /** Verify served bytes match an on-chain payload_hash (self-certification). */
131
+ declare function verifyContentBytes(bytes: Uint8Array, payloadHashHex: string): boolean;
132
+
133
+ export { type App, type BlockHeader, CHAIN_ID, CHAIN_ID_HASH, COIN, EPOCH_LEN, GENESIS_HASH, GENESIS_TIME, HALVING_INTERVAL, INITIAL_BITS, INITIAL_REWARD, LWMA_SOLVETIME_MAX_FACTOR, LWMA_WINDOW, MAX_FUTURE_DRIFT_SECS, MAX_HALVINGS, MAX_TX_INPUTS, MAX_TX_OUTPUTS, MIN_BLOCK_SPACING_SECS, MIN_FEE_ATTEST, MIN_FEE_PROPOSE, MTP_WINDOW, POW_LIMIT_BITS, TARGET_BLOCK_SECS, type Tx, type TxInput, type TxOutput, bigIntToTarget, bitsToTarget, blockReward, canonicalJson, deserialize, hb, hbFixed, headerHash, headerHashBytes, hx, lenBytes, merkleBranch, merkleRoot, payloadHash, powOk, serialize, serializeHeader, sha256d, sighash, strip0x, strippedTx, taggedHash, targetToBigInt, targetToBits, txid, u32, u64, verifyContentBytes, verifyMerkleProof, workForBits };
package/dist/index.js ADDED
@@ -0,0 +1,363 @@
1
+ // src/bytes.ts
2
+ import { bytesToHex, hexToBytes, concatBytes, utf8ToBytes } from "@noble/hashes/utils";
3
+ import { sha256 } from "@noble/hashes/sha256";
4
+ var strip0x = (h) => h.startsWith("0x") ? h.slice(2) : h;
5
+ var hb = (h) => hexToBytes(strip0x(h));
6
+ var hx = (b) => "0x" + bytesToHex(b);
7
+ var sha256d = (b) => sha256(sha256(b));
8
+ function u32(n) {
9
+ const b = new Uint8Array(4);
10
+ new DataView(b.buffer).setUint32(0, n >>> 0, true);
11
+ return b;
12
+ }
13
+ function u64(n) {
14
+ const b = new Uint8Array(8);
15
+ new DataView(b.buffer).setBigUint64(0, BigInt(n), true);
16
+ return b;
17
+ }
18
+ var lenBytes = (b) => concatBytes(u64(b.length), b);
19
+ function hbFixed(h, n) {
20
+ const b = hb(h);
21
+ if (b.length !== n) throw new Error(`expected a ${n}-byte (0x\u2026${n * 2}-hex) field, got ${b.length} bytes`);
22
+ return b;
23
+ }
24
+
25
+ // src/params.ts
26
+ import { hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
27
+ var CHAIN_ID = "compute-substrate-mainnet";
28
+ var CHAIN_ID_HASH = hexToBytes2("1b17c7b04d05394674ca2c8e24f7433e251a1973cac2000c7b60966546e0b875");
29
+ var GENESIS_HASH = "0x00000052c2821f71b19c3d79dfabfb12d4076ba15d83b47d008e582aad6c0d52";
30
+ var GENESIS_TIME = 1777474800;
31
+ var TARGET_BLOCK_SECS = 120;
32
+ var INITIAL_BITS = 503382015;
33
+ var POW_LIMIT_BITS = 503382015;
34
+ var LWMA_WINDOW = 45;
35
+ var LWMA_SOLVETIME_MAX_FACTOR = 12;
36
+ var MAX_FUTURE_DRIFT_SECS = 2 * 60 * 60;
37
+ var MTP_WINDOW = 11;
38
+ var MIN_BLOCK_SPACING_SECS = 60;
39
+ var EPOCH_LEN = 30;
40
+ var COIN = 1e8;
41
+ var INITIAL_REWARD = 50 * COIN;
42
+ var HALVING_INTERVAL = 1051200;
43
+ var MAX_HALVINGS = 64;
44
+ var MIN_FEE_PROPOSE = 25e6;
45
+ var MIN_FEE_ATTEST = 5e6;
46
+ var MAX_TX_INPUTS = 512;
47
+ var MAX_TX_OUTPUTS = 512;
48
+ function blockReward(height) {
49
+ const halvings = Math.floor(height / HALVING_INTERVAL);
50
+ if (halvings >= MAX_HALVINGS) return 0;
51
+ return Math.floor(INITIAL_REWARD / 2 ** halvings);
52
+ }
53
+
54
+ // src/tx.ts
55
+ import { sha256 as sha2562 } from "@noble/hashes/sha256";
56
+ import { concatBytes as concatBytes2, utf8ToBytes as utf8ToBytes2 } from "@noble/hashes/utils";
57
+ var COINBASE_TXID = "0x" + "00".repeat(32);
58
+ var COINBASE_VOUT = 4294967295;
59
+ var isCoinbaseInput = (i) => i.prevTxid === COINBASE_TXID && i.vout === COINBASE_VOUT;
60
+ function strippedTx(tx) {
61
+ return { ...tx, inputs: tx.inputs.map((i) => isCoinbaseInput(i) ? i : { ...i, scriptSig: "0x" }) };
62
+ }
63
+ function serializeApp(app) {
64
+ if (app.type === "None") return u32(0);
65
+ if (app.type === "Propose")
66
+ return concatBytes2(u32(1), lenBytes(utf8ToBytes2(app.domain)), hbFixed(app.payloadHash, 32), lenBytes(utf8ToBytes2(app.uri)), u64(app.expiresEpoch));
67
+ return concatBytes2(u32(2), hbFixed(app.proposalId, 32), u32(app.score), u32(app.confidence));
68
+ }
69
+ function serialize(tx) {
70
+ const parts = [u32(tx.version), u64(tx.inputs.length)];
71
+ for (const i of tx.inputs) parts.push(hbFixed(i.prevTxid, 32), u32(i.vout), lenBytes(hb(i.scriptSig)));
72
+ parts.push(u64(tx.outputs.length));
73
+ for (const o of tx.outputs) parts.push(u64(o.value), hbFixed(o.scriptPubkey, 20));
74
+ parts.push(u32(tx.locktime), serializeApp(tx.app));
75
+ return concatBytes2(...parts);
76
+ }
77
+ function txid(tx) {
78
+ return hx(sha256d(serialize(strippedTx(tx))));
79
+ }
80
+ function taggedHash(tag, msg) {
81
+ const t = sha2562(utf8ToBytes2(tag));
82
+ return sha2562(concatBytes2(t, t, msg));
83
+ }
84
+ function sighash(tx) {
85
+ return hx(sha256d(taggedHash("CSD_SIG_V1", concatBytes2(serialize(strippedTx(tx)), CHAIN_ID_HASH))));
86
+ }
87
+ var Reader = class {
88
+ constructor(b, dv = new DataView(b.buffer, b.byteOffset, b.byteLength)) {
89
+ this.b = b;
90
+ this.dv = dv;
91
+ }
92
+ b;
93
+ dv;
94
+ o = 0;
95
+ u32() {
96
+ const v = this.dv.getUint32(this.o, true);
97
+ this.o += 4;
98
+ return v;
99
+ }
100
+ u64() {
101
+ const v = this.dv.getBigUint64(this.o, true);
102
+ this.o += 8;
103
+ return v;
104
+ }
105
+ take(n) {
106
+ const v = this.b.subarray(this.o, this.o + n);
107
+ if (v.length !== n) throw new Error("unexpected end of bytes");
108
+ this.o += n;
109
+ return v;
110
+ }
111
+ vec() {
112
+ return this.take(Number(this.u64()));
113
+ }
114
+ fixedHex(n) {
115
+ return hx(this.take(n));
116
+ }
117
+ str() {
118
+ return new TextDecoder().decode(this.vec());
119
+ }
120
+ get offset() {
121
+ return this.o;
122
+ }
123
+ get length() {
124
+ return this.b.length;
125
+ }
126
+ };
127
+ function readApp(r) {
128
+ const tag = r.u32();
129
+ if (tag === 0) return { type: "None" };
130
+ if (tag === 1) return { type: "Propose", domain: r.str(), payloadHash: r.fixedHex(32), uri: r.str(), expiresEpoch: r.u64() };
131
+ if (tag === 2) return { type: "Attest", proposalId: r.fixedHex(32), score: r.u32(), confidence: r.u32() };
132
+ throw new Error(`unknown AppPayload variant ${tag}`);
133
+ }
134
+ function deserialize(bytes) {
135
+ const r = new Reader(bytes);
136
+ const version = r.u32();
137
+ const nIn = Number(r.u64());
138
+ const inputs = [];
139
+ for (let i = 0; i < nIn; i++) inputs.push({ prevTxid: r.fixedHex(32), vout: r.u32(), scriptSig: hx(r.vec()) });
140
+ const nOut = Number(r.u64());
141
+ const outputs = [];
142
+ for (let i = 0; i < nOut; i++) outputs.push({ value: r.u64(), scriptPubkey: r.fixedHex(20) });
143
+ const locktime = r.u32();
144
+ const app = readApp(r);
145
+ return { version, inputs, outputs, locktime, app };
146
+ }
147
+
148
+ // src/header.ts
149
+ function serializeHeader(h) {
150
+ const buf = new Uint8Array(84);
151
+ buf.set(u32(h.version), 0);
152
+ buf.set(hbFixed(h.prev, 32), 4);
153
+ buf.set(hbFixed(h.merkle, 32), 36);
154
+ buf.set(u64(h.time), 68);
155
+ buf.set(u32(h.bits), 76);
156
+ buf.set(u32(h.nonce), 80);
157
+ return buf;
158
+ }
159
+ function headerHash(h) {
160
+ return hx(sha256d(serializeHeader(h)));
161
+ }
162
+ function headerHashBytes(h) {
163
+ return sha256d(serializeHeader(h));
164
+ }
165
+ function bitsToTarget(bits) {
166
+ const exp = bits >>> 24 & 255;
167
+ const mant = bits & 16777215;
168
+ const out = new Uint8Array(32);
169
+ if (exp === 0 || mant === 0) return out;
170
+ if ((mant & 8388608) !== 0) return out;
171
+ if (exp > 32) return out;
172
+ let target;
173
+ if (exp <= 3) target = BigInt(mant) >> BigInt(8 * (3 - exp));
174
+ else target = BigInt(mant) << BigInt(8 * (exp - 3));
175
+ if (target === 0n) return out;
176
+ if (target >= 1n << 256n) return out;
177
+ for (let i = 31; i >= 0 && target > 0n; i--) {
178
+ out[i] = Number(target & 0xffn);
179
+ target >>= 8n;
180
+ }
181
+ return out;
182
+ }
183
+ function targetToBigInt(target) {
184
+ let v = 0n;
185
+ for (const byte of target) v = v << 8n | BigInt(byte);
186
+ return v;
187
+ }
188
+ function bigIntToTarget(x) {
189
+ const out = new Uint8Array(32);
190
+ for (let i = 31; i >= 0 && x > 0n; i--) {
191
+ out[i] = Number(x & 0xffn);
192
+ x >>= 8n;
193
+ }
194
+ return out;
195
+ }
196
+ function minBE(x) {
197
+ if (x === 0n) return [];
198
+ const b = [];
199
+ while (x > 0n) {
200
+ b.unshift(Number(x & 0xffn));
201
+ x >>= 8n;
202
+ }
203
+ return b;
204
+ }
205
+ function targetToBits(target) {
206
+ const x = targetToBigInt(target);
207
+ if (x === 0n) return 0;
208
+ const bytes = minBE(x);
209
+ let exp = bytes.length;
210
+ let mant;
211
+ if (exp <= 3) {
212
+ const shift = BigInt(8 * (3 - exp));
213
+ mant = Number(x << shift & 0xffffffffn) & 16777215;
214
+ } else {
215
+ mant = (bytes[0] << 16 | bytes[1] << 8 | bytes[2]) >>> 0;
216
+ }
217
+ if ((mant & 8388608) !== 0) {
218
+ mant >>= 8;
219
+ exp += 1;
220
+ }
221
+ mant &= 16777215;
222
+ return (exp << 24 | mant) >>> 0;
223
+ }
224
+ function powOk(headerHashBE, bits) {
225
+ const target = bitsToTarget(bits);
226
+ if (target.every((b) => b === 0)) return false;
227
+ return targetToBigInt(headerHashBE) <= targetToBigInt(target);
228
+ }
229
+ function workForBits(bits) {
230
+ const target = targetToBigInt(bitsToTarget(bits));
231
+ if (target === 0n) return 0n;
232
+ return (1n << 256n) / (target + 1n);
233
+ }
234
+ function merkleRoot(txidsHex) {
235
+ if (txidsHex.length === 0) return "0x" + "00".repeat(32);
236
+ let layer = txidsHex.map(hb);
237
+ while (layer.length > 1) {
238
+ const next = [];
239
+ for (let i = 0; i < layer.length; i += 2) {
240
+ const left = layer[i];
241
+ const right = i + 1 < layer.length ? layer[i + 1] : layer[i];
242
+ const buf = new Uint8Array(64);
243
+ buf.set(left, 0);
244
+ buf.set(right, 32);
245
+ next.push(sha256d(buf));
246
+ }
247
+ layer = next;
248
+ }
249
+ return hx(layer[0]);
250
+ }
251
+ function verifyMerkleProof(txidHex, pos, branchHex, merkleRootHex) {
252
+ let cur = hb(txidHex);
253
+ let idx = pos;
254
+ for (const sibHex of branchHex) {
255
+ const sib = hb(sibHex);
256
+ const buf = new Uint8Array(64);
257
+ if (idx & 1) {
258
+ buf.set(sib, 0);
259
+ buf.set(cur, 32);
260
+ } else {
261
+ buf.set(cur, 0);
262
+ buf.set(sib, 32);
263
+ }
264
+ cur = sha256d(buf);
265
+ idx >>= 1;
266
+ }
267
+ return hx(cur).toLowerCase() === merkleRootHex.toLowerCase();
268
+ }
269
+ function merkleBranch(txidsHex, pos) {
270
+ let layer = txidsHex.map(hb);
271
+ const branch = [];
272
+ let idx = pos;
273
+ while (layer.length > 1) {
274
+ const sibIdx = idx ^ 1;
275
+ const sib = sibIdx < layer.length ? layer[sibIdx] : layer[idx];
276
+ branch.push(hx(sib));
277
+ const next = [];
278
+ for (let i = 0; i < layer.length; i += 2) {
279
+ const left = layer[i];
280
+ const right = i + 1 < layer.length ? layer[i + 1] : layer[i];
281
+ const buf = new Uint8Array(64);
282
+ buf.set(left, 0);
283
+ buf.set(right, 32);
284
+ next.push(sha256d(buf));
285
+ }
286
+ layer = next;
287
+ idx >>= 1;
288
+ }
289
+ return branch;
290
+ }
291
+
292
+ // src/content.ts
293
+ import { sha256 as sha2563 } from "@noble/hashes/sha256";
294
+ import { bytesToHex as bytesToHex2, utf8ToBytes as utf8ToBytes3 } from "@noble/hashes/utils";
295
+ function canonicalJson(v) {
296
+ if (v === null || typeof v !== "object") return JSON.stringify(v);
297
+ if (Array.isArray(v)) return "[" + v.map(canonicalJson).join(",") + "]";
298
+ const o = v;
299
+ return "{" + Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + canonicalJson(o[k])).join(",") + "}";
300
+ }
301
+ function payloadHash(content) {
302
+ return "0x" + bytesToHex2(sha2563(utf8ToBytes3(canonicalJson(content))));
303
+ }
304
+ function verifyContentBytes(bytes, payloadHashHex) {
305
+ return "0x" + bytesToHex2(sha2563(bytes)) === payloadHashHex.toLowerCase();
306
+ }
307
+ export {
308
+ CHAIN_ID,
309
+ CHAIN_ID_HASH,
310
+ COIN,
311
+ EPOCH_LEN,
312
+ GENESIS_HASH,
313
+ GENESIS_TIME,
314
+ HALVING_INTERVAL,
315
+ INITIAL_BITS,
316
+ INITIAL_REWARD,
317
+ LWMA_SOLVETIME_MAX_FACTOR,
318
+ LWMA_WINDOW,
319
+ MAX_FUTURE_DRIFT_SECS,
320
+ MAX_HALVINGS,
321
+ MAX_TX_INPUTS,
322
+ MAX_TX_OUTPUTS,
323
+ MIN_BLOCK_SPACING_SECS,
324
+ MIN_FEE_ATTEST,
325
+ MIN_FEE_PROPOSE,
326
+ MTP_WINDOW,
327
+ POW_LIMIT_BITS,
328
+ TARGET_BLOCK_SECS,
329
+ bigIntToTarget,
330
+ bitsToTarget,
331
+ blockReward,
332
+ bytesToHex,
333
+ canonicalJson,
334
+ concatBytes,
335
+ deserialize,
336
+ hb,
337
+ hbFixed,
338
+ headerHash,
339
+ headerHashBytes,
340
+ hexToBytes,
341
+ hx,
342
+ lenBytes,
343
+ merkleBranch,
344
+ merkleRoot,
345
+ payloadHash,
346
+ powOk,
347
+ serialize,
348
+ serializeHeader,
349
+ sha256d,
350
+ sighash,
351
+ strip0x,
352
+ strippedTx,
353
+ taggedHash,
354
+ targetToBigInt,
355
+ targetToBits,
356
+ txid,
357
+ u32,
358
+ u64,
359
+ utf8ToBytes,
360
+ verifyContentBytes,
361
+ verifyMerkleProof,
362
+ workForBits
363
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@inversealtruism/csd-codec",
3
+ "version": "0.1.0",
4
+ "description": "Compute Substrate consensus codec — bincode (fixint-LE) serialize/deserialize, txid, sighash (CSD_SIG_V1), header serialize/hash, compact-bits→target, merkle. Byte-identical to the Rust node (golden-vector-gated).",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "LICENSE"
19
+ ],
20
+ "dependencies": {
21
+ "@noble/hashes": "1.8.0"
22
+ },
23
+ "devDependencies": {
24
+ "@inversealtruism/csd-vectors": "0.1.0"
25
+ },
26
+ "license": "MIT",
27
+ "sideEffects": false,
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/InverseAltruism/csd-sdk.git",
34
+ "directory": "packages/codec"
35
+ },
36
+ "homepage": "https://cairn-substrate.com",
37
+ "scripts": {
38
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
39
+ "test": "tsx test/codec.test.ts"
40
+ }
41
+ }