@otskit/client 0.3.0 → 0.5.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/README.md +1 -1
- package/dist/index.cjs +130 -1515
- package/dist/index.d.cts +68 -47
- package/dist/index.d.ts +68 -47
- package/dist/index.js +133 -1505
- package/package.json +7 -5
package/dist/index.cjs
CHANGED
|
@@ -29,7 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
DEFAULT_CALENDARS: () => DEFAULT_CALENDARS,
|
|
30
30
|
DEFAULT_CALENDAR_WHITELIST: () => DEFAULT_CALENDAR_WHITELIST,
|
|
31
31
|
DEFAULT_RESILIENCE: () => DEFAULT_RESILIENCE,
|
|
32
|
-
DetachedTimestampFile: () => DetachedTimestampFile,
|
|
32
|
+
DetachedTimestampFile: () => import_core5.DetachedTimestampFile,
|
|
33
33
|
EsploraClient: () => EsploraClient,
|
|
34
34
|
EsploraResponseError: () => EsploraResponseError,
|
|
35
35
|
MAX_CALENDAR_RESPONSE_SIZE: () => MAX_CALENDAR_RESPONSE_SIZE,
|
|
@@ -41,7 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
ResilientNetworkLayer: () => ResilientNetworkLayer,
|
|
42
42
|
SizeLimitExceededError: () => SizeLimitExceededError,
|
|
43
43
|
StampError: () => StampError,
|
|
44
|
-
Timestamp: () => Timestamp,
|
|
44
|
+
Timestamp: () => import_core5.Timestamp,
|
|
45
45
|
UpgradeError: () => UpgradeError,
|
|
46
46
|
UrlWhitelist: () => UrlWhitelist,
|
|
47
47
|
ValidationError: () => ValidationError,
|
|
@@ -49,19 +49,15 @@ __export(index_exports, {
|
|
|
49
49
|
hashBuffer: () => hashBuffer,
|
|
50
50
|
hashFile: () => hashFile,
|
|
51
51
|
isVerified: () => isVerified,
|
|
52
|
-
verifyAgainstBlockheader: () => verifyAgainstBlockheader,
|
|
52
|
+
verifyAgainstBlockheader: () => import_core6.verifyAgainstBlockheader,
|
|
53
53
|
verifyTimestampAttestation: () => verifyTimestampAttestation
|
|
54
54
|
});
|
|
55
55
|
module.exports = __toCommonJS(index_exports);
|
|
56
56
|
|
|
57
57
|
// src/types.ts
|
|
58
|
+
var import_core = require("@otskit/core");
|
|
58
59
|
var isVerified = (r) => r.status === "verified";
|
|
59
|
-
var DEFAULT_CALENDARS = [
|
|
60
|
-
"https://alice.btc.calendar.opentimestamps.org",
|
|
61
|
-
"https://bob.btc.calendar.opentimestamps.org",
|
|
62
|
-
"https://finney.calendar.eternitywall.com",
|
|
63
|
-
"https://btc.calendar.catallaxy.com"
|
|
64
|
-
];
|
|
60
|
+
var DEFAULT_CALENDARS = [...import_core.DEFAULT_CALENDAR_URLS];
|
|
65
61
|
var DEFAULT_RESILIENCE = {
|
|
66
62
|
totalTimeoutMs: 3e4,
|
|
67
63
|
connectTimeoutMs: 5e3,
|
|
@@ -89,7 +85,7 @@ var OpenTimestampsClientError = class extends Error {
|
|
|
89
85
|
constructor(message, options) {
|
|
90
86
|
super(message);
|
|
91
87
|
this.name = this.constructor.name;
|
|
92
|
-
this.cause = options
|
|
88
|
+
if (options?.cause !== void 0) this.cause = options.cause;
|
|
93
89
|
Error.captureStackTrace?.(this, this.constructor);
|
|
94
90
|
}
|
|
95
91
|
};
|
|
@@ -107,11 +103,11 @@ var StampError = class extends OpenTimestampsClientError {
|
|
|
107
103
|
var UpgradeError = class extends OpenTimestampsClientError {
|
|
108
104
|
};
|
|
109
105
|
var NetworkError = class extends OpenTimestampsClientError {
|
|
110
|
-
/** HTTP status code
|
|
106
|
+
/** HTTP status code when the failure originates from an HTTP response. */
|
|
111
107
|
status;
|
|
112
108
|
constructor(message, options) {
|
|
113
109
|
super(message, options);
|
|
114
|
-
this.status = options
|
|
110
|
+
if (options?.status !== void 0) this.status = options.status;
|
|
115
111
|
}
|
|
116
112
|
};
|
|
117
113
|
var CircuitBreakerError = class extends NetworkError {
|
|
@@ -134,7 +130,7 @@ var SizeLimitExceededError = class extends NetworkError {
|
|
|
134
130
|
options
|
|
135
131
|
);
|
|
136
132
|
this.maxBytes = maxBytes;
|
|
137
|
-
this.actualBytes = actualBytes;
|
|
133
|
+
if (actualBytes !== void 0) this.actualBytes = actualBytes;
|
|
138
134
|
}
|
|
139
135
|
};
|
|
140
136
|
|
|
@@ -186,7 +182,10 @@ var CircuitBreaker = class {
|
|
|
186
182
|
this.onSuccess(key, circuit);
|
|
187
183
|
return result;
|
|
188
184
|
} catch (error) {
|
|
189
|
-
|
|
185
|
+
const is4xx = error instanceof Error && error.retryable === false;
|
|
186
|
+
if (!is4xx) {
|
|
187
|
+
this.onFailure(key, circuit);
|
|
188
|
+
}
|
|
190
189
|
throw error;
|
|
191
190
|
}
|
|
192
191
|
}
|
|
@@ -283,11 +282,15 @@ function sleep(ms, signal) {
|
|
|
283
282
|
reject(new Error("Aborted"));
|
|
284
283
|
return;
|
|
285
284
|
}
|
|
286
|
-
const
|
|
287
|
-
signal?.addEventListener("abort", () => {
|
|
285
|
+
const onAbort = () => {
|
|
288
286
|
clearTimeout(timeout);
|
|
289
287
|
reject(new Error("Aborted"));
|
|
290
|
-
}
|
|
288
|
+
};
|
|
289
|
+
const timeout = setTimeout(() => {
|
|
290
|
+
signal?.removeEventListener("abort", onAbort);
|
|
291
|
+
resolve();
|
|
292
|
+
}, ms);
|
|
293
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
291
294
|
});
|
|
292
295
|
}
|
|
293
296
|
async function withRetry(fn, options, logger, signal) {
|
|
@@ -370,8 +373,9 @@ async function executeRequest(request, maxBytes) {
|
|
|
370
373
|
const response = await globalThis.fetch(request.url, {
|
|
371
374
|
method: request.method,
|
|
372
375
|
headers: { "Content-Type": "application/octet-stream", ...request.headers },
|
|
373
|
-
body: request.body,
|
|
374
|
-
signal: request.signal
|
|
376
|
+
...request.body !== void 0 ? { body: request.body } : {},
|
|
377
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
378
|
+
redirect: "error"
|
|
375
379
|
});
|
|
376
380
|
const data = await readResponseBody(response, maxBytes);
|
|
377
381
|
return { ok: response.ok, status: response.status, statusText: response.statusText, data };
|
|
@@ -489,1453 +493,11 @@ var ResilientNetworkLayer = class {
|
|
|
489
493
|
}
|
|
490
494
|
};
|
|
491
495
|
|
|
492
|
-
//
|
|
493
|
-
var
|
|
494
|
-
constructor(message) {
|
|
495
|
-
super(message);
|
|
496
|
-
this.name = new.target.name;
|
|
497
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
var BadMagicError = class extends DeserializationError {
|
|
501
|
-
};
|
|
502
|
-
var TruncatedStreamError = class extends DeserializationError {
|
|
503
|
-
};
|
|
504
|
-
var OversizedDataError = class extends DeserializationError {
|
|
505
|
-
};
|
|
506
|
-
var VaruintOverflowError = class extends DeserializationError {
|
|
507
|
-
};
|
|
508
|
-
var TrailingGarbageError = class extends DeserializationError {
|
|
509
|
-
};
|
|
510
|
-
var UnknownOperationError = class extends DeserializationError {
|
|
511
|
-
};
|
|
512
|
-
var OpExecutionError = class extends Error {
|
|
513
|
-
constructor(message) {
|
|
514
|
-
super(message);
|
|
515
|
-
this.name = new.target.name;
|
|
516
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
var MessageTooLongError = class extends OpExecutionError {
|
|
520
|
-
};
|
|
521
|
-
var ResultTooLongError = class extends OpExecutionError {
|
|
522
|
-
};
|
|
523
|
-
var InvalidUriError = class extends DeserializationError {
|
|
524
|
-
};
|
|
525
|
-
var VerificationError = class extends Error {
|
|
526
|
-
constructor(message) {
|
|
527
|
-
super(message);
|
|
528
|
-
this.name = new.target.name;
|
|
529
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
var EmptyTimestampError = class extends Error {
|
|
533
|
-
constructor(message) {
|
|
534
|
-
super(message);
|
|
535
|
-
this.name = new.target.name;
|
|
536
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
var MergeError = class extends Error {
|
|
540
|
-
constructor(message) {
|
|
541
|
-
super(message);
|
|
542
|
-
this.name = new.target.name;
|
|
543
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
var EmptyMerkleTreeError = class extends Error {
|
|
547
|
-
constructor(message) {
|
|
548
|
-
super(message);
|
|
549
|
-
this.name = new.target.name;
|
|
550
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
var UnsupportedVersionError = class extends DeserializationError {
|
|
554
|
-
};
|
|
555
|
-
var HEX_RE = /^[0-9a-fA-F]*$/;
|
|
556
|
-
function hexToBytes(hex) {
|
|
557
|
-
if (hex.length % 2 !== 0) {
|
|
558
|
-
throw new Error(`hex string must have even length; got ${hex.length}`);
|
|
559
|
-
}
|
|
560
|
-
if (!HEX_RE.test(hex)) {
|
|
561
|
-
throw new Error("hex string contains non-hex characters");
|
|
562
|
-
}
|
|
563
|
-
const out = new Uint8Array(hex.length / 2);
|
|
564
|
-
for (let i = 0; i < out.length; i++) {
|
|
565
|
-
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
566
|
-
}
|
|
567
|
-
return out;
|
|
568
|
-
}
|
|
569
|
-
var HEX_TABLE = Array.from({ length: 256 }, (_, b) => b.toString(16).padStart(2, "0"));
|
|
570
|
-
function bytesToHex(bytes) {
|
|
571
|
-
let s = "";
|
|
572
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
573
|
-
s += HEX_TABLE[bytes[i]];
|
|
574
|
-
}
|
|
575
|
-
return s;
|
|
576
|
-
}
|
|
577
|
-
var encoder = new TextEncoder();
|
|
578
|
-
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
579
|
-
function textToBytes(text) {
|
|
580
|
-
return encoder.encode(text);
|
|
581
|
-
}
|
|
582
|
-
function bytesEqual(a, b) {
|
|
583
|
-
if (a.length !== b.length) return false;
|
|
584
|
-
for (let i = 0; i < a.length; i++) {
|
|
585
|
-
if (a[i] !== b[i]) return false;
|
|
586
|
-
}
|
|
587
|
-
return true;
|
|
588
|
-
}
|
|
589
|
-
function compareBytes(a, b) {
|
|
590
|
-
const min = Math.min(a.length, b.length);
|
|
591
|
-
for (let i = 0; i < min; i++) {
|
|
592
|
-
const d = a[i] - b[i];
|
|
593
|
-
if (d !== 0) return d;
|
|
594
|
-
}
|
|
595
|
-
return a.length - b.length;
|
|
596
|
-
}
|
|
597
|
-
var StreamDeserializationContext = class {
|
|
598
|
-
#buffer;
|
|
599
|
-
#counter = 0;
|
|
600
|
-
constructor(stream) {
|
|
601
|
-
if (!(stream instanceof Uint8Array)) {
|
|
602
|
-
throw new TypeError("StreamDeserializationContext expects a Uint8Array");
|
|
603
|
-
}
|
|
604
|
-
this.#buffer = stream;
|
|
605
|
-
}
|
|
606
|
-
get counter() {
|
|
607
|
-
return this.#counter;
|
|
608
|
-
}
|
|
609
|
-
/** Lee `length` bytes. Lanza si el stream no tiene suficientes. */
|
|
610
|
-
read(length) {
|
|
611
|
-
if (length < 0) throw new RangeError("read length must be >= 0");
|
|
612
|
-
if (this.#counter + length > this.#buffer.length) {
|
|
613
|
-
throw new TruncatedStreamError(
|
|
614
|
-
`attempted to read ${length} bytes at offset ${this.#counter}, only ${this.#buffer.length - this.#counter} available`
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
const slice = this.#buffer.subarray(this.#counter, this.#counter + length);
|
|
618
|
-
this.#counter += length;
|
|
619
|
-
return slice;
|
|
620
|
-
}
|
|
621
|
-
/** Lee un único byte. */
|
|
622
|
-
readByte() {
|
|
623
|
-
return this.read(1)[0];
|
|
624
|
-
}
|
|
625
|
-
/** Varuint LEB128: 7 bits por byte, bit 7 = continuación. */
|
|
626
|
-
readVaruint() {
|
|
627
|
-
let value = 0;
|
|
628
|
-
let shift = 0;
|
|
629
|
-
let byte;
|
|
630
|
-
do {
|
|
631
|
-
if (shift > 56) {
|
|
632
|
-
throw new VaruintOverflowError("varuint exceeds Number.MAX_SAFE_INTEGER");
|
|
633
|
-
}
|
|
634
|
-
byte = this.readByte();
|
|
635
|
-
value += (byte & 127) * 2 ** shift;
|
|
636
|
-
if (!Number.isSafeInteger(value)) {
|
|
637
|
-
throw new VaruintOverflowError("varuint exceeds Number.MAX_SAFE_INTEGER");
|
|
638
|
-
}
|
|
639
|
-
shift += 7;
|
|
640
|
-
} while (byte & 128);
|
|
641
|
-
return value;
|
|
642
|
-
}
|
|
643
|
-
/** Lee un bloque varbytes. `maxLen` es obligatorio (defensa DoS). */
|
|
644
|
-
readVarbytes(maxLen, minLen = 0) {
|
|
645
|
-
const length = this.readVaruint();
|
|
646
|
-
if (length > maxLen) {
|
|
647
|
-
throw new OversizedDataError(`varbytes length ${length} exceeds maxLen ${maxLen}`);
|
|
648
|
-
}
|
|
649
|
-
if (length < minLen) {
|
|
650
|
-
throw new OversizedDataError(`varbytes length ${length} below minLen ${minLen}`);
|
|
651
|
-
}
|
|
652
|
-
return this.read(length);
|
|
653
|
-
}
|
|
654
|
-
/** Verifica el número mágico de cabecera. */
|
|
655
|
-
assertMagic(expectedMagic) {
|
|
656
|
-
const actual = this.read(expectedMagic.length);
|
|
657
|
-
if (!bytesEqual(expectedMagic, actual)) {
|
|
658
|
-
throw new BadMagicError("header magic mismatch");
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
/** Exige que no queden bytes sin consumir. */
|
|
662
|
-
assertEof() {
|
|
663
|
-
if (this.#counter < this.#buffer.length) {
|
|
664
|
-
throw new TrailingGarbageError("trailing garbage after end of deserialized data");
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
var StreamSerializationContext = class {
|
|
669
|
-
#buffer = new Uint8Array(4096);
|
|
670
|
-
#length = 0;
|
|
671
|
-
get length() {
|
|
672
|
-
return this.#length;
|
|
673
|
-
}
|
|
674
|
-
getOutput() {
|
|
675
|
-
return this.#buffer.slice(0, this.#length);
|
|
676
|
-
}
|
|
677
|
-
writeByte(value) {
|
|
678
|
-
if (!Number.isInteger(value) || value < 0 || value > 255) {
|
|
679
|
-
throw new RangeError(`writeByte expects a byte 0..255; got ${value}`);
|
|
680
|
-
}
|
|
681
|
-
if (this.#length >= this.#buffer.length) {
|
|
682
|
-
const grown = new Uint8Array(this.#buffer.length * 2);
|
|
683
|
-
grown.set(this.#buffer, 0);
|
|
684
|
-
this.#buffer = grown;
|
|
685
|
-
}
|
|
686
|
-
this.#buffer[this.#length] = value;
|
|
687
|
-
this.#length += 1;
|
|
688
|
-
}
|
|
689
|
-
writeBytes(value) {
|
|
690
|
-
for (let i = 0; i < value.length; i++) {
|
|
691
|
-
this.writeByte(value[i]);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
/** Codifica un varuint LEB128. */
|
|
695
|
-
writeVaruint(value) {
|
|
696
|
-
if (!Number.isSafeInteger(value) || value < 0) {
|
|
697
|
-
throw new RangeError(`writeVaruint expects a safe non-negative integer; got ${value}`);
|
|
698
|
-
}
|
|
699
|
-
do {
|
|
700
|
-
let byte = value % 128;
|
|
701
|
-
value = Math.floor(value / 128);
|
|
702
|
-
if (value > 0) byte |= 128;
|
|
703
|
-
this.writeByte(byte);
|
|
704
|
-
} while (value > 0);
|
|
705
|
-
}
|
|
706
|
-
writeVarbytes(value) {
|
|
707
|
-
this.writeVaruint(value.length);
|
|
708
|
-
this.writeBytes(value);
|
|
709
|
-
}
|
|
710
|
-
};
|
|
711
|
-
var rotl = (x, n) => x << n | x >>> 32 - n;
|
|
712
|
-
function sha1(msg) {
|
|
713
|
-
let h0 = 1732584193, h1 = 4023233417, h2 = 2562383102, h3 = 271733878, h4 = 3285377520;
|
|
714
|
-
const bitLen = msg.length * 8;
|
|
715
|
-
const withOne = msg.length + 1;
|
|
716
|
-
const padded = new Uint8Array(Math.ceil((withOne + 8) / 64) * 64);
|
|
717
|
-
padded.set(msg, 0);
|
|
718
|
-
padded[msg.length] = 128;
|
|
719
|
-
const dv = new DataView(padded.buffer);
|
|
720
|
-
dv.setUint32(padded.length - 4, bitLen >>> 0, false);
|
|
721
|
-
dv.setUint32(padded.length - 8, Math.floor(bitLen / 2 ** 32), false);
|
|
722
|
-
const w = new Uint32Array(80);
|
|
723
|
-
for (let off = 0; off < padded.length; off += 64) {
|
|
724
|
-
for (let i = 0; i < 16; i++) w[i] = dv.getUint32(off + i * 4, false);
|
|
725
|
-
for (let i = 16; i < 80; i++) w[i] = rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
|
|
726
|
-
let a = h0, b = h1, c = h2, d = h3, e = h4;
|
|
727
|
-
for (let i = 0; i < 80; i++) {
|
|
728
|
-
let f2, k;
|
|
729
|
-
if (i < 20) {
|
|
730
|
-
f2 = b & c | ~b & d;
|
|
731
|
-
k = 1518500249;
|
|
732
|
-
} else if (i < 40) {
|
|
733
|
-
f2 = b ^ c ^ d;
|
|
734
|
-
k = 1859775393;
|
|
735
|
-
} else if (i < 60) {
|
|
736
|
-
f2 = b & c | b & d | c & d;
|
|
737
|
-
k = 2400959708;
|
|
738
|
-
} else {
|
|
739
|
-
f2 = b ^ c ^ d;
|
|
740
|
-
k = 3395469782;
|
|
741
|
-
}
|
|
742
|
-
const t = rotl(a, 5) + f2 + e + k + w[i] | 0;
|
|
743
|
-
e = d;
|
|
744
|
-
d = c;
|
|
745
|
-
c = rotl(b, 30);
|
|
746
|
-
b = a;
|
|
747
|
-
a = t;
|
|
748
|
-
}
|
|
749
|
-
h0 = h0 + a | 0;
|
|
750
|
-
h1 = h1 + b | 0;
|
|
751
|
-
h2 = h2 + c | 0;
|
|
752
|
-
h3 = h3 + d | 0;
|
|
753
|
-
h4 = h4 + e | 0;
|
|
754
|
-
}
|
|
755
|
-
const out = new Uint8Array(20);
|
|
756
|
-
const odv = new DataView(out.buffer);
|
|
757
|
-
odv.setUint32(0, h0, false);
|
|
758
|
-
odv.setUint32(4, h1, false);
|
|
759
|
-
odv.setUint32(8, h2, false);
|
|
760
|
-
odv.setUint32(12, h3, false);
|
|
761
|
-
odv.setUint32(16, h4, false);
|
|
762
|
-
return out;
|
|
763
|
-
}
|
|
764
|
-
var K = new Uint32Array([
|
|
765
|
-
1116352408,
|
|
766
|
-
1899447441,
|
|
767
|
-
3049323471,
|
|
768
|
-
3921009573,
|
|
769
|
-
961987163,
|
|
770
|
-
1508970993,
|
|
771
|
-
2453635748,
|
|
772
|
-
2870763221,
|
|
773
|
-
3624381080,
|
|
774
|
-
310598401,
|
|
775
|
-
607225278,
|
|
776
|
-
1426881987,
|
|
777
|
-
1925078388,
|
|
778
|
-
2162078206,
|
|
779
|
-
2614888103,
|
|
780
|
-
3248222580,
|
|
781
|
-
3835390401,
|
|
782
|
-
4022224774,
|
|
783
|
-
264347078,
|
|
784
|
-
604807628,
|
|
785
|
-
770255983,
|
|
786
|
-
1249150122,
|
|
787
|
-
1555081692,
|
|
788
|
-
1996064986,
|
|
789
|
-
2554220882,
|
|
790
|
-
2821834349,
|
|
791
|
-
2952996808,
|
|
792
|
-
3210313671,
|
|
793
|
-
3336571891,
|
|
794
|
-
3584528711,
|
|
795
|
-
113926993,
|
|
796
|
-
338241895,
|
|
797
|
-
666307205,
|
|
798
|
-
773529912,
|
|
799
|
-
1294757372,
|
|
800
|
-
1396182291,
|
|
801
|
-
1695183700,
|
|
802
|
-
1986661051,
|
|
803
|
-
2177026350,
|
|
804
|
-
2456956037,
|
|
805
|
-
2730485921,
|
|
806
|
-
2820302411,
|
|
807
|
-
3259730800,
|
|
808
|
-
3345764771,
|
|
809
|
-
3516065817,
|
|
810
|
-
3600352804,
|
|
811
|
-
4094571909,
|
|
812
|
-
275423344,
|
|
813
|
-
430227734,
|
|
814
|
-
506948616,
|
|
815
|
-
659060556,
|
|
816
|
-
883997877,
|
|
817
|
-
958139571,
|
|
818
|
-
1322822218,
|
|
819
|
-
1537002063,
|
|
820
|
-
1747873779,
|
|
821
|
-
1955562222,
|
|
822
|
-
2024104815,
|
|
823
|
-
2227730452,
|
|
824
|
-
2361852424,
|
|
825
|
-
2428436474,
|
|
826
|
-
2756734187,
|
|
827
|
-
3204031479,
|
|
828
|
-
3329325298
|
|
829
|
-
]);
|
|
830
|
-
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
831
|
-
function sha256(msg) {
|
|
832
|
-
const h = new Uint32Array([
|
|
833
|
-
1779033703,
|
|
834
|
-
3144134277,
|
|
835
|
-
1013904242,
|
|
836
|
-
2773480762,
|
|
837
|
-
1359893119,
|
|
838
|
-
2600822924,
|
|
839
|
-
528734635,
|
|
840
|
-
1541459225
|
|
841
|
-
]);
|
|
842
|
-
const bitLen = msg.length * 8;
|
|
843
|
-
const withOne = msg.length + 1;
|
|
844
|
-
const padded = new Uint8Array(Math.ceil((withOne + 8) / 64) * 64);
|
|
845
|
-
padded.set(msg, 0);
|
|
846
|
-
padded[msg.length] = 128;
|
|
847
|
-
const dv = new DataView(padded.buffer);
|
|
848
|
-
dv.setUint32(padded.length - 4, bitLen >>> 0, false);
|
|
849
|
-
dv.setUint32(padded.length - 8, Math.floor(bitLen / 2 ** 32), false);
|
|
850
|
-
const w = new Uint32Array(64);
|
|
851
|
-
for (let off = 0; off < padded.length; off += 64) {
|
|
852
|
-
for (let i = 0; i < 16; i++) w[i] = dv.getUint32(off + i * 4, false);
|
|
853
|
-
for (let i = 16; i < 64; i++) {
|
|
854
|
-
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
855
|
-
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
856
|
-
w[i] = w[i - 16] + s0 + w[i - 7] + s1 | 0;
|
|
857
|
-
}
|
|
858
|
-
let [a, b, c, d, e, f2, g, hh] = h;
|
|
859
|
-
for (let i = 0; i < 64; i++) {
|
|
860
|
-
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
861
|
-
const ch = e & f2 ^ ~e & g;
|
|
862
|
-
const t1 = hh + S1 + ch + K[i] + w[i] | 0;
|
|
863
|
-
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
864
|
-
const maj = a & b ^ a & c ^ b & c;
|
|
865
|
-
const t2 = S0 + maj | 0;
|
|
866
|
-
hh = g;
|
|
867
|
-
g = f2;
|
|
868
|
-
f2 = e;
|
|
869
|
-
e = d + t1 | 0;
|
|
870
|
-
d = c;
|
|
871
|
-
c = b;
|
|
872
|
-
b = a;
|
|
873
|
-
a = t1 + t2 | 0;
|
|
874
|
-
}
|
|
875
|
-
h[0] = h[0] + a | 0;
|
|
876
|
-
h[1] = h[1] + b | 0;
|
|
877
|
-
h[2] = h[2] + c | 0;
|
|
878
|
-
h[3] = h[3] + d | 0;
|
|
879
|
-
h[4] = h[4] + e | 0;
|
|
880
|
-
h[5] = h[5] + f2 | 0;
|
|
881
|
-
h[6] = h[6] + g | 0;
|
|
882
|
-
h[7] = h[7] + hh | 0;
|
|
883
|
-
}
|
|
884
|
-
const out = new Uint8Array(32);
|
|
885
|
-
const odv = new DataView(out.buffer);
|
|
886
|
-
for (let i = 0; i < 8; i++) odv.setUint32(i * 4, h[i], false);
|
|
887
|
-
return out;
|
|
888
|
-
}
|
|
889
|
-
var rol = (x, n) => x << n | x >>> 32 - n;
|
|
890
|
-
var ZL = [
|
|
891
|
-
0,
|
|
892
|
-
1,
|
|
893
|
-
2,
|
|
894
|
-
3,
|
|
895
|
-
4,
|
|
896
|
-
5,
|
|
897
|
-
6,
|
|
898
|
-
7,
|
|
899
|
-
8,
|
|
900
|
-
9,
|
|
901
|
-
10,
|
|
902
|
-
11,
|
|
903
|
-
12,
|
|
904
|
-
13,
|
|
905
|
-
14,
|
|
906
|
-
15,
|
|
907
|
-
7,
|
|
908
|
-
4,
|
|
909
|
-
13,
|
|
910
|
-
1,
|
|
911
|
-
10,
|
|
912
|
-
6,
|
|
913
|
-
15,
|
|
914
|
-
3,
|
|
915
|
-
12,
|
|
916
|
-
0,
|
|
917
|
-
9,
|
|
918
|
-
5,
|
|
919
|
-
2,
|
|
920
|
-
14,
|
|
921
|
-
11,
|
|
922
|
-
8,
|
|
923
|
-
3,
|
|
924
|
-
10,
|
|
925
|
-
14,
|
|
926
|
-
4,
|
|
927
|
-
9,
|
|
928
|
-
15,
|
|
929
|
-
8,
|
|
930
|
-
1,
|
|
931
|
-
2,
|
|
932
|
-
7,
|
|
933
|
-
0,
|
|
934
|
-
6,
|
|
935
|
-
13,
|
|
936
|
-
11,
|
|
937
|
-
5,
|
|
938
|
-
12,
|
|
939
|
-
1,
|
|
940
|
-
9,
|
|
941
|
-
11,
|
|
942
|
-
10,
|
|
943
|
-
0,
|
|
944
|
-
8,
|
|
945
|
-
12,
|
|
946
|
-
4,
|
|
947
|
-
13,
|
|
948
|
-
3,
|
|
949
|
-
7,
|
|
950
|
-
15,
|
|
951
|
-
14,
|
|
952
|
-
5,
|
|
953
|
-
6,
|
|
954
|
-
2,
|
|
955
|
-
4,
|
|
956
|
-
0,
|
|
957
|
-
5,
|
|
958
|
-
9,
|
|
959
|
-
7,
|
|
960
|
-
12,
|
|
961
|
-
2,
|
|
962
|
-
10,
|
|
963
|
-
14,
|
|
964
|
-
1,
|
|
965
|
-
3,
|
|
966
|
-
8,
|
|
967
|
-
11,
|
|
968
|
-
6,
|
|
969
|
-
15,
|
|
970
|
-
13
|
|
971
|
-
];
|
|
972
|
-
var ZR = [
|
|
973
|
-
5,
|
|
974
|
-
14,
|
|
975
|
-
7,
|
|
976
|
-
0,
|
|
977
|
-
9,
|
|
978
|
-
2,
|
|
979
|
-
11,
|
|
980
|
-
4,
|
|
981
|
-
13,
|
|
982
|
-
6,
|
|
983
|
-
15,
|
|
984
|
-
8,
|
|
985
|
-
1,
|
|
986
|
-
10,
|
|
987
|
-
3,
|
|
988
|
-
12,
|
|
989
|
-
6,
|
|
990
|
-
11,
|
|
991
|
-
3,
|
|
992
|
-
7,
|
|
993
|
-
0,
|
|
994
|
-
13,
|
|
995
|
-
5,
|
|
996
|
-
10,
|
|
997
|
-
14,
|
|
998
|
-
15,
|
|
999
|
-
8,
|
|
1000
|
-
12,
|
|
1001
|
-
4,
|
|
1002
|
-
9,
|
|
1003
|
-
1,
|
|
1004
|
-
2,
|
|
1005
|
-
15,
|
|
1006
|
-
5,
|
|
1007
|
-
1,
|
|
1008
|
-
3,
|
|
1009
|
-
7,
|
|
1010
|
-
14,
|
|
1011
|
-
6,
|
|
1012
|
-
9,
|
|
1013
|
-
11,
|
|
1014
|
-
8,
|
|
1015
|
-
12,
|
|
1016
|
-
2,
|
|
1017
|
-
10,
|
|
1018
|
-
0,
|
|
1019
|
-
4,
|
|
1020
|
-
13,
|
|
1021
|
-
8,
|
|
1022
|
-
6,
|
|
1023
|
-
4,
|
|
1024
|
-
1,
|
|
1025
|
-
3,
|
|
1026
|
-
11,
|
|
1027
|
-
15,
|
|
1028
|
-
0,
|
|
1029
|
-
5,
|
|
1030
|
-
12,
|
|
1031
|
-
2,
|
|
1032
|
-
13,
|
|
1033
|
-
9,
|
|
1034
|
-
7,
|
|
1035
|
-
10,
|
|
1036
|
-
14,
|
|
1037
|
-
12,
|
|
1038
|
-
15,
|
|
1039
|
-
10,
|
|
1040
|
-
4,
|
|
1041
|
-
1,
|
|
1042
|
-
5,
|
|
1043
|
-
8,
|
|
1044
|
-
7,
|
|
1045
|
-
6,
|
|
1046
|
-
2,
|
|
1047
|
-
13,
|
|
1048
|
-
14,
|
|
1049
|
-
0,
|
|
1050
|
-
3,
|
|
1051
|
-
9,
|
|
1052
|
-
11
|
|
1053
|
-
];
|
|
1054
|
-
var SL = [
|
|
1055
|
-
11,
|
|
1056
|
-
14,
|
|
1057
|
-
15,
|
|
1058
|
-
12,
|
|
1059
|
-
5,
|
|
1060
|
-
8,
|
|
1061
|
-
7,
|
|
1062
|
-
9,
|
|
1063
|
-
11,
|
|
1064
|
-
13,
|
|
1065
|
-
14,
|
|
1066
|
-
15,
|
|
1067
|
-
6,
|
|
1068
|
-
7,
|
|
1069
|
-
9,
|
|
1070
|
-
8,
|
|
1071
|
-
7,
|
|
1072
|
-
6,
|
|
1073
|
-
8,
|
|
1074
|
-
13,
|
|
1075
|
-
11,
|
|
1076
|
-
9,
|
|
1077
|
-
7,
|
|
1078
|
-
15,
|
|
1079
|
-
7,
|
|
1080
|
-
12,
|
|
1081
|
-
15,
|
|
1082
|
-
9,
|
|
1083
|
-
11,
|
|
1084
|
-
7,
|
|
1085
|
-
13,
|
|
1086
|
-
12,
|
|
1087
|
-
11,
|
|
1088
|
-
13,
|
|
1089
|
-
6,
|
|
1090
|
-
7,
|
|
1091
|
-
14,
|
|
1092
|
-
9,
|
|
1093
|
-
13,
|
|
1094
|
-
15,
|
|
1095
|
-
14,
|
|
1096
|
-
8,
|
|
1097
|
-
13,
|
|
1098
|
-
6,
|
|
1099
|
-
5,
|
|
1100
|
-
12,
|
|
1101
|
-
7,
|
|
1102
|
-
5,
|
|
1103
|
-
11,
|
|
1104
|
-
12,
|
|
1105
|
-
14,
|
|
1106
|
-
15,
|
|
1107
|
-
14,
|
|
1108
|
-
15,
|
|
1109
|
-
9,
|
|
1110
|
-
8,
|
|
1111
|
-
9,
|
|
1112
|
-
14,
|
|
1113
|
-
5,
|
|
1114
|
-
6,
|
|
1115
|
-
8,
|
|
1116
|
-
6,
|
|
1117
|
-
5,
|
|
1118
|
-
12,
|
|
1119
|
-
9,
|
|
1120
|
-
15,
|
|
1121
|
-
5,
|
|
1122
|
-
11,
|
|
1123
|
-
6,
|
|
1124
|
-
8,
|
|
1125
|
-
13,
|
|
1126
|
-
12,
|
|
1127
|
-
5,
|
|
1128
|
-
12,
|
|
1129
|
-
13,
|
|
1130
|
-
14,
|
|
1131
|
-
11,
|
|
1132
|
-
8,
|
|
1133
|
-
5,
|
|
1134
|
-
6
|
|
1135
|
-
];
|
|
1136
|
-
var SR = [
|
|
1137
|
-
8,
|
|
1138
|
-
9,
|
|
1139
|
-
9,
|
|
1140
|
-
11,
|
|
1141
|
-
13,
|
|
1142
|
-
15,
|
|
1143
|
-
15,
|
|
1144
|
-
5,
|
|
1145
|
-
7,
|
|
1146
|
-
7,
|
|
1147
|
-
8,
|
|
1148
|
-
11,
|
|
1149
|
-
14,
|
|
1150
|
-
14,
|
|
1151
|
-
12,
|
|
1152
|
-
6,
|
|
1153
|
-
9,
|
|
1154
|
-
13,
|
|
1155
|
-
15,
|
|
1156
|
-
7,
|
|
1157
|
-
12,
|
|
1158
|
-
8,
|
|
1159
|
-
9,
|
|
1160
|
-
11,
|
|
1161
|
-
7,
|
|
1162
|
-
7,
|
|
1163
|
-
12,
|
|
1164
|
-
7,
|
|
1165
|
-
6,
|
|
1166
|
-
15,
|
|
1167
|
-
13,
|
|
1168
|
-
11,
|
|
1169
|
-
9,
|
|
1170
|
-
7,
|
|
1171
|
-
15,
|
|
1172
|
-
11,
|
|
1173
|
-
8,
|
|
1174
|
-
6,
|
|
1175
|
-
6,
|
|
1176
|
-
14,
|
|
1177
|
-
12,
|
|
1178
|
-
13,
|
|
1179
|
-
5,
|
|
1180
|
-
14,
|
|
1181
|
-
13,
|
|
1182
|
-
13,
|
|
1183
|
-
7,
|
|
1184
|
-
5,
|
|
1185
|
-
15,
|
|
1186
|
-
5,
|
|
1187
|
-
8,
|
|
1188
|
-
11,
|
|
1189
|
-
14,
|
|
1190
|
-
14,
|
|
1191
|
-
6,
|
|
1192
|
-
14,
|
|
1193
|
-
6,
|
|
1194
|
-
9,
|
|
1195
|
-
12,
|
|
1196
|
-
9,
|
|
1197
|
-
12,
|
|
1198
|
-
5,
|
|
1199
|
-
15,
|
|
1200
|
-
8,
|
|
1201
|
-
8,
|
|
1202
|
-
5,
|
|
1203
|
-
12,
|
|
1204
|
-
9,
|
|
1205
|
-
12,
|
|
1206
|
-
5,
|
|
1207
|
-
14,
|
|
1208
|
-
6,
|
|
1209
|
-
8,
|
|
1210
|
-
13,
|
|
1211
|
-
6,
|
|
1212
|
-
5,
|
|
1213
|
-
15,
|
|
1214
|
-
13,
|
|
1215
|
-
11,
|
|
1216
|
-
11
|
|
1217
|
-
];
|
|
1218
|
-
var KL = [0, 1518500249, 1859775393, 2400959708, 2840853838];
|
|
1219
|
-
var KR = [1352829926, 1548603684, 1836072691, 2053994217, 0];
|
|
1220
|
-
var f = (j, x, y, z) => {
|
|
1221
|
-
if (j < 16) return x ^ y ^ z;
|
|
1222
|
-
if (j < 32) return x & y | ~x & z;
|
|
1223
|
-
if (j < 48) return (x | ~y) ^ z;
|
|
1224
|
-
if (j < 64) return x & z | y & ~z;
|
|
1225
|
-
return x ^ (y | ~z);
|
|
1226
|
-
};
|
|
1227
|
-
function ripemd160(msg) {
|
|
1228
|
-
let h0 = 1732584193, h1 = 4023233417, h2 = 2562383102, h3 = 271733878, h4 = 3285377520;
|
|
1229
|
-
const bitLen = msg.length * 8;
|
|
1230
|
-
const withOne = msg.length + 1;
|
|
1231
|
-
const padded = new Uint8Array(Math.ceil((withOne + 8) / 64) * 64);
|
|
1232
|
-
padded.set(msg, 0);
|
|
1233
|
-
padded[msg.length] = 128;
|
|
1234
|
-
const dv = new DataView(padded.buffer);
|
|
1235
|
-
dv.setUint32(padded.length - 8, bitLen >>> 0, true);
|
|
1236
|
-
dv.setUint32(padded.length - 4, Math.floor(bitLen / 2 ** 32), true);
|
|
1237
|
-
const x = new Uint32Array(16);
|
|
1238
|
-
for (let off = 0; off < padded.length; off += 64) {
|
|
1239
|
-
for (let i = 0; i < 16; i++) x[i] = dv.getUint32(off + i * 4, true);
|
|
1240
|
-
let al = h0, bl = h1, cl = h2, dl = h3, el = h4;
|
|
1241
|
-
let ar = h0, br = h1, cr = h2, dr = h3, er = h4;
|
|
1242
|
-
for (let j = 0; j < 80; j++) {
|
|
1243
|
-
const round = Math.floor(j / 16);
|
|
1244
|
-
let t2 = al + f(j, bl, cl, dl) + x[ZL[j]] + KL[round] | 0;
|
|
1245
|
-
t2 = rol(t2, SL[j]) + el | 0;
|
|
1246
|
-
al = el;
|
|
1247
|
-
el = dl;
|
|
1248
|
-
dl = rol(cl, 10);
|
|
1249
|
-
cl = bl;
|
|
1250
|
-
bl = t2;
|
|
1251
|
-
t2 = ar + f(79 - j, br, cr, dr) + x[ZR[j]] + KR[round] | 0;
|
|
1252
|
-
t2 = rol(t2, SR[j]) + er | 0;
|
|
1253
|
-
ar = er;
|
|
1254
|
-
er = dr;
|
|
1255
|
-
dr = rol(cr, 10);
|
|
1256
|
-
cr = br;
|
|
1257
|
-
br = t2;
|
|
1258
|
-
}
|
|
1259
|
-
const t = h1 + cl + dr | 0;
|
|
1260
|
-
h1 = h2 + dl + er | 0;
|
|
1261
|
-
h2 = h3 + el + ar | 0;
|
|
1262
|
-
h3 = h4 + al + br | 0;
|
|
1263
|
-
h4 = h0 + bl + cr | 0;
|
|
1264
|
-
h0 = t;
|
|
1265
|
-
}
|
|
1266
|
-
const out = new Uint8Array(20);
|
|
1267
|
-
const odv = new DataView(out.buffer);
|
|
1268
|
-
odv.setUint32(0, h0, true);
|
|
1269
|
-
odv.setUint32(4, h1, true);
|
|
1270
|
-
odv.setUint32(8, h2, true);
|
|
1271
|
-
odv.setUint32(12, h3, true);
|
|
1272
|
-
odv.setUint32(16, h4, true);
|
|
1273
|
-
return out;
|
|
1274
|
-
}
|
|
1275
|
-
var MAX_RESULT_LENGTH = 4096;
|
|
1276
|
-
var MAX_MSG_LENGTH = 4096;
|
|
1277
|
-
function concatBytes(a, b) {
|
|
1278
|
-
const out = new Uint8Array(a.length + b.length);
|
|
1279
|
-
out.set(a, 0);
|
|
1280
|
-
out.set(b, a.length);
|
|
1281
|
-
return out;
|
|
1282
|
-
}
|
|
1283
|
-
var Op = class _Op {
|
|
1284
|
-
static MAX_RESULT_LENGTH = MAX_RESULT_LENGTH;
|
|
1285
|
-
static MAX_MSG_LENGTH = MAX_MSG_LENGTH;
|
|
1286
|
-
/** Deserializa una operación leyendo su tag y despachando a la factoría correcta. */
|
|
1287
|
-
static deserialize(ctx) {
|
|
1288
|
-
return _Op.deserializeFromTag(ctx, ctx.readByte());
|
|
1289
|
-
}
|
|
1290
|
-
/** Igual que `deserialize`, pero con el tag ya leído del stream (lo usa el árbol Timestamp). */
|
|
1291
|
-
static deserializeFromTag(ctx, tag) {
|
|
1292
|
-
const factory = OP_BY_TAG.get(tag);
|
|
1293
|
-
if (factory === void 0) {
|
|
1294
|
-
throw new UnknownOperationError(`unknown operation tag 0x${tag.toString(16).padStart(2, "0")}`);
|
|
1295
|
-
}
|
|
1296
|
-
return factory(ctx);
|
|
1297
|
-
}
|
|
1298
|
-
get maxMsgLength() {
|
|
1299
|
-
return MAX_MSG_LENGTH;
|
|
1300
|
-
}
|
|
1301
|
-
checkMsg(msg) {
|
|
1302
|
-
if (msg.length > this.maxMsgLength) {
|
|
1303
|
-
throw new MessageTooLongError(`message length ${msg.length} exceeds ${this.maxMsgLength}`);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
checkResult(result) {
|
|
1307
|
-
if (result.length > MAX_RESULT_LENGTH) {
|
|
1308
|
-
throw new ResultTooLongError(`result length ${result.length} exceeds ${MAX_RESULT_LENGTH}`);
|
|
1309
|
-
}
|
|
1310
|
-
return result;
|
|
1311
|
-
}
|
|
1312
|
-
};
|
|
1313
|
-
var OpBinary = class extends Op {
|
|
1314
|
-
arg;
|
|
1315
|
-
constructor(arg) {
|
|
1316
|
-
super();
|
|
1317
|
-
if (!(arg instanceof Uint8Array)) {
|
|
1318
|
-
throw new TypeError("OpBinary arg must be a Uint8Array");
|
|
1319
|
-
}
|
|
1320
|
-
this.arg = arg.slice();
|
|
1321
|
-
}
|
|
1322
|
-
serialize(ctx) {
|
|
1323
|
-
ctx.writeByte(this.tag);
|
|
1324
|
-
ctx.writeVarbytes(this.arg);
|
|
1325
|
-
}
|
|
1326
|
-
};
|
|
1327
|
-
var OpAppend = class _OpAppend extends OpBinary {
|
|
1328
|
-
static TAG = 240;
|
|
1329
|
-
tag = _OpAppend.TAG;
|
|
1330
|
-
tagName = "append";
|
|
1331
|
-
call(msg) {
|
|
1332
|
-
this.checkMsg(msg);
|
|
1333
|
-
return this.checkResult(concatBytes(msg, this.arg));
|
|
1334
|
-
}
|
|
1335
|
-
equals(other) {
|
|
1336
|
-
return other instanceof _OpAppend && bytesEqual(this.arg, other.arg);
|
|
1337
|
-
}
|
|
1338
|
-
};
|
|
1339
|
-
var OpPrepend = class _OpPrepend extends OpBinary {
|
|
1340
|
-
static TAG = 241;
|
|
1341
|
-
tag = _OpPrepend.TAG;
|
|
1342
|
-
tagName = "prepend";
|
|
1343
|
-
call(msg) {
|
|
1344
|
-
this.checkMsg(msg);
|
|
1345
|
-
return this.checkResult(concatBytes(this.arg, msg));
|
|
1346
|
-
}
|
|
1347
|
-
equals(other) {
|
|
1348
|
-
return other instanceof _OpPrepend && bytesEqual(this.arg, other.arg);
|
|
1349
|
-
}
|
|
1350
|
-
};
|
|
1351
|
-
var OpUnary = class extends Op {
|
|
1352
|
-
serialize(ctx) {
|
|
1353
|
-
ctx.writeByte(this.tag);
|
|
1354
|
-
}
|
|
1355
|
-
};
|
|
1356
|
-
var OpReverse = class _OpReverse extends OpUnary {
|
|
1357
|
-
static TAG = 242;
|
|
1358
|
-
tag = _OpReverse.TAG;
|
|
1359
|
-
tagName = "reverse";
|
|
1360
|
-
call(msg) {
|
|
1361
|
-
this.checkMsg(msg);
|
|
1362
|
-
const r = new Uint8Array(msg.length);
|
|
1363
|
-
for (let i = 0; i < msg.length; i++) r[i] = msg[msg.length - 1 - i];
|
|
1364
|
-
return this.checkResult(r);
|
|
1365
|
-
}
|
|
1366
|
-
equals(other) {
|
|
1367
|
-
return other instanceof _OpReverse;
|
|
1368
|
-
}
|
|
1369
|
-
};
|
|
1370
|
-
var OpHexlify = class _OpHexlify extends OpUnary {
|
|
1371
|
-
static TAG = 243;
|
|
1372
|
-
tag = _OpHexlify.TAG;
|
|
1373
|
-
tagName = "hexlify";
|
|
1374
|
-
// El resultado mide el doble que el mensaje; el límite de mensaje es la mitad.
|
|
1375
|
-
get maxMsgLength() {
|
|
1376
|
-
return MAX_RESULT_LENGTH / 2;
|
|
1377
|
-
}
|
|
1378
|
-
call(msg) {
|
|
1379
|
-
this.checkMsg(msg);
|
|
1380
|
-
return this.checkResult(textToBytes(bytesToHex(msg)));
|
|
1381
|
-
}
|
|
1382
|
-
equals(other) {
|
|
1383
|
-
return other instanceof _OpHexlify;
|
|
1384
|
-
}
|
|
1385
|
-
};
|
|
1386
|
-
var CryptOp = class extends OpUnary {
|
|
1387
|
-
call(msg) {
|
|
1388
|
-
this.checkMsg(msg);
|
|
1389
|
-
return this.hash(msg);
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
|
-
* Hashea el contenido COMPLETO de un fichero (longitud arbitraria) con el algoritmo
|
|
1393
|
-
* de esta operación. A diferencia de `call`, NO aplica el límite `MAX_MSG_LENGTH`:
|
|
1394
|
-
* `call` transforma digests dentro del árbol de prueba (≤ 4096 bytes), mientras que
|
|
1395
|
-
* `hashFile` recibe el contenido íntegro del fichero a sellar, que puede ser de
|
|
1396
|
-
* cualquier tamaño. Lo usa `DetachedTimestampFile.fromBytes`.
|
|
1397
|
-
*/
|
|
1398
|
-
hashFile(data) {
|
|
1399
|
-
if (!(data instanceof Uint8Array)) {
|
|
1400
|
-
throw new TypeError("hashFile expects a Uint8Array");
|
|
1401
|
-
}
|
|
1402
|
-
return this.hash(data);
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
1405
|
-
var OpSHA1 = class _OpSHA1 extends CryptOp {
|
|
1406
|
-
static TAG = 2;
|
|
1407
|
-
tag = _OpSHA1.TAG;
|
|
1408
|
-
tagName = "sha1";
|
|
1409
|
-
digestLength = 20;
|
|
1410
|
-
hash(msg) {
|
|
1411
|
-
return sha1(msg);
|
|
1412
|
-
}
|
|
1413
|
-
equals(other) {
|
|
1414
|
-
return other instanceof _OpSHA1;
|
|
1415
|
-
}
|
|
1416
|
-
};
|
|
1417
|
-
var OpRIPEMD160 = class _OpRIPEMD160 extends CryptOp {
|
|
1418
|
-
static TAG = 3;
|
|
1419
|
-
tag = _OpRIPEMD160.TAG;
|
|
1420
|
-
tagName = "ripemd160";
|
|
1421
|
-
digestLength = 20;
|
|
1422
|
-
hash(msg) {
|
|
1423
|
-
return ripemd160(msg);
|
|
1424
|
-
}
|
|
1425
|
-
equals(other) {
|
|
1426
|
-
return other instanceof _OpRIPEMD160;
|
|
1427
|
-
}
|
|
1428
|
-
};
|
|
1429
|
-
var OpSHA256 = class _OpSHA256 extends CryptOp {
|
|
1430
|
-
static TAG = 8;
|
|
1431
|
-
tag = _OpSHA256.TAG;
|
|
1432
|
-
tagName = "sha256";
|
|
1433
|
-
digestLength = 32;
|
|
1434
|
-
hash(msg) {
|
|
1435
|
-
return sha256(msg);
|
|
1436
|
-
}
|
|
1437
|
-
equals(other) {
|
|
1438
|
-
return other instanceof _OpSHA256;
|
|
1439
|
-
}
|
|
1440
|
-
};
|
|
1441
|
-
var unary = (ctor) => () => new ctor();
|
|
1442
|
-
var binary = (ctor) => (ctx) => new ctor(ctx.readVarbytes(MAX_RESULT_LENGTH, 1));
|
|
1443
|
-
function buildTagTable(entries) {
|
|
1444
|
-
const map = /* @__PURE__ */ new Map();
|
|
1445
|
-
for (const [tag, factory] of entries) {
|
|
1446
|
-
if (map.has(tag)) {
|
|
1447
|
-
throw new Error(`duplicate operation tag 0x${tag.toString(16)}`);
|
|
1448
|
-
}
|
|
1449
|
-
map.set(tag, factory);
|
|
1450
|
-
}
|
|
1451
|
-
return map;
|
|
1452
|
-
}
|
|
1453
|
-
var OP_BY_TAG = buildTagTable([
|
|
1454
|
-
[OpAppend.TAG, binary(OpAppend)],
|
|
1455
|
-
[OpPrepend.TAG, binary(OpPrepend)],
|
|
1456
|
-
[OpReverse.TAG, unary(OpReverse)],
|
|
1457
|
-
[OpHexlify.TAG, unary(OpHexlify)],
|
|
1458
|
-
[OpSHA1.TAG, unary(OpSHA1)],
|
|
1459
|
-
[OpRIPEMD160.TAG, unary(OpRIPEMD160)],
|
|
1460
|
-
[OpSHA256.TAG, unary(OpSHA256)]
|
|
1461
|
-
]);
|
|
1462
|
-
var TAG_SIZE = 8;
|
|
1463
|
-
var MAX_PAYLOAD_SIZE = 8192;
|
|
1464
|
-
var MAX_URI_LENGTH = 1e3;
|
|
1465
|
-
var ALLOWED_URI_CHARS = new Set(
|
|
1466
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._/:"
|
|
1467
|
-
);
|
|
1468
|
-
var PENDING_TAG = new Uint8Array([131, 223, 227, 13, 46, 249, 12, 142]);
|
|
1469
|
-
var BITCOIN_TAG = new Uint8Array([5, 136, 150, 13, 115, 215, 25, 1]);
|
|
1470
|
-
var LITECOIN_TAG = new Uint8Array([6, 134, 154, 13, 115, 215, 27, 69]);
|
|
1471
|
-
function decodeAndValidateUri(bytes) {
|
|
1472
|
-
if (bytes.length === 0) {
|
|
1473
|
-
throw new InvalidUriError("pending attestation URI is empty");
|
|
1474
|
-
}
|
|
1475
|
-
if (bytes.length > MAX_URI_LENGTH) {
|
|
1476
|
-
throw new InvalidUriError(`pending attestation URI exceeds ${MAX_URI_LENGTH} bytes`);
|
|
1477
|
-
}
|
|
1478
|
-
let uri = "";
|
|
1479
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
1480
|
-
const byte = bytes[i];
|
|
1481
|
-
const char = String.fromCharCode(byte);
|
|
1482
|
-
if (!ALLOWED_URI_CHARS.has(char)) {
|
|
1483
|
-
throw new InvalidUriError(
|
|
1484
|
-
`pending attestation URI contains invalid byte 0x${byte.toString(16).padStart(2, "0")}`
|
|
1485
|
-
);
|
|
1486
|
-
}
|
|
1487
|
-
uri += char;
|
|
1488
|
-
}
|
|
1489
|
-
return uri;
|
|
1490
|
-
}
|
|
1491
|
-
function deserializeAttestation(ctx) {
|
|
1492
|
-
const tag = ctx.read(TAG_SIZE).slice();
|
|
1493
|
-
const payload = ctx.readVarbytes(MAX_PAYLOAD_SIZE);
|
|
1494
|
-
const payloadCtx = new StreamDeserializationContext(payload);
|
|
1495
|
-
let attestation;
|
|
1496
|
-
if (bytesEqual(tag, PENDING_TAG)) {
|
|
1497
|
-
const uriBytes = payloadCtx.readVarbytes(MAX_URI_LENGTH).slice();
|
|
1498
|
-
attestation = { kind: "pending", tag, uri: decodeAndValidateUri(uriBytes), uriBytes };
|
|
1499
|
-
} else if (bytesEqual(tag, BITCOIN_TAG)) {
|
|
1500
|
-
attestation = { kind: "bitcoin", tag, height: payloadCtx.readVaruint() };
|
|
1501
|
-
} else if (bytesEqual(tag, LITECOIN_TAG)) {
|
|
1502
|
-
attestation = { kind: "litecoin", tag, height: payloadCtx.readVaruint() };
|
|
1503
|
-
} else {
|
|
1504
|
-
return { kind: "unknown", tag, payload: payload.slice() };
|
|
1505
|
-
}
|
|
1506
|
-
payloadCtx.assertEof();
|
|
1507
|
-
return attestation;
|
|
1508
|
-
}
|
|
1509
|
-
function serializePayload(ctx, att) {
|
|
1510
|
-
switch (att.kind) {
|
|
1511
|
-
case "pending":
|
|
1512
|
-
ctx.writeVarbytes(att.uriBytes);
|
|
1513
|
-
return;
|
|
1514
|
-
case "bitcoin":
|
|
1515
|
-
case "litecoin":
|
|
1516
|
-
ctx.writeVaruint(att.height);
|
|
1517
|
-
return;
|
|
1518
|
-
case "unknown":
|
|
1519
|
-
ctx.writeBytes(att.payload);
|
|
1520
|
-
return;
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
function serializeAttestation(ctx, att) {
|
|
1524
|
-
ctx.writeBytes(att.tag);
|
|
1525
|
-
const payloadCtx = new StreamSerializationContext();
|
|
1526
|
-
serializePayload(payloadCtx, att);
|
|
1527
|
-
ctx.writeVarbytes(payloadCtx.getOutput());
|
|
1528
|
-
}
|
|
1529
|
-
function compareAttestations(a, b) {
|
|
1530
|
-
const deltaTag = compareBytes(a.tag, b.tag);
|
|
1531
|
-
if (deltaTag !== 0) {
|
|
1532
|
-
return deltaTag;
|
|
1533
|
-
}
|
|
1534
|
-
switch (a.kind) {
|
|
1535
|
-
case "pending":
|
|
1536
|
-
return compareBytes(a.uriBytes, b.uriBytes);
|
|
1537
|
-
case "bitcoin":
|
|
1538
|
-
case "litecoin":
|
|
1539
|
-
return a.height - b.height;
|
|
1540
|
-
case "unknown":
|
|
1541
|
-
return compareBytes(a.payload, b.payload);
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
function attestationsEqual(a, b) {
|
|
1545
|
-
if (a.kind !== b.kind || !bytesEqual(a.tag, b.tag)) {
|
|
1546
|
-
return false;
|
|
1547
|
-
}
|
|
1548
|
-
switch (a.kind) {
|
|
1549
|
-
case "pending":
|
|
1550
|
-
return bytesEqual(a.uriBytes, b.uriBytes);
|
|
1551
|
-
case "bitcoin":
|
|
1552
|
-
case "litecoin":
|
|
1553
|
-
return a.height === b.height;
|
|
1554
|
-
case "unknown":
|
|
1555
|
-
return bytesEqual(a.payload, b.payload);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
var MERKLEROOT_RE = /^[0-9a-fA-F]{64}$/;
|
|
1559
|
-
function verifyAgainstBlockheader(digest, block) {
|
|
1560
|
-
if (digest.length !== 32) {
|
|
1561
|
-
throw new VerificationError(`expected digest of 32 bytes; got ${digest.length}`);
|
|
1562
|
-
}
|
|
1563
|
-
if (typeof block.merkleroot !== "string" || !MERKLEROOT_RE.test(block.merkleroot)) {
|
|
1564
|
-
throw new VerificationError("block merkleroot is not a 64-char hex string");
|
|
1565
|
-
}
|
|
1566
|
-
if (!Number.isInteger(block.time) || block.time <= 0) {
|
|
1567
|
-
throw new VerificationError("block time is not a positive integer");
|
|
1568
|
-
}
|
|
1569
|
-
if (!bytesEqual(digest, hexToBytes(block.merkleroot))) {
|
|
1570
|
-
throw new VerificationError("digest does not match block merkleroot");
|
|
1571
|
-
}
|
|
1572
|
-
return block.time;
|
|
1573
|
-
}
|
|
1574
|
-
var MAX_TREE_DEPTH = 256;
|
|
1575
|
-
function opToBytes(op) {
|
|
1576
|
-
const ctx = new StreamSerializationContext();
|
|
1577
|
-
op.serialize(ctx);
|
|
1578
|
-
return ctx.getOutput();
|
|
1579
|
-
}
|
|
1580
|
-
function opKey(op) {
|
|
1581
|
-
return bytesToHex(opToBytes(op));
|
|
1582
|
-
}
|
|
1583
|
-
var Timestamp = class _Timestamp {
|
|
1584
|
-
/** Digest de este nodo (copia defensiva, no comparte memoria con la entrada). */
|
|
1585
|
-
msg;
|
|
1586
|
-
/** Sellos directos sobre `msg`. El cliente puede añadir con `.push()`. */
|
|
1587
|
-
attestations = [];
|
|
1588
|
-
/** Ramas indexadas por la serialización canónica (hex) de su op. */
|
|
1589
|
-
#ops = /* @__PURE__ */ new Map();
|
|
1590
|
-
constructor(msg) {
|
|
1591
|
-
if (!(msg instanceof Uint8Array)) {
|
|
1592
|
-
throw new TypeError("Timestamp msg must be a Uint8Array");
|
|
1593
|
-
}
|
|
1594
|
-
if (msg.length > Op.MAX_MSG_LENGTH) {
|
|
1595
|
-
throw new TypeError(`Timestamp msg length ${msg.length} exceeds ${Op.MAX_MSG_LENGTH}`);
|
|
1596
|
-
}
|
|
1597
|
-
this.msg = msg.slice();
|
|
1598
|
-
}
|
|
1599
|
-
/** El digest de este nodo (copia: mutar el resultado no afecta al árbol). */
|
|
1600
|
-
getDigest() {
|
|
1601
|
-
return this.msg.slice();
|
|
1602
|
-
}
|
|
1603
|
-
/** Las ramas (op + sub-timestamp) de este nodo, como array de solo lectura. */
|
|
1604
|
-
get branches() {
|
|
1605
|
-
return [...this.#ops.values()];
|
|
1606
|
-
}
|
|
1607
|
-
/**
|
|
1608
|
-
* Deserializa un Timestamp. El formato no incluye el mensaje sobre el que opera,
|
|
1609
|
-
* así que hay que aportarlo (`initialMsg`) para recalcular los resultados de las ops.
|
|
1610
|
-
* @param depth profundidad actual; protege contra árboles maliciosamente profundos.
|
|
1611
|
-
*/
|
|
1612
|
-
static deserialize(ctx, initialMsg, depth = 0) {
|
|
1613
|
-
if (depth > MAX_TREE_DEPTH) {
|
|
1614
|
-
throw new OversizedDataError(`timestamp tree exceeds max depth ${MAX_TREE_DEPTH}`);
|
|
1615
|
-
}
|
|
1616
|
-
const self = new _Timestamp(initialMsg);
|
|
1617
|
-
let tag = ctx.readByte();
|
|
1618
|
-
while (tag === 255) {
|
|
1619
|
-
self.#deserializeElement(ctx, ctx.readByte(), depth);
|
|
1620
|
-
tag = ctx.readByte();
|
|
1621
|
-
}
|
|
1622
|
-
self.#deserializeElement(ctx, tag, depth);
|
|
1623
|
-
return self;
|
|
1624
|
-
}
|
|
1625
|
-
#deserializeElement(ctx, tag, depth) {
|
|
1626
|
-
if (tag === 0) {
|
|
1627
|
-
this.attestations.push(deserializeAttestation(ctx));
|
|
1628
|
-
return;
|
|
1629
|
-
}
|
|
1630
|
-
const op = Op.deserializeFromTag(ctx, tag);
|
|
1631
|
-
let result;
|
|
1632
|
-
try {
|
|
1633
|
-
result = op.call(this.msg);
|
|
1634
|
-
} catch (err) {
|
|
1635
|
-
throw new DeserializationError(
|
|
1636
|
-
`operation failed during deserialization: ${err.message}`
|
|
1637
|
-
);
|
|
1638
|
-
}
|
|
1639
|
-
const stamp = _Timestamp.deserialize(ctx, result, depth + 1);
|
|
1640
|
-
this.#ops.set(opKey(op), { op, stamp });
|
|
1641
|
-
}
|
|
1642
|
-
/** Serializa este nodo en orden canónico (determinista byte-a-byte). */
|
|
1643
|
-
serialize(ctx) {
|
|
1644
|
-
const attestations = [...this.attestations].sort(compareAttestations);
|
|
1645
|
-
const branches = [...this.#ops.values()].sort(
|
|
1646
|
-
(a, b) => compareBytes(opToBytes(a.op), opToBytes(b.op))
|
|
1647
|
-
);
|
|
1648
|
-
const total = attestations.length + branches.length;
|
|
1649
|
-
if (total === 0) {
|
|
1650
|
-
throw new EmptyTimestampError("an empty timestamp cannot be serialized");
|
|
1651
|
-
}
|
|
1652
|
-
let index = 0;
|
|
1653
|
-
for (const attestation of attestations) {
|
|
1654
|
-
if (index < total - 1) ctx.writeByte(255);
|
|
1655
|
-
ctx.writeByte(0);
|
|
1656
|
-
serializeAttestation(ctx, attestation);
|
|
1657
|
-
index++;
|
|
1658
|
-
}
|
|
1659
|
-
for (const { op, stamp } of branches) {
|
|
1660
|
-
if (index < total - 1) ctx.writeByte(255);
|
|
1661
|
-
op.serialize(ctx);
|
|
1662
|
-
stamp.serialize(ctx);
|
|
1663
|
-
index++;
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
/**
|
|
1667
|
-
* Añade una op a este nodo y devuelve el sub-timestamp de su resultado.
|
|
1668
|
-
* Si la op (por contenido) ya existe, devuelve la rama existente.
|
|
1669
|
-
*/
|
|
1670
|
-
add(op) {
|
|
1671
|
-
const key = opKey(op);
|
|
1672
|
-
const existing = this.#ops.get(key);
|
|
1673
|
-
if (existing !== void 0) {
|
|
1674
|
-
return existing.stamp;
|
|
1675
|
-
}
|
|
1676
|
-
const stamp = new _Timestamp(op.call(this.msg));
|
|
1677
|
-
this.#ops.set(key, { op, stamp });
|
|
1678
|
-
return stamp;
|
|
1679
|
-
}
|
|
1680
|
-
/**
|
|
1681
|
-
* Vincula `op` a un sub-timestamp YA EXISTENTE, compartiendo el objeto (no crea uno nuevo).
|
|
1682
|
-
* A diferencia de `add`, hace que esta rama apunte al mismo `Timestamp` que otra rama
|
|
1683
|
-
* (de otro nodo) ya construyó, de modo que las attestations añadidas más arriba sean
|
|
1684
|
-
* alcanzables desde ambos caminos. Lo usa el árbol Merkle para el cross-link izquierda/derecha.
|
|
1685
|
-
* Falla (fail-closed) si `stamp` no es un Timestamp o si `op.call(this.msg)` no coincide con `stamp.msg`.
|
|
1686
|
-
*/
|
|
1687
|
-
addExisting(op, stamp) {
|
|
1688
|
-
if (!(stamp instanceof _Timestamp)) {
|
|
1689
|
-
throw new TypeError("addExisting requires a Timestamp");
|
|
1690
|
-
}
|
|
1691
|
-
if (!bytesEqual(op.call(this.msg), stamp.msg)) {
|
|
1692
|
-
throw new MergeError("operation result does not match the existing timestamp message");
|
|
1693
|
-
}
|
|
1694
|
-
this.#ops.set(opKey(op), { op, stamp });
|
|
1695
|
-
return stamp;
|
|
1696
|
-
}
|
|
1697
|
-
/** Incorpora las attestations y ramas de `other` (mismo `msg`) en este timestamp. */
|
|
1698
|
-
merge(other) {
|
|
1699
|
-
if (!(other instanceof _Timestamp)) {
|
|
1700
|
-
throw new MergeError("can only merge Timestamps together");
|
|
1701
|
-
}
|
|
1702
|
-
if (!bytesEqual(this.msg, other.msg)) {
|
|
1703
|
-
throw new MergeError("cannot merge timestamps for different messages");
|
|
1704
|
-
}
|
|
1705
|
-
for (const attestation of other.attestations) {
|
|
1706
|
-
if (!this.attestations.some((existing) => attestationsEqual(existing, attestation))) {
|
|
1707
|
-
this.attestations.push(attestation);
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
for (const { op, stamp } of other.#ops.values()) {
|
|
1711
|
-
const key = opKey(op);
|
|
1712
|
-
let branch = this.#ops.get(key);
|
|
1713
|
-
if (branch === void 0) {
|
|
1714
|
-
branch = { op, stamp: new _Timestamp(op.call(this.msg)) };
|
|
1715
|
-
this.#ops.set(key, branch);
|
|
1716
|
-
}
|
|
1717
|
-
branch.stamp.merge(stamp);
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
/** Todas las attestations del árbol con el msg de su nodo (sin pérdida de datos). */
|
|
1721
|
-
allAttestations() {
|
|
1722
|
-
const result = [];
|
|
1723
|
-
for (const attestation of this.attestations) {
|
|
1724
|
-
result.push({ msg: this.msg.slice(), attestation });
|
|
1725
|
-
}
|
|
1726
|
-
for (const { stamp } of this.#ops.values()) {
|
|
1727
|
-
result.push(...stamp.allAttestations());
|
|
1728
|
-
}
|
|
1729
|
-
return result;
|
|
1730
|
-
}
|
|
1731
|
-
/** Todas las attestations del árbol (sin el msg asociado). */
|
|
1732
|
-
getAttestations() {
|
|
1733
|
-
return this.allAttestations().map((entry) => entry.attestation);
|
|
1734
|
-
}
|
|
1735
|
-
/** Verdadero si el árbol contiene una attestation verificable localmente (Bitcoin/Litecoin). */
|
|
1736
|
-
isTimestampComplete() {
|
|
1737
|
-
return this.allAttestations().some(
|
|
1738
|
-
({ attestation }) => attestation.kind === "bitcoin" || attestation.kind === "litecoin"
|
|
1739
|
-
);
|
|
1740
|
-
}
|
|
1741
|
-
/** Sub-timestamps que tienen attestations directas. */
|
|
1742
|
-
directlyVerified() {
|
|
1743
|
-
if (this.attestations.length > 0) {
|
|
1744
|
-
return [this];
|
|
1745
|
-
}
|
|
1746
|
-
const result = [];
|
|
1747
|
-
for (const { stamp } of this.#ops.values()) {
|
|
1748
|
-
result.push(...stamp.directlyVerified());
|
|
1749
|
-
}
|
|
1750
|
-
return result;
|
|
1751
|
-
}
|
|
1752
|
-
/** Los mensajes de las hojas del árbol (nodos sin ops). */
|
|
1753
|
-
allTips() {
|
|
1754
|
-
if (this.#ops.size === 0) {
|
|
1755
|
-
return [this.msg.slice()];
|
|
1756
|
-
}
|
|
1757
|
-
const result = [];
|
|
1758
|
-
for (const { stamp } of this.#ops.values()) {
|
|
1759
|
-
result.push(...stamp.allTips());
|
|
1760
|
-
}
|
|
1761
|
-
return result;
|
|
1762
|
-
}
|
|
1763
|
-
/** Igualdad estructural recursiva con otro timestamp. */
|
|
1764
|
-
equals(other) {
|
|
1765
|
-
if (!(other instanceof _Timestamp)) {
|
|
1766
|
-
return false;
|
|
1767
|
-
}
|
|
1768
|
-
if (!bytesEqual(this.msg, other.msg)) {
|
|
1769
|
-
return false;
|
|
1770
|
-
}
|
|
1771
|
-
if (this.attestations.length !== other.attestations.length) {
|
|
1772
|
-
return false;
|
|
1773
|
-
}
|
|
1774
|
-
const ours = [...this.attestations].sort(compareAttestations);
|
|
1775
|
-
const theirs = [...other.attestations].sort(compareAttestations);
|
|
1776
|
-
for (let i = 0; i < ours.length; i++) {
|
|
1777
|
-
if (!attestationsEqual(ours[i], theirs[i])) {
|
|
1778
|
-
return false;
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
if (this.#ops.size !== other.#ops.size) {
|
|
1782
|
-
return false;
|
|
1783
|
-
}
|
|
1784
|
-
for (const [key, branch] of this.#ops) {
|
|
1785
|
-
const otherBranch = other.#ops.get(key);
|
|
1786
|
-
if (otherBranch === void 0) {
|
|
1787
|
-
return false;
|
|
1788
|
-
}
|
|
1789
|
-
if (!branch.stamp.equals(otherBranch.stamp)) {
|
|
1790
|
-
return false;
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
return true;
|
|
1794
|
-
}
|
|
1795
|
-
};
|
|
1796
|
-
function catSha256(left, right) {
|
|
1797
|
-
if (!(left instanceof Timestamp) || !(right instanceof Timestamp)) {
|
|
1798
|
-
throw new TypeError("catSha256 requires two Timestamps");
|
|
1799
|
-
}
|
|
1800
|
-
const concat = right.add(new OpPrepend(left.msg));
|
|
1801
|
-
left.addExisting(new OpAppend(right.msg), concat);
|
|
1802
|
-
return concat.add(new OpSHA256());
|
|
1803
|
-
}
|
|
1804
|
-
function makeMerkleTree(timestamps) {
|
|
1805
|
-
if (timestamps.length === 0) {
|
|
1806
|
-
throw new EmptyMerkleTreeError("makeMerkleTree requires at least one timestamp");
|
|
1807
|
-
}
|
|
1808
|
-
for (const stamp of timestamps) {
|
|
1809
|
-
if (!(stamp instanceof Timestamp)) {
|
|
1810
|
-
throw new TypeError("makeMerkleTree requires an array of Timestamps");
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
let round = [...timestamps];
|
|
1814
|
-
while (round.length > 1) {
|
|
1815
|
-
const next = [];
|
|
1816
|
-
for (let i = 0; i < round.length; i += 2) {
|
|
1817
|
-
if (i + 1 < round.length) {
|
|
1818
|
-
next.push(catSha256(round[i], round[i + 1]));
|
|
1819
|
-
} else {
|
|
1820
|
-
next.push(round[i]);
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
round = next;
|
|
1824
|
-
}
|
|
1825
|
-
return round[0];
|
|
1826
|
-
}
|
|
1827
|
-
var HEADER_MAGIC = new Uint8Array([
|
|
1828
|
-
0,
|
|
1829
|
-
79,
|
|
1830
|
-
112,
|
|
1831
|
-
101,
|
|
1832
|
-
110,
|
|
1833
|
-
84,
|
|
1834
|
-
105,
|
|
1835
|
-
109,
|
|
1836
|
-
101,
|
|
1837
|
-
115,
|
|
1838
|
-
116,
|
|
1839
|
-
97,
|
|
1840
|
-
109,
|
|
1841
|
-
112,
|
|
1842
|
-
115,
|
|
1843
|
-
0,
|
|
1844
|
-
0,
|
|
1845
|
-
80,
|
|
1846
|
-
114,
|
|
1847
|
-
111,
|
|
1848
|
-
111,
|
|
1849
|
-
102,
|
|
1850
|
-
0,
|
|
1851
|
-
191,
|
|
1852
|
-
137,
|
|
1853
|
-
226,
|
|
1854
|
-
232,
|
|
1855
|
-
132,
|
|
1856
|
-
232,
|
|
1857
|
-
146,
|
|
1858
|
-
148
|
|
1859
|
-
]);
|
|
1860
|
-
var MAJOR_VERSION = 1;
|
|
1861
|
-
var DetachedTimestampFile = class _DetachedTimestampFile {
|
|
1862
|
-
fileHashOp;
|
|
1863
|
-
timestamp;
|
|
1864
|
-
constructor(fileHashOp, timestamp) {
|
|
1865
|
-
if (!(fileHashOp instanceof CryptOp)) {
|
|
1866
|
-
throw new TypeError("DetachedTimestampFile: fileHashOp must be a CryptOp");
|
|
1867
|
-
}
|
|
1868
|
-
if (!(timestamp instanceof Timestamp)) {
|
|
1869
|
-
throw new TypeError("DetachedTimestampFile: timestamp must be a Timestamp");
|
|
1870
|
-
}
|
|
1871
|
-
if (timestamp.msg.length !== fileHashOp.digestLength) {
|
|
1872
|
-
throw new TypeError(
|
|
1873
|
-
`DetachedTimestampFile: timestamp message length ${timestamp.msg.length} does not match ${fileHashOp.tagName} digest length ${fileHashOp.digestLength}`
|
|
1874
|
-
);
|
|
1875
|
-
}
|
|
1876
|
-
this.fileHashOp = fileHashOp;
|
|
1877
|
-
this.timestamp = timestamp;
|
|
1878
|
-
}
|
|
1879
|
-
/** Digest del fichero sellado (copia defensiva: mutarla no afecta al objeto). */
|
|
1880
|
-
fileDigest() {
|
|
1881
|
-
return this.timestamp.getDigest();
|
|
1882
|
-
}
|
|
1883
|
-
/** Escribe el fichero `.ots` en el contexto: magic → versión → op → digest → árbol. */
|
|
1884
|
-
serialize(ctx) {
|
|
1885
|
-
ctx.writeBytes(HEADER_MAGIC);
|
|
1886
|
-
ctx.writeVaruint(MAJOR_VERSION);
|
|
1887
|
-
this.fileHashOp.serialize(ctx);
|
|
1888
|
-
ctx.writeBytes(this.timestamp.msg);
|
|
1889
|
-
this.timestamp.serialize(ctx);
|
|
1890
|
-
}
|
|
1891
|
-
/** Serializa el fichero `.ots` completo a bytes. */
|
|
1892
|
-
serializeToBytes() {
|
|
1893
|
-
const ctx = new StreamSerializationContext();
|
|
1894
|
-
this.serialize(ctx);
|
|
1895
|
-
return ctx.getOutput();
|
|
1896
|
-
}
|
|
1897
|
-
/**
|
|
1898
|
-
* Lee un fichero `.ots` desde bytes. Único tipo de entrada: `Uint8Array` (fail-closed;
|
|
1899
|
-
* elimina los 4 tipos del original y el bug `Array.from(ArrayBuffer) → []`).
|
|
1900
|
-
*/
|
|
1901
|
-
static deserialize(input) {
|
|
1902
|
-
if (!(input instanceof Uint8Array)) {
|
|
1903
|
-
throw new TypeError("DetachedTimestampFile.deserialize expects a Uint8Array");
|
|
1904
|
-
}
|
|
1905
|
-
const ctx = new StreamDeserializationContext(input);
|
|
1906
|
-
ctx.assertMagic(HEADER_MAGIC);
|
|
1907
|
-
const major = ctx.readVaruint();
|
|
1908
|
-
if (major !== MAJOR_VERSION) {
|
|
1909
|
-
throw new UnsupportedVersionError(`unsupported .ots major version ${major}`);
|
|
1910
|
-
}
|
|
1911
|
-
const op = Op.deserialize(ctx);
|
|
1912
|
-
if (!(op instanceof CryptOp)) {
|
|
1913
|
-
throw new DeserializationError("file hash operation must be a cryptographic hash");
|
|
1914
|
-
}
|
|
1915
|
-
const fileHash = ctx.read(op.digestLength);
|
|
1916
|
-
const timestamp = Timestamp.deserialize(ctx, fileHash);
|
|
1917
|
-
ctx.assertEof();
|
|
1918
|
-
return new _DetachedTimestampFile(op, timestamp);
|
|
1919
|
-
}
|
|
1920
|
-
/** Crea un `.ots` nuevo hasheando el contenido completo de un fichero. */
|
|
1921
|
-
static fromBytes(fileHashOp, fileContent) {
|
|
1922
|
-
if (!(fileHashOp instanceof CryptOp)) {
|
|
1923
|
-
throw new TypeError("DetachedTimestampFile.fromBytes: fileHashOp must be a CryptOp");
|
|
1924
|
-
}
|
|
1925
|
-
const digest = fileHashOp.hashFile(fileContent);
|
|
1926
|
-
return new _DetachedTimestampFile(fileHashOp, new Timestamp(digest));
|
|
1927
|
-
}
|
|
1928
|
-
/** Crea un `.ots` nuevo a partir de un digest ya calculado del fichero. */
|
|
1929
|
-
static fromHash(fileHashOp, fileDigest) {
|
|
1930
|
-
return new _DetachedTimestampFile(fileHashOp, new Timestamp(fileDigest));
|
|
1931
|
-
}
|
|
1932
|
-
/** Igualdad estructural con otro fichero `.ots`. */
|
|
1933
|
-
equals(other) {
|
|
1934
|
-
return other instanceof _DetachedTimestampFile && this.fileHashOp.equals(other.fileHashOp) && this.timestamp.equals(other.timestamp);
|
|
1935
|
-
}
|
|
1936
|
-
};
|
|
496
|
+
// src/core/orchestration.ts
|
|
497
|
+
var import_core4 = require("@otskit/core");
|
|
1937
498
|
|
|
1938
499
|
// src/network/calendar.ts
|
|
500
|
+
var import_core2 = require("@otskit/core");
|
|
1939
501
|
var MAX_CALENDAR_RESPONSE_SIZE = 1e4;
|
|
1940
502
|
function assertCommitment(bytes) {
|
|
1941
503
|
if (!(bytes instanceof Uint8Array)) {
|
|
@@ -1961,7 +523,7 @@ var CalendarClient = class {
|
|
|
1961
523
|
url;
|
|
1962
524
|
networkLayer;
|
|
1963
525
|
logger;
|
|
1964
|
-
/**
|
|
526
|
+
/** Submits a digest to the calendar and returns the Timestamp that commits to it. */
|
|
1965
527
|
async submit(digest, signal) {
|
|
1966
528
|
assertCommitment(digest);
|
|
1967
529
|
this.logger?.debug(`Submitting digest to ${this.url}/digest`);
|
|
@@ -1972,10 +534,10 @@ var CalendarClient = class {
|
|
|
1972
534
|
);
|
|
1973
535
|
return this.#parseTimestamp(response.data, digest);
|
|
1974
536
|
}
|
|
1975
|
-
/**
|
|
537
|
+
/** Asks the calendar for a more complete Timestamp for `commitment` (upgrade). */
|
|
1976
538
|
async getTimestamp(commitment, signal) {
|
|
1977
539
|
assertCommitment(commitment);
|
|
1978
|
-
const path = `/timestamp/${bytesToHex(commitment)}`;
|
|
540
|
+
const path = `/timestamp/${(0, import_core2.bytesToHex)(commitment)}`;
|
|
1979
541
|
this.logger?.debug(`Querying ${this.url}${path}`);
|
|
1980
542
|
let response;
|
|
1981
543
|
try {
|
|
@@ -1994,15 +556,15 @@ var CalendarClient = class {
|
|
|
1994
556
|
}
|
|
1995
557
|
return this.#parseTimestamp(response.data, commitment);
|
|
1996
558
|
}
|
|
1997
|
-
/**
|
|
559
|
+
/** Deserializes the calendar response as a Timestamp committed to `commitment`. */
|
|
1998
560
|
#parseTimestamp(data, commitment) {
|
|
1999
561
|
if (data.length > MAX_CALENDAR_RESPONSE_SIZE) {
|
|
2000
562
|
throw new CalendarResponseTooLargeError(
|
|
2001
563
|
`calendar response of ${data.length} bytes exceeds limit ${MAX_CALENDAR_RESPONSE_SIZE}`
|
|
2002
564
|
);
|
|
2003
565
|
}
|
|
2004
|
-
const ctx = new StreamDeserializationContext(data);
|
|
2005
|
-
const timestamp = Timestamp.deserialize(ctx, commitment);
|
|
566
|
+
const ctx = new import_core2.StreamDeserializationContext(data);
|
|
567
|
+
const timestamp = import_core2.Timestamp.deserialize(ctx, commitment);
|
|
2006
568
|
ctx.assertEof();
|
|
2007
569
|
return timestamp;
|
|
2008
570
|
}
|
|
@@ -2024,7 +586,7 @@ function parseWhitelistPattern(raw) {
|
|
|
2024
586
|
hostname,
|
|
2025
587
|
port: parsed.port,
|
|
2026
588
|
pathname: parsed.pathname,
|
|
2027
|
-
wildcardSuffix
|
|
589
|
+
...wildcardSuffix !== void 0 ? { wildcardSuffix } : {}
|
|
2028
590
|
};
|
|
2029
591
|
}
|
|
2030
592
|
function hostnameMatchesPattern(hostname, pattern) {
|
|
@@ -2040,20 +602,26 @@ var UrlWhitelist = class {
|
|
|
2040
602
|
for (const u of urls) this.add(u);
|
|
2041
603
|
}
|
|
2042
604
|
}
|
|
2043
|
-
/**
|
|
605
|
+
/**
|
|
606
|
+
* Adds a pattern. If the URL has no scheme, both http:// and https:// variants are added.
|
|
607
|
+
* Throws TypeError if the pattern is not a valid string or is structurally invalid.
|
|
608
|
+
*/
|
|
2044
609
|
add(url) {
|
|
2045
610
|
if (typeof url !== "string") {
|
|
2046
611
|
throw new TypeError("UrlWhitelist: URL must be a string");
|
|
2047
612
|
}
|
|
2048
613
|
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
2049
614
|
const pattern = parseWhitelistPattern(url);
|
|
2050
|
-
if (pattern
|
|
615
|
+
if (pattern === void 0) {
|
|
616
|
+
throw new TypeError(`UrlWhitelist: invalid or unsupported pattern: "${url}"`);
|
|
617
|
+
}
|
|
618
|
+
this.#patterns.set(url, pattern);
|
|
2051
619
|
} else {
|
|
2052
620
|
this.add("http://" + url);
|
|
2053
621
|
this.add("https://" + url);
|
|
2054
622
|
}
|
|
2055
623
|
}
|
|
2056
|
-
/**
|
|
624
|
+
/** Returns true if `url` matches any pattern in the allowlist. */
|
|
2057
625
|
contains(url) {
|
|
2058
626
|
let parsed;
|
|
2059
627
|
try {
|
|
@@ -2075,27 +643,21 @@ var UrlWhitelist = class {
|
|
|
2075
643
|
return `UrlWhitelist([${[...this.#patterns.keys()].join(", ")}])`;
|
|
2076
644
|
}
|
|
2077
645
|
};
|
|
2078
|
-
var DEFAULT_CALENDAR_WHITELIST = new UrlWhitelist([
|
|
2079
|
-
|
|
2080
|
-
// Peter Todd
|
|
2081
|
-
"https://*.btc.calendar.opentimestamps.org",
|
|
2082
|
-
// Peter Todd Bitcoin calendars
|
|
2083
|
-
"https://*.calendar.eternitywall.com",
|
|
2084
|
-
// Eternity Wall
|
|
2085
|
-
"https://*.calendar.catallaxy.com"
|
|
2086
|
-
// Catallaxy
|
|
2087
|
-
]);
|
|
2088
|
-
var DEFAULT_AGGREGATORS = [
|
|
2089
|
-
"https://a.pool.opentimestamps.org",
|
|
2090
|
-
"https://b.pool.opentimestamps.org",
|
|
2091
|
-
"https://a.pool.eternitywall.com",
|
|
2092
|
-
"https://ots.btc.catallaxy.com"
|
|
2093
|
-
];
|
|
646
|
+
var DEFAULT_CALENDAR_WHITELIST = new UrlWhitelist([...import_core2.TRUSTED_CALENDAR_WHITELIST_PATTERNS]);
|
|
647
|
+
var DEFAULT_AGGREGATORS = [...import_core2.DEFAULT_AGGREGATOR_URLS];
|
|
2094
648
|
|
|
2095
649
|
// src/network/esplora.ts
|
|
650
|
+
var import_node_crypto = require("crypto");
|
|
651
|
+
var import_core3 = require("@otskit/core");
|
|
2096
652
|
var PUBLIC_ESPLORA_URL = "https://blockstream.info/api";
|
|
2097
653
|
var MAX_ESPLORA_RESPONSE_SIZE = 1e5;
|
|
654
|
+
var RAW_HEADER_SIZE = 80;
|
|
2098
655
|
var HEX64_RE = /^[0-9a-f]{64}$/i;
|
|
656
|
+
function sha256dDisplayHex(data) {
|
|
657
|
+
const first = (0, import_node_crypto.createHash)("sha256").update(data).digest();
|
|
658
|
+
const second = (0, import_node_crypto.createHash)("sha256").update(first).digest();
|
|
659
|
+
return Buffer.from(second).reverse().toString("hex");
|
|
660
|
+
}
|
|
2099
661
|
var EsploraClient = class {
|
|
2100
662
|
#url;
|
|
2101
663
|
#networkLayer;
|
|
@@ -2115,7 +677,7 @@ var EsploraClient = class {
|
|
|
2115
677
|
this.#url = raw.replace(/\/+$/, "");
|
|
2116
678
|
this.#logger = options.logger;
|
|
2117
679
|
}
|
|
2118
|
-
/**
|
|
680
|
+
/** Returns the block hash (64-char hex, lowercase) at the given height. */
|
|
2119
681
|
async blockHash(height, signal) {
|
|
2120
682
|
if (!Number.isSafeInteger(height) || height < 0) {
|
|
2121
683
|
throw new ValidationError(`block height must be a non-negative safe integer; got ${height}`);
|
|
@@ -2132,7 +694,7 @@ var EsploraClient = class {
|
|
|
2132
694
|
}
|
|
2133
695
|
return text.toLowerCase();
|
|
2134
696
|
}
|
|
2135
|
-
/**
|
|
697
|
+
/** Returns the block header (merkle root + timestamp) for the given hash. */
|
|
2136
698
|
async block(hash, signal) {
|
|
2137
699
|
if (typeof hash !== "string" || !HEX64_RE.test(hash)) {
|
|
2138
700
|
throw new ValidationError("block hash must be a 64-char hex string");
|
|
@@ -2150,7 +712,7 @@ var EsploraClient = class {
|
|
|
2150
712
|
} catch (err) {
|
|
2151
713
|
throw new EsploraResponseError("esplora returned a non-JSON block response", {
|
|
2152
714
|
/* v8 ignore next */
|
|
2153
|
-
|
|
715
|
+
...err instanceof Error ? { cause: err } : {}
|
|
2154
716
|
});
|
|
2155
717
|
}
|
|
2156
718
|
if (typeof body !== "object" || body === null) {
|
|
@@ -2165,7 +727,36 @@ var EsploraClient = class {
|
|
|
2165
727
|
}
|
|
2166
728
|
return { merkleroot, time };
|
|
2167
729
|
}
|
|
2168
|
-
/**
|
|
730
|
+
/**
|
|
731
|
+
* Fetches the raw 80-byte block header for `hash` and self-authenticates it:
|
|
732
|
+
* sha256d(rawHeader) reversed must equal `hash`. This removes trust in the explorer's
|
|
733
|
+
* JSON layer — the raw header is cryptographically bound to the block hash we requested.
|
|
734
|
+
*/
|
|
735
|
+
async rawBlockHeader(hash, signal) {
|
|
736
|
+
if (typeof hash !== "string" || !HEX64_RE.test(hash)) {
|
|
737
|
+
throw new ValidationError("block hash must be a 64-char hex string");
|
|
738
|
+
}
|
|
739
|
+
this.#logger?.debug(`Esplora raw header ${hash}`);
|
|
740
|
+
const response = await this.#networkLayer.request(
|
|
741
|
+
this.#url,
|
|
742
|
+
{ url: `${this.#url}/block/${hash}/header`, method: "GET", headers: { Accept: "application/octet-stream" } },
|
|
743
|
+
signal
|
|
744
|
+
);
|
|
745
|
+
const data = response.data;
|
|
746
|
+
if (data.length !== RAW_HEADER_SIZE) {
|
|
747
|
+
throw new EsploraResponseError(
|
|
748
|
+
`raw block header must be ${RAW_HEADER_SIZE} bytes; got ${data.length}`
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
const actualHash = sha256dDisplayHex(data);
|
|
752
|
+
if (actualHash !== hash.toLowerCase()) {
|
|
753
|
+
throw new EsploraResponseError(
|
|
754
|
+
`raw block header hash mismatch: expected ${hash.toLowerCase()}, got ${actualHash}`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
return data;
|
|
758
|
+
}
|
|
759
|
+
/** Decodes the response body as text, enforcing the size limit (fail-closed). */
|
|
2169
760
|
#decode(data) {
|
|
2170
761
|
if (data.length > MAX_ESPLORA_RESPONSE_SIZE) {
|
|
2171
762
|
throw new EsploraResponseError(
|
|
@@ -2176,22 +767,22 @@ var EsploraClient = class {
|
|
|
2176
767
|
return new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
2177
768
|
} catch (cause) {
|
|
2178
769
|
throw new EsploraResponseError("esplora response contains invalid UTF-8 bytes", {
|
|
2179
|
-
cause
|
|
770
|
+
...cause instanceof Error ? { cause } : {}
|
|
2180
771
|
});
|
|
2181
772
|
}
|
|
2182
773
|
}
|
|
2183
774
|
};
|
|
2184
775
|
async function verifyTimestampAttestation(digest, attestation, explorer, signal) {
|
|
2185
776
|
if (attestation.kind !== "bitcoin" && attestation.kind !== "litecoin") {
|
|
2186
|
-
throw new VerificationError(`cannot verify a '${attestation.kind}' attestation against the chain`);
|
|
777
|
+
throw new import_core3.VerificationError(`cannot verify a '${attestation.kind}' attestation against the chain`);
|
|
2187
778
|
}
|
|
2188
779
|
const hash = await explorer.blockHash(attestation.height, signal);
|
|
2189
|
-
const
|
|
2190
|
-
return
|
|
780
|
+
const rawHeader = await explorer.rawBlockHeader(hash, signal);
|
|
781
|
+
return (0, import_core3.verifyAgainstRawHeader)(digest, rawHeader);
|
|
2191
782
|
}
|
|
2192
783
|
|
|
2193
784
|
// src/core/orchestration.ts
|
|
2194
|
-
var
|
|
785
|
+
var import_node_crypto2 = require("crypto");
|
|
2195
786
|
|
|
2196
787
|
// src/security/ssrf.ts
|
|
2197
788
|
var import_promises = require("dns/promises");
|
|
@@ -2314,7 +905,7 @@ function secureNonce(n) {
|
|
|
2314
905
|
}
|
|
2315
906
|
function timingSafeEq(a, b) {
|
|
2316
907
|
if (a.length !== b.length) return false;
|
|
2317
|
-
return (0,
|
|
908
|
+
return (0, import_node_crypto2.timingSafeEqual)(a, b);
|
|
2318
909
|
}
|
|
2319
910
|
var bytesEqFast = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)) === 0;
|
|
2320
911
|
async function orchestrateStamp(hash, calendars, networkLayer, logger, signal, minimumSuccessfulSubmissions = 2, allowPrivateCalendars = false) {
|
|
@@ -2334,10 +925,10 @@ async function orchestrateStamp(hash, calendars, networkLayer, logger, signal, m
|
|
|
2334
925
|
);
|
|
2335
926
|
const digest = validateHash(hash);
|
|
2336
927
|
logger?.info(`Starting stamp for ${Buffer.from(digest).toString("hex")}`);
|
|
2337
|
-
const detached = DetachedTimestampFile.fromHash(new OpSHA256(), digest);
|
|
2338
|
-
const nonceAppended = detached.timestamp.add(new OpAppend(secureNonce(16)));
|
|
2339
|
-
const merkleRoot = nonceAppended.add(new OpSHA256());
|
|
2340
|
-
const merkleTip = makeMerkleTree([merkleRoot]);
|
|
928
|
+
const detached = import_core4.DetachedTimestampFile.fromHash(new import_core4.OpSHA256(), digest);
|
|
929
|
+
const nonceAppended = detached.timestamp.add(new import_core4.OpAppend(secureNonce(16)));
|
|
930
|
+
const merkleRoot = nonceAppended.add(new import_core4.OpSHA256());
|
|
931
|
+
const merkleTip = (0, import_core4.makeMerkleTree)([merkleRoot]);
|
|
2341
932
|
const results = await Promise.allSettled(
|
|
2342
933
|
calendars.map((url) => new CalendarClient(url, networkLayer, logger).submit(merkleTip.getDigest(), signal))
|
|
2343
934
|
);
|
|
@@ -2367,11 +958,11 @@ async function orchestrateStamp(hash, calendars, networkLayer, logger, signal, m
|
|
|
2367
958
|
async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, logger, signal) {
|
|
2368
959
|
let detached;
|
|
2369
960
|
try {
|
|
2370
|
-
detached = DetachedTimestampFile.deserialize(new Uint8Array(incompleteProof));
|
|
961
|
+
detached = import_core4.DetachedTimestampFile.deserialize(new Uint8Array(incompleteProof));
|
|
2371
962
|
} catch (error) {
|
|
2372
963
|
throw new ValidationError("Invalid .ots proof format", {
|
|
2373
964
|
/* v8 ignore next */
|
|
2374
|
-
|
|
965
|
+
...error instanceof Error ? { cause: error } : {}
|
|
2375
966
|
});
|
|
2376
967
|
}
|
|
2377
968
|
if (detached.timestamp.isTimestampComplete()) {
|
|
@@ -2408,15 +999,21 @@ async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, log
|
|
|
2408
999
|
}
|
|
2409
1000
|
return Buffer.from(after);
|
|
2410
1001
|
}
|
|
2411
|
-
async function orchestrateVerify(proof, networkLayer, originalDataHash, logger, signal) {
|
|
1002
|
+
async function orchestrateVerify(proof, networkLayer, originalDataHash, logger, signal, esploraUrl) {
|
|
2412
1003
|
let detached;
|
|
2413
1004
|
try {
|
|
2414
|
-
detached = DetachedTimestampFile.deserialize(new Uint8Array(proof));
|
|
1005
|
+
detached = import_core4.DetachedTimestampFile.deserialize(new Uint8Array(proof));
|
|
2415
1006
|
} catch (cause) {
|
|
2416
1007
|
throw new ValidationError("Invalid .ots proof format", {
|
|
2417
|
-
cause
|
|
1008
|
+
...cause instanceof Error ? { cause } : {}
|
|
2418
1009
|
});
|
|
2419
1010
|
}
|
|
1011
|
+
if (detached.fileHashOp instanceof import_core4.OpSHA1 || detached.fileHashOp instanceof import_core4.OpRIPEMD160) {
|
|
1012
|
+
return {
|
|
1013
|
+
status: "invalid",
|
|
1014
|
+
reason: `This proof uses ${detached.fileHashOp.tagName} (a weak hash algorithm). Re-stamp the original file with SHA-256 to get a verifiable proof.`
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
2420
1017
|
if (originalDataHash !== void 0) {
|
|
2421
1018
|
let expected;
|
|
2422
1019
|
try {
|
|
@@ -2424,7 +1021,7 @@ async function orchestrateVerify(proof, networkLayer, originalDataHash, logger,
|
|
|
2424
1021
|
} catch (err) {
|
|
2425
1022
|
throw new ValidationError(
|
|
2426
1023
|
err instanceof Error ? err.message : "Invalid hash format",
|
|
2427
|
-
{
|
|
1024
|
+
{ ...err instanceof Error ? { cause: err } : {} }
|
|
2428
1025
|
);
|
|
2429
1026
|
}
|
|
2430
1027
|
if (!timingSafeEq(expected, detached.fileDigest())) {
|
|
@@ -2455,7 +1052,10 @@ async function orchestrateVerify(proof, networkLayer, originalDataHash, logger,
|
|
|
2455
1052
|
reason: hasLitecoin ? "Litecoin-only attestation is not supported by this client" : "No Bitcoin attestation found \u2014 timestamp not yet confirmed"
|
|
2456
1053
|
};
|
|
2457
1054
|
}
|
|
2458
|
-
const explorer = new EsploraClient(networkLayer
|
|
1055
|
+
const explorer = new EsploraClient(networkLayer, {
|
|
1056
|
+
...esploraUrl !== void 0 ? { url: esploraUrl } : {},
|
|
1057
|
+
...logger !== void 0 ? { logger } : {}
|
|
1058
|
+
});
|
|
2459
1059
|
let lastNetworkError;
|
|
2460
1060
|
let lastCryptoError;
|
|
2461
1061
|
for (const { msg, attestation } of bitcoinAtts) {
|
|
@@ -2497,20 +1097,32 @@ var OpenTimestampsClient = class {
|
|
|
2497
1097
|
globalSignal;
|
|
2498
1098
|
minimumSuccessfulSubmissions;
|
|
2499
1099
|
allowPrivateCalendars;
|
|
1100
|
+
esploraUrl;
|
|
2500
1101
|
/**
|
|
2501
1102
|
* Create a new OpenTimestamps client
|
|
2502
1103
|
*
|
|
2503
1104
|
* @param options Client configuration options
|
|
2504
1105
|
*/
|
|
2505
1106
|
constructor(options = {}) {
|
|
1107
|
+
this.logger = options.logger;
|
|
2506
1108
|
if (!options.calendars || options.calendars.length === 0) {
|
|
2507
1109
|
this.calendars = DEFAULT_CALENDARS;
|
|
2508
1110
|
this.logger?.info("No calendars provided, using defaults");
|
|
2509
1111
|
} else {
|
|
2510
1112
|
this.calendars = options.calendars;
|
|
2511
1113
|
}
|
|
2512
|
-
|
|
1114
|
+
const minSubs = options.minimumSuccessfulSubmissions ?? 2;
|
|
1115
|
+
if (!Number.isInteger(minSubs) || minSubs < 1) {
|
|
1116
|
+
throw new ValidationError("minimumSuccessfulSubmissions must be an integer >= 1");
|
|
1117
|
+
}
|
|
1118
|
+
if (minSubs > this.calendars.length) {
|
|
1119
|
+
throw new ValidationError(
|
|
1120
|
+
`minimumSuccessfulSubmissions (${minSubs}) cannot exceed the number of calendars (${this.calendars.length})`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
this.minimumSuccessfulSubmissions = minSubs;
|
|
2513
1124
|
this.allowPrivateCalendars = options.allowPrivateCalendars ?? false;
|
|
1125
|
+
this.esploraUrl = options.esploraUrl;
|
|
2514
1126
|
const resilienceConfig = {
|
|
2515
1127
|
...DEFAULT_RESILIENCE,
|
|
2516
1128
|
...options.resilience,
|
|
@@ -2527,8 +1139,7 @@ var OpenTimestampsClient = class {
|
|
|
2527
1139
|
...options.resilience?.circuitBreaker
|
|
2528
1140
|
}
|
|
2529
1141
|
};
|
|
2530
|
-
this.
|
|
2531
|
-
this.globalSignal = options.signal;
|
|
1142
|
+
if (options.signal !== void 0) this.globalSignal = options.signal;
|
|
2532
1143
|
const internalOptions = options;
|
|
2533
1144
|
this.networkLayer = internalOptions._networkLayer ?? new ResilientNetworkLayer(resilienceConfig, this.logger);
|
|
2534
1145
|
this.logger?.info(`OpenTimestamps client initialized with ${this.calendars.length} calendars`);
|
|
@@ -2613,7 +1224,7 @@ var OpenTimestampsClient = class {
|
|
|
2613
1224
|
* ```
|
|
2614
1225
|
*/
|
|
2615
1226
|
async verify(proof, originalDataHash) {
|
|
2616
|
-
return orchestrateVerify(proof, this.networkLayer, originalDataHash, this.logger, this.globalSignal);
|
|
1227
|
+
return orchestrateVerify(proof, this.networkLayer, originalDataHash, this.logger, this.globalSignal, this.esploraUrl);
|
|
2617
1228
|
}
|
|
2618
1229
|
/**
|
|
2619
1230
|
* Get the current state of the circuit breaker for a calendar
|
|
@@ -2645,6 +1256,10 @@ var OpenTimestampsClient = class {
|
|
|
2645
1256
|
}
|
|
2646
1257
|
};
|
|
2647
1258
|
|
|
1259
|
+
// src/index.ts
|
|
1260
|
+
var import_core5 = require("@otskit/core");
|
|
1261
|
+
var import_core6 = require("@otskit/core");
|
|
1262
|
+
|
|
2648
1263
|
// src/utils/hash.ts
|
|
2649
1264
|
var import_crypto = require("crypto");
|
|
2650
1265
|
var import_fs = require("fs");
|