@otskit/client 0.4.0 → 0.6.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 +9 -7
- package/dist/browser.d.ts +36 -0
- package/dist/browser.js +742 -0
- package/dist/index.cjs +378 -267
- package/dist/index.d.cts +71 -56
- package/dist/index.d.ts +71 -56
- package/dist/index.js +374 -268
- package/package.json +18 -8
package/dist/index.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ export { Attestation, BitcoinAttestation, DetachedTimestampFile, PendingAttestat
|
|
|
4
4
|
/**
|
|
5
5
|
* Type definitions for the OpenTimestamps Client SDK
|
|
6
6
|
*/
|
|
7
|
-
/** Logger interface for observability */
|
|
7
|
+
/** Logger interface for observability. */
|
|
8
8
|
interface Logger {
|
|
9
9
|
debug(message: string, ...args: unknown[]): void;
|
|
10
10
|
info(message: string, ...args: unknown[]): void;
|
|
@@ -39,7 +39,7 @@ interface ResilienceOptions {
|
|
|
39
39
|
connectTimeoutMs: number;
|
|
40
40
|
retries: RetryOptions;
|
|
41
41
|
circuitBreaker: CircuitBreakerOptions;
|
|
42
|
-
/**
|
|
42
|
+
/** Maximum bytes allowed in the response body. Defaults to 100 KB. */
|
|
43
43
|
maxResponseBytes?: number;
|
|
44
44
|
}
|
|
45
45
|
/** Client configuration options */
|
|
@@ -55,12 +55,18 @@ interface ClientOptions {
|
|
|
55
55
|
/** Minimum successful calendar submissions required (default: 2) */
|
|
56
56
|
minimumSuccessfulSubmissions?: number;
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* Base URL for the Esplora block explorer used by verify().
|
|
59
|
+
* Defaults to Blockstream (https://blockstream.info/api).
|
|
60
|
+
* Override to use a self-hosted or alternative explorer.
|
|
61
|
+
*/
|
|
62
|
+
esploraUrl?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Allows calendar URLs that resolve to private/reserved IPs.
|
|
59
65
|
*
|
|
60
|
-
* **
|
|
66
|
+
* **Do not enable in production.** Useful for local testing or corporate networks.
|
|
61
67
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
68
|
+
* Even when `false`, protection is best-effort against DNS rebinding (TOCTOU).
|
|
69
|
+
* For high-security environments, complement with network-level egress filtering.
|
|
64
70
|
*
|
|
65
71
|
* @default false
|
|
66
72
|
*/
|
|
@@ -133,12 +139,13 @@ declare const DEFAULT_RESILIENCE: ResilienceOptions;
|
|
|
133
139
|
* ```
|
|
134
140
|
*/
|
|
135
141
|
declare class OpenTimestampsClient {
|
|
136
|
-
private calendars;
|
|
137
|
-
private networkLayer;
|
|
138
|
-
private logger?;
|
|
139
|
-
private globalSignal?;
|
|
140
|
-
private minimumSuccessfulSubmissions;
|
|
141
|
-
private allowPrivateCalendars;
|
|
142
|
+
private readonly calendars;
|
|
143
|
+
private readonly networkLayer;
|
|
144
|
+
private readonly logger?;
|
|
145
|
+
private readonly globalSignal?;
|
|
146
|
+
private readonly minimumSuccessfulSubmissions;
|
|
147
|
+
private readonly allowPrivateCalendars;
|
|
148
|
+
private readonly esploraUrl;
|
|
142
149
|
/**
|
|
143
150
|
* Create a new OpenTimestamps client
|
|
144
151
|
*
|
|
@@ -181,7 +188,7 @@ declare class OpenTimestampsClient {
|
|
|
181
188
|
* const upgradedProof = await client.upgrade(incompleteProof)
|
|
182
189
|
*
|
|
183
190
|
* // If upgrade throws UpgradeError, Bitcoin hasn't confirmed yet
|
|
184
|
-
* // Retry later (typically
|
|
191
|
+
* // Retry later (typically ~60 minutes after stamp)
|
|
185
192
|
* ```
|
|
186
193
|
*/
|
|
187
194
|
upgrade(incompleteProof: Buffer, options?: OperationOptions): Promise<Buffer>;
|
|
@@ -244,7 +251,7 @@ declare class ValidationError extends OpenTimestampsClientError {
|
|
|
244
251
|
declare class StampError extends OpenTimestampsClientError {
|
|
245
252
|
readonly successfulSubmissions: Array<{
|
|
246
253
|
calendar: string;
|
|
247
|
-
proof?:
|
|
254
|
+
proof?: Uint8Array;
|
|
248
255
|
}>;
|
|
249
256
|
readonly failedSubmissions: Array<{
|
|
250
257
|
calendar: string;
|
|
@@ -252,7 +259,7 @@ declare class StampError extends OpenTimestampsClientError {
|
|
|
252
259
|
}>;
|
|
253
260
|
constructor(message: string, successful: Array<{
|
|
254
261
|
calendar: string;
|
|
255
|
-
proof?:
|
|
262
|
+
proof?: Uint8Array;
|
|
256
263
|
}>, failed: Array<{
|
|
257
264
|
calendar: string;
|
|
258
265
|
error: Error;
|
|
@@ -265,7 +272,7 @@ declare class UpgradeError extends OpenTimestampsClientError {
|
|
|
265
272
|
}
|
|
266
273
|
/** Network-related error (timeout, all retries failed, etc.) */
|
|
267
274
|
declare class NetworkError extends OpenTimestampsClientError {
|
|
268
|
-
/** HTTP status code
|
|
275
|
+
/** HTTP status code when the failure originates from an HTTP response. */
|
|
269
276
|
readonly status?: number;
|
|
270
277
|
constructor(message: string, options?: {
|
|
271
278
|
cause?: Error;
|
|
@@ -276,16 +283,16 @@ declare class NetworkError extends OpenTimestampsClientError {
|
|
|
276
283
|
declare class CircuitBreakerError extends NetworkError {
|
|
277
284
|
constructor(calendar: string);
|
|
278
285
|
}
|
|
279
|
-
/**
|
|
286
|
+
/** The calendar does not yet know the queried commitment (HTTP 404). */
|
|
280
287
|
declare class CommitmentNotFoundError extends NetworkError {
|
|
281
288
|
}
|
|
282
|
-
/**
|
|
289
|
+
/** The calendar response exceeds the allowed size limit (DoS defense). */
|
|
283
290
|
declare class CalendarResponseTooLargeError extends NetworkError {
|
|
284
291
|
}
|
|
285
|
-
/**
|
|
292
|
+
/** Invalid Esplora response: empty, non-JSON, malformed, or too large (DoS defense). */
|
|
286
293
|
declare class EsploraResponseError extends NetworkError {
|
|
287
294
|
}
|
|
288
|
-
/**
|
|
295
|
+
/** Response exceeds the allowed byte limit (DoS defense). */
|
|
289
296
|
declare class SizeLimitExceededError extends NetworkError {
|
|
290
297
|
readonly maxBytes: number;
|
|
291
298
|
readonly actualBytes?: number;
|
|
@@ -306,8 +313,7 @@ declare enum CircuitState {
|
|
|
306
313
|
}
|
|
307
314
|
|
|
308
315
|
/**
|
|
309
|
-
*
|
|
310
|
-
* Funciona en Node.js 18+, browsers y edge runtimes.
|
|
316
|
+
* Fetch adapter for Node.js 20+.
|
|
311
317
|
*/
|
|
312
318
|
interface FetchRequest {
|
|
313
319
|
url: string;
|
|
@@ -324,9 +330,9 @@ interface FetchResponse {
|
|
|
324
330
|
}
|
|
325
331
|
|
|
326
332
|
declare class ResilientNetworkLayer {
|
|
327
|
-
private options;
|
|
328
|
-
private logger?;
|
|
329
|
-
private circuitBreaker;
|
|
333
|
+
private readonly options;
|
|
334
|
+
private readonly logger?;
|
|
335
|
+
private readonly circuitBreaker;
|
|
330
336
|
constructor(options: ResilienceOptions, logger?: Logger | undefined);
|
|
331
337
|
/**
|
|
332
338
|
* Execute a request with full resilience pipeline
|
|
@@ -341,30 +347,33 @@ declare class ResilientNetworkLayer {
|
|
|
341
347
|
}
|
|
342
348
|
|
|
343
349
|
/**
|
|
344
|
-
*
|
|
350
|
+
* Remote OpenTimestamps calendar client (real OTS protocol).
|
|
345
351
|
*/
|
|
346
352
|
|
|
347
|
-
/**
|
|
353
|
+
/** Maximum response size from a calendar server (DoS defense). */
|
|
348
354
|
declare const MAX_CALENDAR_RESPONSE_SIZE = 10000;
|
|
349
|
-
/**
|
|
355
|
+
/** Interface to a remote calendar server. */
|
|
350
356
|
declare class CalendarClient {
|
|
351
357
|
#private;
|
|
352
358
|
private readonly url;
|
|
353
359
|
private readonly networkLayer;
|
|
354
360
|
private readonly logger?;
|
|
355
361
|
constructor(url: string, networkLayer: ResilientNetworkLayer, logger?: Logger | undefined);
|
|
356
|
-
/**
|
|
362
|
+
/** Submits a digest to the calendar and returns the Timestamp that commits to it. */
|
|
357
363
|
submit(digest: Uint8Array, signal?: AbortSignal): Promise<Timestamp>;
|
|
358
|
-
/**
|
|
364
|
+
/** Asks the calendar for a more complete Timestamp for `commitment` (upgrade). */
|
|
359
365
|
getTimestamp(commitment: Uint8Array, signal?: AbortSignal): Promise<Timestamp>;
|
|
360
366
|
}
|
|
361
|
-
/**
|
|
367
|
+
/** Allowlist of trusted calendar URLs. */
|
|
362
368
|
declare class UrlWhitelist {
|
|
363
369
|
#private;
|
|
364
370
|
constructor(urls?: readonly string[]);
|
|
365
|
-
/**
|
|
371
|
+
/**
|
|
372
|
+
* Adds a pattern. If the URL has no scheme, both http:// and https:// variants are added.
|
|
373
|
+
* Throws TypeError if the pattern is not a valid string or is structurally invalid.
|
|
374
|
+
*/
|
|
366
375
|
add(url: string): void;
|
|
367
|
-
/**
|
|
376
|
+
/** Returns true if `url` matches any pattern in the allowlist. */
|
|
368
377
|
contains(url: string): boolean;
|
|
369
378
|
toString(): string;
|
|
370
379
|
}
|
|
@@ -379,32 +388,38 @@ declare const DEFAULT_CALENDAR_WHITELIST: UrlWhitelist;
|
|
|
379
388
|
*/
|
|
380
389
|
declare const DEFAULT_AGGREGATORS: readonly string[];
|
|
381
390
|
|
|
382
|
-
/**
|
|
391
|
+
/** Default public Esplora explorer (Bitcoin mainnet). */
|
|
383
392
|
declare const PUBLIC_ESPLORA_URL = "https://blockstream.info/api";
|
|
384
|
-
/**
|
|
393
|
+
/** Maximum size of an Esplora response (DoS defense). A JSON block header is a few hundred bytes. */
|
|
385
394
|
declare const MAX_ESPLORA_RESPONSE_SIZE = 100000;
|
|
386
395
|
interface EsploraClientOptions {
|
|
387
|
-
/** URL
|
|
396
|
+
/** Base URL of the explorer (defaults to Blockstream). Useful for pointing to a Litecoin Esplora. */
|
|
388
397
|
url?: string;
|
|
389
398
|
logger?: Logger;
|
|
390
399
|
}
|
|
391
|
-
/**
|
|
400
|
+
/** Client for a remote Esplora explorer. */
|
|
392
401
|
declare class EsploraClient {
|
|
393
402
|
#private;
|
|
394
403
|
constructor(networkLayer: ResilientNetworkLayer, options?: EsploraClientOptions);
|
|
395
|
-
/**
|
|
404
|
+
/** Returns the block hash (64-char hex, lowercase) at the given height. */
|
|
396
405
|
blockHash(height: number, signal?: AbortSignal): Promise<string>;
|
|
397
|
-
/**
|
|
406
|
+
/** Returns the block header (merkle root + timestamp) for the given hash. */
|
|
398
407
|
block(hash: string, signal?: AbortSignal): Promise<BlockHeader>;
|
|
408
|
+
/**
|
|
409
|
+
* Fetches the raw 80-byte block header for `hash` and self-authenticates it:
|
|
410
|
+
* sha256d(rawHeader) reversed must equal `hash`. This removes trust in the explorer's
|
|
411
|
+
* JSON layer — the raw header is cryptographically bound to the block hash we requested.
|
|
412
|
+
*/
|
|
413
|
+
rawBlockHeader(hash: string, signal?: AbortSignal): Promise<Uint8Array>;
|
|
399
414
|
}
|
|
400
415
|
/**
|
|
401
|
-
*
|
|
416
|
+
* Verifies a Bitcoin/Litecoin attestation against the corresponding block header.
|
|
402
417
|
*
|
|
403
|
-
* `digest`
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
418
|
+
* `digest` is the final tree commitment at the attestation point (32 bytes, must equal the
|
|
419
|
+
* block's merkle root). `explorer` must point to the correct chain (Blockstream for Bitcoin;
|
|
420
|
+
* a Litecoin Esplora for Litecoin). Returns the block time (epoch seconds) on success;
|
|
421
|
+
* throws `VerificationError` if the digest does not match or the attestation is not
|
|
422
|
+
* on-chain verifiable (`pending`/`unknown`). Fail-closed.
|
|
408
423
|
*/
|
|
409
424
|
declare function verifyTimestampAttestation(digest: Uint8Array, attestation: Attestation, explorer: EsploraClient, signal?: AbortSignal): Promise<number>;
|
|
410
425
|
|
|
@@ -412,21 +427,21 @@ declare function hashBuffer(data: Buffer | Uint8Array): Buffer;
|
|
|
412
427
|
declare function hashFile(path: string): Promise<Buffer>;
|
|
413
428
|
|
|
414
429
|
/**
|
|
415
|
-
* SSRF protection
|
|
430
|
+
* SSRF protection for user-configurable calendar URLs.
|
|
416
431
|
*
|
|
417
|
-
*
|
|
418
|
-
* - TOCTOU/DNS rebinding:
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
* - IPv4-mapped IPv6 (::ffff:x.x.x.x):
|
|
422
|
-
*
|
|
432
|
+
* KNOWN LIMITATIONS (documented intentionally):
|
|
433
|
+
* - TOCTOU / DNS rebinding: DNS validation happens BEFORE the connection.
|
|
434
|
+
* A server with TTL=0 can change its IP between validation and the actual fetch.
|
|
435
|
+
* Real mitigation requires network-level egress filtering.
|
|
436
|
+
* - IPv4-mapped IPv6 (::ffff:x.x.x.x): the ::ffff: prefix is blocked but the
|
|
437
|
+
* IPv4 component validation depends on the format Node.js returns.
|
|
423
438
|
*/
|
|
424
439
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
440
|
+
* Validates that a calendar URL is safe for outbound HTTP requests.
|
|
441
|
+
* Blocks private/reserved IPs by default.
|
|
427
442
|
*
|
|
428
|
-
* @param allowPrivate
|
|
429
|
-
*
|
|
443
|
+
* @param allowPrivate When true, skips the IP range check.
|
|
444
|
+
* Useful for local testing or internal corporate networks.
|
|
430
445
|
*/
|
|
431
446
|
declare function assertSafeCalendarUrl(url: string, options: {
|
|
432
447
|
allowPrivate: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { Attestation, BitcoinAttestation, DetachedTimestampFile, PendingAttestat
|
|
|
4
4
|
/**
|
|
5
5
|
* Type definitions for the OpenTimestamps Client SDK
|
|
6
6
|
*/
|
|
7
|
-
/** Logger interface for observability */
|
|
7
|
+
/** Logger interface for observability. */
|
|
8
8
|
interface Logger {
|
|
9
9
|
debug(message: string, ...args: unknown[]): void;
|
|
10
10
|
info(message: string, ...args: unknown[]): void;
|
|
@@ -39,7 +39,7 @@ interface ResilienceOptions {
|
|
|
39
39
|
connectTimeoutMs: number;
|
|
40
40
|
retries: RetryOptions;
|
|
41
41
|
circuitBreaker: CircuitBreakerOptions;
|
|
42
|
-
/**
|
|
42
|
+
/** Maximum bytes allowed in the response body. Defaults to 100 KB. */
|
|
43
43
|
maxResponseBytes?: number;
|
|
44
44
|
}
|
|
45
45
|
/** Client configuration options */
|
|
@@ -55,12 +55,18 @@ interface ClientOptions {
|
|
|
55
55
|
/** Minimum successful calendar submissions required (default: 2) */
|
|
56
56
|
minimumSuccessfulSubmissions?: number;
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* Base URL for the Esplora block explorer used by verify().
|
|
59
|
+
* Defaults to Blockstream (https://blockstream.info/api).
|
|
60
|
+
* Override to use a self-hosted or alternative explorer.
|
|
61
|
+
*/
|
|
62
|
+
esploraUrl?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Allows calendar URLs that resolve to private/reserved IPs.
|
|
59
65
|
*
|
|
60
|
-
* **
|
|
66
|
+
* **Do not enable in production.** Useful for local testing or corporate networks.
|
|
61
67
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
68
|
+
* Even when `false`, protection is best-effort against DNS rebinding (TOCTOU).
|
|
69
|
+
* For high-security environments, complement with network-level egress filtering.
|
|
64
70
|
*
|
|
65
71
|
* @default false
|
|
66
72
|
*/
|
|
@@ -133,12 +139,13 @@ declare const DEFAULT_RESILIENCE: ResilienceOptions;
|
|
|
133
139
|
* ```
|
|
134
140
|
*/
|
|
135
141
|
declare class OpenTimestampsClient {
|
|
136
|
-
private calendars;
|
|
137
|
-
private networkLayer;
|
|
138
|
-
private logger?;
|
|
139
|
-
private globalSignal?;
|
|
140
|
-
private minimumSuccessfulSubmissions;
|
|
141
|
-
private allowPrivateCalendars;
|
|
142
|
+
private readonly calendars;
|
|
143
|
+
private readonly networkLayer;
|
|
144
|
+
private readonly logger?;
|
|
145
|
+
private readonly globalSignal?;
|
|
146
|
+
private readonly minimumSuccessfulSubmissions;
|
|
147
|
+
private readonly allowPrivateCalendars;
|
|
148
|
+
private readonly esploraUrl;
|
|
142
149
|
/**
|
|
143
150
|
* Create a new OpenTimestamps client
|
|
144
151
|
*
|
|
@@ -181,7 +188,7 @@ declare class OpenTimestampsClient {
|
|
|
181
188
|
* const upgradedProof = await client.upgrade(incompleteProof)
|
|
182
189
|
*
|
|
183
190
|
* // If upgrade throws UpgradeError, Bitcoin hasn't confirmed yet
|
|
184
|
-
* // Retry later (typically
|
|
191
|
+
* // Retry later (typically ~60 minutes after stamp)
|
|
185
192
|
* ```
|
|
186
193
|
*/
|
|
187
194
|
upgrade(incompleteProof: Buffer, options?: OperationOptions): Promise<Buffer>;
|
|
@@ -244,7 +251,7 @@ declare class ValidationError extends OpenTimestampsClientError {
|
|
|
244
251
|
declare class StampError extends OpenTimestampsClientError {
|
|
245
252
|
readonly successfulSubmissions: Array<{
|
|
246
253
|
calendar: string;
|
|
247
|
-
proof?:
|
|
254
|
+
proof?: Uint8Array;
|
|
248
255
|
}>;
|
|
249
256
|
readonly failedSubmissions: Array<{
|
|
250
257
|
calendar: string;
|
|
@@ -252,7 +259,7 @@ declare class StampError extends OpenTimestampsClientError {
|
|
|
252
259
|
}>;
|
|
253
260
|
constructor(message: string, successful: Array<{
|
|
254
261
|
calendar: string;
|
|
255
|
-
proof?:
|
|
262
|
+
proof?: Uint8Array;
|
|
256
263
|
}>, failed: Array<{
|
|
257
264
|
calendar: string;
|
|
258
265
|
error: Error;
|
|
@@ -265,7 +272,7 @@ declare class UpgradeError extends OpenTimestampsClientError {
|
|
|
265
272
|
}
|
|
266
273
|
/** Network-related error (timeout, all retries failed, etc.) */
|
|
267
274
|
declare class NetworkError extends OpenTimestampsClientError {
|
|
268
|
-
/** HTTP status code
|
|
275
|
+
/** HTTP status code when the failure originates from an HTTP response. */
|
|
269
276
|
readonly status?: number;
|
|
270
277
|
constructor(message: string, options?: {
|
|
271
278
|
cause?: Error;
|
|
@@ -276,16 +283,16 @@ declare class NetworkError extends OpenTimestampsClientError {
|
|
|
276
283
|
declare class CircuitBreakerError extends NetworkError {
|
|
277
284
|
constructor(calendar: string);
|
|
278
285
|
}
|
|
279
|
-
/**
|
|
286
|
+
/** The calendar does not yet know the queried commitment (HTTP 404). */
|
|
280
287
|
declare class CommitmentNotFoundError extends NetworkError {
|
|
281
288
|
}
|
|
282
|
-
/**
|
|
289
|
+
/** The calendar response exceeds the allowed size limit (DoS defense). */
|
|
283
290
|
declare class CalendarResponseTooLargeError extends NetworkError {
|
|
284
291
|
}
|
|
285
|
-
/**
|
|
292
|
+
/** Invalid Esplora response: empty, non-JSON, malformed, or too large (DoS defense). */
|
|
286
293
|
declare class EsploraResponseError extends NetworkError {
|
|
287
294
|
}
|
|
288
|
-
/**
|
|
295
|
+
/** Response exceeds the allowed byte limit (DoS defense). */
|
|
289
296
|
declare class SizeLimitExceededError extends NetworkError {
|
|
290
297
|
readonly maxBytes: number;
|
|
291
298
|
readonly actualBytes?: number;
|
|
@@ -306,8 +313,7 @@ declare enum CircuitState {
|
|
|
306
313
|
}
|
|
307
314
|
|
|
308
315
|
/**
|
|
309
|
-
*
|
|
310
|
-
* Funciona en Node.js 18+, browsers y edge runtimes.
|
|
316
|
+
* Fetch adapter for Node.js 20+.
|
|
311
317
|
*/
|
|
312
318
|
interface FetchRequest {
|
|
313
319
|
url: string;
|
|
@@ -324,9 +330,9 @@ interface FetchResponse {
|
|
|
324
330
|
}
|
|
325
331
|
|
|
326
332
|
declare class ResilientNetworkLayer {
|
|
327
|
-
private options;
|
|
328
|
-
private logger?;
|
|
329
|
-
private circuitBreaker;
|
|
333
|
+
private readonly options;
|
|
334
|
+
private readonly logger?;
|
|
335
|
+
private readonly circuitBreaker;
|
|
330
336
|
constructor(options: ResilienceOptions, logger?: Logger | undefined);
|
|
331
337
|
/**
|
|
332
338
|
* Execute a request with full resilience pipeline
|
|
@@ -341,30 +347,33 @@ declare class ResilientNetworkLayer {
|
|
|
341
347
|
}
|
|
342
348
|
|
|
343
349
|
/**
|
|
344
|
-
*
|
|
350
|
+
* Remote OpenTimestamps calendar client (real OTS protocol).
|
|
345
351
|
*/
|
|
346
352
|
|
|
347
|
-
/**
|
|
353
|
+
/** Maximum response size from a calendar server (DoS defense). */
|
|
348
354
|
declare const MAX_CALENDAR_RESPONSE_SIZE = 10000;
|
|
349
|
-
/**
|
|
355
|
+
/** Interface to a remote calendar server. */
|
|
350
356
|
declare class CalendarClient {
|
|
351
357
|
#private;
|
|
352
358
|
private readonly url;
|
|
353
359
|
private readonly networkLayer;
|
|
354
360
|
private readonly logger?;
|
|
355
361
|
constructor(url: string, networkLayer: ResilientNetworkLayer, logger?: Logger | undefined);
|
|
356
|
-
/**
|
|
362
|
+
/** Submits a digest to the calendar and returns the Timestamp that commits to it. */
|
|
357
363
|
submit(digest: Uint8Array, signal?: AbortSignal): Promise<Timestamp>;
|
|
358
|
-
/**
|
|
364
|
+
/** Asks the calendar for a more complete Timestamp for `commitment` (upgrade). */
|
|
359
365
|
getTimestamp(commitment: Uint8Array, signal?: AbortSignal): Promise<Timestamp>;
|
|
360
366
|
}
|
|
361
|
-
/**
|
|
367
|
+
/** Allowlist of trusted calendar URLs. */
|
|
362
368
|
declare class UrlWhitelist {
|
|
363
369
|
#private;
|
|
364
370
|
constructor(urls?: readonly string[]);
|
|
365
|
-
/**
|
|
371
|
+
/**
|
|
372
|
+
* Adds a pattern. If the URL has no scheme, both http:// and https:// variants are added.
|
|
373
|
+
* Throws TypeError if the pattern is not a valid string or is structurally invalid.
|
|
374
|
+
*/
|
|
366
375
|
add(url: string): void;
|
|
367
|
-
/**
|
|
376
|
+
/** Returns true if `url` matches any pattern in the allowlist. */
|
|
368
377
|
contains(url: string): boolean;
|
|
369
378
|
toString(): string;
|
|
370
379
|
}
|
|
@@ -379,32 +388,38 @@ declare const DEFAULT_CALENDAR_WHITELIST: UrlWhitelist;
|
|
|
379
388
|
*/
|
|
380
389
|
declare const DEFAULT_AGGREGATORS: readonly string[];
|
|
381
390
|
|
|
382
|
-
/**
|
|
391
|
+
/** Default public Esplora explorer (Bitcoin mainnet). */
|
|
383
392
|
declare const PUBLIC_ESPLORA_URL = "https://blockstream.info/api";
|
|
384
|
-
/**
|
|
393
|
+
/** Maximum size of an Esplora response (DoS defense). A JSON block header is a few hundred bytes. */
|
|
385
394
|
declare const MAX_ESPLORA_RESPONSE_SIZE = 100000;
|
|
386
395
|
interface EsploraClientOptions {
|
|
387
|
-
/** URL
|
|
396
|
+
/** Base URL of the explorer (defaults to Blockstream). Useful for pointing to a Litecoin Esplora. */
|
|
388
397
|
url?: string;
|
|
389
398
|
logger?: Logger;
|
|
390
399
|
}
|
|
391
|
-
/**
|
|
400
|
+
/** Client for a remote Esplora explorer. */
|
|
392
401
|
declare class EsploraClient {
|
|
393
402
|
#private;
|
|
394
403
|
constructor(networkLayer: ResilientNetworkLayer, options?: EsploraClientOptions);
|
|
395
|
-
/**
|
|
404
|
+
/** Returns the block hash (64-char hex, lowercase) at the given height. */
|
|
396
405
|
blockHash(height: number, signal?: AbortSignal): Promise<string>;
|
|
397
|
-
/**
|
|
406
|
+
/** Returns the block header (merkle root + timestamp) for the given hash. */
|
|
398
407
|
block(hash: string, signal?: AbortSignal): Promise<BlockHeader>;
|
|
408
|
+
/**
|
|
409
|
+
* Fetches the raw 80-byte block header for `hash` and self-authenticates it:
|
|
410
|
+
* sha256d(rawHeader) reversed must equal `hash`. This removes trust in the explorer's
|
|
411
|
+
* JSON layer — the raw header is cryptographically bound to the block hash we requested.
|
|
412
|
+
*/
|
|
413
|
+
rawBlockHeader(hash: string, signal?: AbortSignal): Promise<Uint8Array>;
|
|
399
414
|
}
|
|
400
415
|
/**
|
|
401
|
-
*
|
|
416
|
+
* Verifies a Bitcoin/Litecoin attestation against the corresponding block header.
|
|
402
417
|
*
|
|
403
|
-
* `digest`
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
418
|
+
* `digest` is the final tree commitment at the attestation point (32 bytes, must equal the
|
|
419
|
+
* block's merkle root). `explorer` must point to the correct chain (Blockstream for Bitcoin;
|
|
420
|
+
* a Litecoin Esplora for Litecoin). Returns the block time (epoch seconds) on success;
|
|
421
|
+
* throws `VerificationError` if the digest does not match or the attestation is not
|
|
422
|
+
* on-chain verifiable (`pending`/`unknown`). Fail-closed.
|
|
408
423
|
*/
|
|
409
424
|
declare function verifyTimestampAttestation(digest: Uint8Array, attestation: Attestation, explorer: EsploraClient, signal?: AbortSignal): Promise<number>;
|
|
410
425
|
|
|
@@ -412,21 +427,21 @@ declare function hashBuffer(data: Buffer | Uint8Array): Buffer;
|
|
|
412
427
|
declare function hashFile(path: string): Promise<Buffer>;
|
|
413
428
|
|
|
414
429
|
/**
|
|
415
|
-
* SSRF protection
|
|
430
|
+
* SSRF protection for user-configurable calendar URLs.
|
|
416
431
|
*
|
|
417
|
-
*
|
|
418
|
-
* - TOCTOU/DNS rebinding:
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
* - IPv4-mapped IPv6 (::ffff:x.x.x.x):
|
|
422
|
-
*
|
|
432
|
+
* KNOWN LIMITATIONS (documented intentionally):
|
|
433
|
+
* - TOCTOU / DNS rebinding: DNS validation happens BEFORE the connection.
|
|
434
|
+
* A server with TTL=0 can change its IP between validation and the actual fetch.
|
|
435
|
+
* Real mitigation requires network-level egress filtering.
|
|
436
|
+
* - IPv4-mapped IPv6 (::ffff:x.x.x.x): the ::ffff: prefix is blocked but the
|
|
437
|
+
* IPv4 component validation depends on the format Node.js returns.
|
|
423
438
|
*/
|
|
424
439
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
440
|
+
* Validates that a calendar URL is safe for outbound HTTP requests.
|
|
441
|
+
* Blocks private/reserved IPs by default.
|
|
427
442
|
*
|
|
428
|
-
* @param allowPrivate
|
|
429
|
-
*
|
|
443
|
+
* @param allowPrivate When true, skips the IP range check.
|
|
444
|
+
* Useful for local testing or internal corporate networks.
|
|
430
445
|
*/
|
|
431
446
|
declare function assertSafeCalendarUrl(url: string, options: {
|
|
432
447
|
allowPrivate: boolean;
|