@bsv/dpp-overlay-topics 0.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +58 -0
- package/README.md +169 -0
- package/dist/anchorStorage.d.ts +58 -0
- package/dist/anchorStorage.d.ts.map +1 -0
- package/dist/anchorStorage.js +72 -0
- package/dist/anchorStorage.js.map +1 -0
- package/dist/attestationAnchor.d.ts +10 -0
- package/dist/attestationAnchor.d.ts.map +1 -0
- package/dist/attestationAnchor.js +10 -0
- package/dist/attestationAnchor.js.map +1 -0
- package/dist/attestationStorage.d.ts +49 -0
- package/dist/attestationStorage.d.ts.map +1 -0
- package/dist/attestationStorage.js +78 -0
- package/dist/attestationStorage.js.map +1 -0
- package/dist/capabilities.d.ts +127 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +210 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/engineStorage.d.ts +106 -0
- package/dist/engineStorage.d.ts.map +1 -0
- package/dist/engineStorage.js +253 -0
- package/dist/engineStorage.js.map +1 -0
- package/dist/evidenceExport.d.ts +189 -0
- package/dist/evidenceExport.d.ts.map +1 -0
- package/dist/evidenceExport.js +517 -0
- package/dist/evidenceExport.js.map +1 -0
- package/dist/history.d.ts +114 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +215 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1223 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +31 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +31 -0
- package/dist/lib.js.map +1 -0
- package/dist/limits.d.ts +44 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +44 -0
- package/dist/limits.js.map +1 -0
- package/dist/lsAttestation.d.ts +21 -0
- package/dist/lsAttestation.d.ts.map +1 -0
- package/dist/lsAttestation.js +45 -0
- package/dist/lsAttestation.js.map +1 -0
- package/dist/lsDpp.d.ts +53 -0
- package/dist/lsDpp.d.ts.map +1 -0
- package/dist/lsDpp.js +148 -0
- package/dist/lsDpp.js.map +1 -0
- package/dist/lsUoraDpp.d.ts +55 -0
- package/dist/lsUoraDpp.d.ts.map +1 -0
- package/dist/lsUoraDpp.js +152 -0
- package/dist/lsUoraDpp.js.map +1 -0
- package/dist/policyConfig.d.ts +31 -0
- package/dist/policyConfig.d.ts.map +1 -0
- package/dist/policyConfig.js +112 -0
- package/dist/policyConfig.js.map +1 -0
- package/dist/retraction.d.ts +41 -0
- package/dist/retraction.d.ts.map +1 -0
- package/dist/retraction.js +147 -0
- package/dist/retraction.js.map +1 -0
- package/dist/storage.d.ts +117 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +158 -0
- package/dist/storage.js.map +1 -0
- package/dist/sync.d.ts +75 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +118 -0
- package/dist/sync.js.map +1 -0
- package/dist/tmAttestation.d.ts +36 -0
- package/dist/tmAttestation.d.ts.map +1 -0
- package/dist/tmAttestation.js +50 -0
- package/dist/tmAttestation.js.map +1 -0
- package/dist/tmDpp.d.ts +154 -0
- package/dist/tmDpp.d.ts.map +1 -0
- package/dist/tmDpp.js +397 -0
- package/dist/tmDpp.js.map +1 -0
- package/dist/tmUoraDpp.d.ts +73 -0
- package/dist/tmUoraDpp.d.ts.map +1 -0
- package/dist/tmUoraDpp.js +150 -0
- package/dist/tmUoraDpp.js.map +1 -0
- package/dist/uoraAnchor.d.ts +209 -0
- package/dist/uoraAnchor.d.ts.map +1 -0
- package/dist/uoraAnchor.js +433 -0
- package/dist/uoraAnchor.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { type DppRecord, type DppRecordStore, type RecordSelector } from './storage.js';
|
|
2
|
+
export declare const PAGE_VERSION = "1";
|
|
3
|
+
/** One record as a page carries it: what the record store knows, nothing hydrated. */
|
|
4
|
+
export interface HistoryItem {
|
|
5
|
+
txid: string;
|
|
6
|
+
outputIndex: number;
|
|
7
|
+
op: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
previousTxid: string;
|
|
10
|
+
spent: boolean;
|
|
11
|
+
spendingTxid: string;
|
|
12
|
+
/** The source's own sequence, the order pages are read in; strictly increasing across the pages of one export. */
|
|
13
|
+
sequence: number;
|
|
14
|
+
}
|
|
15
|
+
export interface HistoryPage {
|
|
16
|
+
pageVersion: typeof PAGE_VERSION;
|
|
17
|
+
items: HistoryItem[];
|
|
18
|
+
nextCursor: string | null;
|
|
19
|
+
snapshotId: string;
|
|
20
|
+
scope: {
|
|
21
|
+
source: string;
|
|
22
|
+
query: {
|
|
23
|
+
passportId: string;
|
|
24
|
+
} | {
|
|
25
|
+
uid: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The sequences this page covers within the snapshot: from the position
|
|
29
|
+
* after the previous page to the last item here, or to the snapshot's own
|
|
30
|
+
* sequence on the last page. Consecutive pages tile the snapshot with no
|
|
31
|
+
* gap and no overlap, so a reader detects a missing or a repeated page
|
|
32
|
+
* from the ranges alone.
|
|
33
|
+
*/
|
|
34
|
+
sequenceRange: {
|
|
35
|
+
from: number;
|
|
36
|
+
to: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
truncated: boolean;
|
|
40
|
+
completeForSnapshot: boolean;
|
|
41
|
+
pageSize: number;
|
|
42
|
+
}
|
|
43
|
+
export type HistoryErrorCode = 'query-invalid' | 'limit-invalid' | 'cursor-invalid' | 'snapshot-expired';
|
|
44
|
+
/** A refusal with the status and the name the wire carries. */
|
|
45
|
+
export declare class HistoryError extends Error {
|
|
46
|
+
readonly status: number;
|
|
47
|
+
readonly code: HistoryErrorCode;
|
|
48
|
+
readonly hint?: string | undefined;
|
|
49
|
+
constructor(status: number, code: HistoryErrorCode, message: string, hint?: string | undefined);
|
|
50
|
+
}
|
|
51
|
+
export interface Snapshot {
|
|
52
|
+
id: string;
|
|
53
|
+
/** Records with a sequence above this are outside the snapshot. */
|
|
54
|
+
sequence: number;
|
|
55
|
+
/** When the snapshot was taken, epoch milliseconds; expiry is measured from here. */
|
|
56
|
+
takenAt: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* What a cursor resumes: a page of the history or a part of the complete
|
|
60
|
+
* export (`spec/portable-evidence.md` section 2). The kind is inside the
|
|
61
|
+
* signed payload, so a history cursor presented to the export, or the
|
|
62
|
+
* reverse, is refused by name rather than read as a position.
|
|
63
|
+
*/
|
|
64
|
+
export type CursorKind = 'history' | 'export';
|
|
65
|
+
/** Where an export resumes: the snapshot, the position and the index of the part to build. */
|
|
66
|
+
export interface ExportResumption {
|
|
67
|
+
snapshot: Snapshot;
|
|
68
|
+
after: number;
|
|
69
|
+
part: number;
|
|
70
|
+
}
|
|
71
|
+
export interface HistoryRequest {
|
|
72
|
+
selector: RecordSelector;
|
|
73
|
+
/** As received on the wire: absent means the default page size. */
|
|
74
|
+
limit?: string | null;
|
|
75
|
+
cursor?: string | null;
|
|
76
|
+
}
|
|
77
|
+
export declare class HistoryPaginator {
|
|
78
|
+
private readonly records;
|
|
79
|
+
private readonly secret;
|
|
80
|
+
private readonly now;
|
|
81
|
+
constructor(records: DppRecordStore, options?: {
|
|
82
|
+
secret?: Buffer;
|
|
83
|
+
now?: () => Date;
|
|
84
|
+
});
|
|
85
|
+
/** Pin the store as it is now. The exporter uses this directly; the paginator does on a first page. */
|
|
86
|
+
takeSnapshot(): Promise<Snapshot>;
|
|
87
|
+
/** At most `limit` records of the selector within the snapshot after `after`, in sequence order: one part's candidates. */
|
|
88
|
+
readFrom(selector: RecordSelector, snapshot: Snapshot, after: number, limit: number): Promise<DppRecord[]>;
|
|
89
|
+
/** Every record of the selector within the snapshot, in sequence order, read page by page. */
|
|
90
|
+
readAll(selector: RecordSelector, snapshot: Snapshot): Promise<DppRecord[]>;
|
|
91
|
+
page(request: HistoryRequest): Promise<HistoryPage>;
|
|
92
|
+
/**
|
|
93
|
+
* The cursor that resumes the complete export after `after`, at part
|
|
94
|
+
* `part`, over the snapshot the first part pinned: the same tag, TTL and
|
|
95
|
+
* query binding as a history cursor, under the export kind.
|
|
96
|
+
*/
|
|
97
|
+
mintExportCursor(snapshot: Snapshot, query: {
|
|
98
|
+
passportId: string;
|
|
99
|
+
}, after: number, part: number): string;
|
|
100
|
+
/** Where an export cursor resumes, or the named refusal a history cursor or a stale one earns. */
|
|
101
|
+
openExportCursor(cursor: string, query: {
|
|
102
|
+
passportId: string;
|
|
103
|
+
}): ExportResumption;
|
|
104
|
+
/**
|
|
105
|
+
* Open a cursor for the given query and kind, refusing by name a cursor
|
|
106
|
+
* of the other kind, one issued for another query, and one whose snapshot
|
|
107
|
+
* is older than the TTL.
|
|
108
|
+
*/
|
|
109
|
+
private resume;
|
|
110
|
+
private mintCursor;
|
|
111
|
+
private tag;
|
|
112
|
+
private openCursor;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pages of a snapshot (`spec/portable-evidence.md` section 1,
|
|
3
|
+
* `contracts/paginated-history.schema.json`): the export beside the bounded
|
|
4
|
+
* lookup, which stays exactly what it is.
|
|
5
|
+
*
|
|
6
|
+
* A snapshot is the highest record sequence at the moment the first page is
|
|
7
|
+
* asked for. Every later page of the export reads only records at or below
|
|
8
|
+
* it, ordered by the store's own sequence, so a record admitted while a
|
|
9
|
+
* reader is walking the pages never appears in them and never shifts a page
|
|
10
|
+
* boundary: two readers of one snapshot receive the same pages. The cursor
|
|
11
|
+
* carries the snapshot and the position, base64url over JSON, and an
|
|
12
|
+
* HMAC-SHA256 tag under a secret this process drew at startup, so a cursor
|
|
13
|
+
* from another process, or one edited in transit, is refused by name rather
|
|
14
|
+
* than read as a position. A snapshot older than the TTL is refused by name
|
|
15
|
+
* too, with the instruction to start again without a cursor, because a stale
|
|
16
|
+
* snapshot continued from the middle would be a history that quietly stopped.
|
|
17
|
+
*/
|
|
18
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
19
|
+
import { DPP_SERVICE } from './lsDpp.js';
|
|
20
|
+
import { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, SNAPSHOT_TTL_MS } from './limits.js';
|
|
21
|
+
import { selectorFilter } from './storage.js';
|
|
22
|
+
export const PAGE_VERSION = '1';
|
|
23
|
+
/** A refusal with the status and the name the wire carries. */
|
|
24
|
+
export class HistoryError extends Error {
|
|
25
|
+
status;
|
|
26
|
+
code;
|
|
27
|
+
hint;
|
|
28
|
+
constructor(status, code, message, hint) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.status = status;
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.hint = hint;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const base64url = (bytes) => bytes.toString('base64url');
|
|
36
|
+
export class HistoryPaginator {
|
|
37
|
+
records;
|
|
38
|
+
secret;
|
|
39
|
+
now;
|
|
40
|
+
constructor(records, options = {}) {
|
|
41
|
+
this.records = records;
|
|
42
|
+
// Per process, never persisted: a cursor is a resumption token for this
|
|
43
|
+
// node's current snapshot, and outliving the process is exactly what it
|
|
44
|
+
// must not do.
|
|
45
|
+
this.secret = options.secret ?? randomBytes(32);
|
|
46
|
+
this.now = options.now ?? (() => new Date());
|
|
47
|
+
}
|
|
48
|
+
/** Pin the store as it is now. The exporter uses this directly; the paginator does on a first page. */
|
|
49
|
+
async takeSnapshot() {
|
|
50
|
+
const sequence = await this.records.highestSequence();
|
|
51
|
+
const takenAt = this.now().getTime();
|
|
52
|
+
return { id: `${sequence}.${takenAt.toString(36)}`, sequence, takenAt };
|
|
53
|
+
}
|
|
54
|
+
/** At most `limit` records of the selector within the snapshot after `after`, in sequence order: one part's candidates. */
|
|
55
|
+
async readFrom(selector, snapshot, after, limit) {
|
|
56
|
+
return await this.records.findPage(selector, { after, upTo: snapshot.sequence }, limit);
|
|
57
|
+
}
|
|
58
|
+
/** Every record of the selector within the snapshot, in sequence order, read page by page. */
|
|
59
|
+
async readAll(selector, snapshot) {
|
|
60
|
+
const all = [];
|
|
61
|
+
let after = 0;
|
|
62
|
+
for (;;) {
|
|
63
|
+
const rows = await this.records.findPage(selector, { after, upTo: snapshot.sequence }, MAX_PAGE_SIZE);
|
|
64
|
+
all.push(...rows);
|
|
65
|
+
if (rows.length < MAX_PAGE_SIZE)
|
|
66
|
+
return all;
|
|
67
|
+
after = rows[rows.length - 1].sequence;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async page(request) {
|
|
71
|
+
let query;
|
|
72
|
+
try {
|
|
73
|
+
query = selectorFilter(request.selector);
|
|
74
|
+
}
|
|
75
|
+
catch (cause) {
|
|
76
|
+
throw new HistoryError(400, 'query-invalid', cause instanceof Error ? cause.message : 'invalid query');
|
|
77
|
+
}
|
|
78
|
+
const limit = parseLimit(request.limit);
|
|
79
|
+
let snapshot;
|
|
80
|
+
let after;
|
|
81
|
+
if (request.cursor == null || request.cursor === '') {
|
|
82
|
+
snapshot = await this.takeSnapshot();
|
|
83
|
+
after = 0;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
;
|
|
87
|
+
({ snapshot, after } = this.resume(request.cursor, query, 'history'));
|
|
88
|
+
}
|
|
89
|
+
// One row beyond the page tells whether the snapshot continues, without a
|
|
90
|
+
// count query that would race nothing but cost a scan.
|
|
91
|
+
const rows = await this.records.findPage(query, { after, upTo: snapshot.sequence }, limit + 1);
|
|
92
|
+
const more = rows.length > limit;
|
|
93
|
+
const items = rows.slice(0, limit);
|
|
94
|
+
const last = items[items.length - 1];
|
|
95
|
+
const to = more && last != null ? last.sequence : snapshot.sequence;
|
|
96
|
+
const nextCursor = more && last != null
|
|
97
|
+
? this.mintCursor({ v: 1, kind: 'history', snapshot, query, after: last.sequence })
|
|
98
|
+
: null;
|
|
99
|
+
return {
|
|
100
|
+
pageVersion: PAGE_VERSION,
|
|
101
|
+
items: items.map(toItem),
|
|
102
|
+
nextCursor,
|
|
103
|
+
snapshotId: snapshot.id,
|
|
104
|
+
scope: {
|
|
105
|
+
source: DPP_SERVICE,
|
|
106
|
+
query,
|
|
107
|
+
sequenceRange: { from: Math.min(after + 1, to), to },
|
|
108
|
+
},
|
|
109
|
+
truncated: more,
|
|
110
|
+
// Only on the last page: every record of the selector at or below the
|
|
111
|
+
// snapshot has now been returned across the pages of this export.
|
|
112
|
+
completeForSnapshot: !more,
|
|
113
|
+
pageSize: limit,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The cursor that resumes the complete export after `after`, at part
|
|
118
|
+
* `part`, over the snapshot the first part pinned: the same tag, TTL and
|
|
119
|
+
* query binding as a history cursor, under the export kind.
|
|
120
|
+
*/
|
|
121
|
+
mintExportCursor(snapshot, query, after, part) {
|
|
122
|
+
return this.mintCursor({ v: 1, kind: 'export', snapshot, query, after, part });
|
|
123
|
+
}
|
|
124
|
+
/** Where an export cursor resumes, or the named refusal a history cursor or a stale one earns. */
|
|
125
|
+
openExportCursor(cursor, query) {
|
|
126
|
+
const { snapshot, after, part } = this.resume(cursor, query, 'export');
|
|
127
|
+
if (!Number.isSafeInteger(part) || part < 2) {
|
|
128
|
+
throw new HistoryError(400, 'cursor-invalid', 'the cursor is not one this node issued: no part index');
|
|
129
|
+
}
|
|
130
|
+
return { snapshot, after, part: part };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Open a cursor for the given query and kind, refusing by name a cursor
|
|
134
|
+
* of the other kind, one issued for another query, and one whose snapshot
|
|
135
|
+
* is older than the TTL.
|
|
136
|
+
*/
|
|
137
|
+
resume(cursor, query, kind) {
|
|
138
|
+
const payload = this.openCursor(cursor);
|
|
139
|
+
if (payload.kind !== kind) {
|
|
140
|
+
throw new HistoryError(400, 'cursor-invalid', `the cursor was issued for the ${payload.kind}, not the ${kind}`);
|
|
141
|
+
}
|
|
142
|
+
if (!sameQuery(payload.query, query)) {
|
|
143
|
+
throw new HistoryError(400, 'cursor-invalid', 'the cursor was issued for a different query');
|
|
144
|
+
}
|
|
145
|
+
if (this.now().getTime() - payload.snapshot.takenAt > SNAPSHOT_TTL_MS) {
|
|
146
|
+
throw new HistoryError(410, 'snapshot-expired', `snapshot ${payload.snapshot.id} is older than ${SNAPSHOT_TTL_MS / 1000} seconds`, `restart the ${kind === 'history' ? 'export' : 'complete export'} without a cursor to read a fresh snapshot from its first ${kind === 'history' ? 'page' : 'part'}`);
|
|
147
|
+
}
|
|
148
|
+
return { snapshot: payload.snapshot, after: payload.after, part: payload.part };
|
|
149
|
+
}
|
|
150
|
+
mintCursor(payload) {
|
|
151
|
+
const body = base64url(Buffer.from(JSON.stringify(payload), 'utf8'));
|
|
152
|
+
return `${body}.${this.tag(body)}`;
|
|
153
|
+
}
|
|
154
|
+
tag(body) {
|
|
155
|
+
return base64url(createHmac('sha256', this.secret).update(body).digest());
|
|
156
|
+
}
|
|
157
|
+
openCursor(cursor) {
|
|
158
|
+
const invalid = (detail) => new HistoryError(400, 'cursor-invalid', `the cursor is not one this node issued: ${detail}`);
|
|
159
|
+
const separator = cursor.lastIndexOf('.');
|
|
160
|
+
if (separator <= 0)
|
|
161
|
+
throw invalid('malformed');
|
|
162
|
+
const body = cursor.slice(0, separator);
|
|
163
|
+
const given = Buffer.from(cursor.slice(separator + 1), 'base64url');
|
|
164
|
+
const expected = Buffer.from(this.tag(body), 'base64url');
|
|
165
|
+
if (given.length !== expected.length || !timingSafeEqual(given, expected))
|
|
166
|
+
throw invalid('bad signature');
|
|
167
|
+
let payload;
|
|
168
|
+
try {
|
|
169
|
+
payload = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
throw invalid('unreadable');
|
|
173
|
+
}
|
|
174
|
+
if (payload?.v !== 1 ||
|
|
175
|
+
(payload.kind !== 'history' && payload.kind !== 'export') ||
|
|
176
|
+
typeof payload.snapshot?.id !== 'string' ||
|
|
177
|
+
!Number.isSafeInteger(payload.snapshot.sequence) ||
|
|
178
|
+
!Number.isSafeInteger(payload.snapshot.takenAt) ||
|
|
179
|
+
!Number.isSafeInteger(payload.after) ||
|
|
180
|
+
typeof payload.query !== 'object' ||
|
|
181
|
+
payload.query === null) {
|
|
182
|
+
throw invalid('unexpected shape');
|
|
183
|
+
}
|
|
184
|
+
return payload;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** Default 100, maximum 500, anything else refused: a string of digits is the only accepted spelling. */
|
|
188
|
+
function parseLimit(raw) {
|
|
189
|
+
if (raw == null || raw === '')
|
|
190
|
+
return DEFAULT_PAGE_SIZE;
|
|
191
|
+
if (!/^\d+$/.test(raw))
|
|
192
|
+
throw new HistoryError(400, 'limit-invalid', `limit must be an integer from 1 to ${MAX_PAGE_SIZE}`);
|
|
193
|
+
const limit = Number(raw);
|
|
194
|
+
if (limit < 1 || limit > MAX_PAGE_SIZE) {
|
|
195
|
+
throw new HistoryError(400, 'limit-invalid', `limit must be an integer from 1 to ${MAX_PAGE_SIZE}`);
|
|
196
|
+
}
|
|
197
|
+
return limit;
|
|
198
|
+
}
|
|
199
|
+
function sameQuery(a, b) {
|
|
200
|
+
return ('passportId' in a ? a.passportId : undefined) === ('passportId' in b ? b.passportId : undefined) &&
|
|
201
|
+
('uid' in a ? a.uid : undefined) === ('uid' in b ? b.uid : undefined);
|
|
202
|
+
}
|
|
203
|
+
function toItem(record) {
|
|
204
|
+
return {
|
|
205
|
+
txid: record.txid,
|
|
206
|
+
outputIndex: record.outputIndex,
|
|
207
|
+
op: record.op,
|
|
208
|
+
timestamp: record.timestamp,
|
|
209
|
+
previousTxid: record.previousTxid,
|
|
210
|
+
spent: record.spent,
|
|
211
|
+
spendingTxid: record.spendingTxid,
|
|
212
|
+
sequence: record.sequence,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DPP overlay node: an HTTP host for two rails, wrapping the
|
|
3
|
+
* `@bsv/overlay` Engine.
|
|
4
|
+
*
|
|
5
|
+
* the passport `tm_dpp` admits states, `ls_dpp` resolves them
|
|
6
|
+
* the anchors `tm_attestation` admits generic anchors, `ls_attestation`
|
|
7
|
+
* looks them up; `tm_uora_dpp` and `ls_uora_dpp` carry the
|
|
8
|
+
* historical UORA anchors
|
|
9
|
+
*
|
|
10
|
+
* The wire contract is the ecosystem's standard one (BRC-22 submit, BRC-24
|
|
11
|
+
* lookup), pinned as OpenAPI in `contracts/overlay.yaml`:
|
|
12
|
+
*
|
|
13
|
+
* POST /submit application/octet-stream body = BEEF bytes
|
|
14
|
+
* X-Topics: ["tm_dpp"] -> STEAK as JSON, plus an
|
|
15
|
+
* X-Admission answer header (topic=admitted|duplicate|none)
|
|
16
|
+
* Authorization: Bearer <token> when SUBMIT_TOKEN is set
|
|
17
|
+
* POST /lookup {"service":"ls_dpp","query":{"passportId"|"uid": "..."}}
|
|
18
|
+
* {"service":"ls_uora_dpp","query":{"issuer":"did:key:z..."}}
|
|
19
|
+
* -> {type:"output-list",
|
|
20
|
+
* outputs:[{beef,outputIndex}]}
|
|
21
|
+
* POST /arc-ingest {"txid","merklePath","blockHeight"}
|
|
22
|
+
* -> the proof, checked and
|
|
23
|
+
* applied to the held state (spec/services.md section 2);
|
|
24
|
+
* X-Callback-Token or bearer when ARC_CALLBACK_TOKEN is set
|
|
25
|
+
* GET /health -> {status:"ok",...}
|
|
26
|
+
*
|
|
27
|
+
* And the extensions this reference deployment serves beside them, each in
|
|
28
|
+
* the shape a contract in `contracts/` fixes:
|
|
29
|
+
*
|
|
30
|
+
* GET /capabilities -> the capability document
|
|
31
|
+
* (contracts/capabilities.schema.json), built from constants
|
|
32
|
+
* and configuration, never typed by hand
|
|
33
|
+
* GET /history?passportId=|uid=&limit=&cursor= -> one page of a stable
|
|
34
|
+
* snapshot (contracts/paginated-history.schema.json), the
|
|
35
|
+
* export beside the bounded lookup
|
|
36
|
+
* GET /evidence-package?passportId= -> dpp-evidence-package@1
|
|
37
|
+
* as {manifest, files} (contracts/evidence-package.schema.json),
|
|
38
|
+
* signed with EXPORT_SIGNING_KEY; 503 when it is unset;
|
|
39
|
+
* bounded to the newest 500 states, the rest declared absent
|
|
40
|
+
* GET /evidence-export?passportId=&cursor= -> one part of the complete
|
|
41
|
+
* export (contracts/evidence-export.schema.json): a package
|
|
42
|
+
* over a contiguous sequence range of one snapshot, oldest
|
|
43
|
+
* first, bounded by states and bytes, resumed by cursor;
|
|
44
|
+
* Authorization: Bearer <token> when EXPORT_TOKEN is set
|
|
45
|
+
* POST /retract {"txid","outputIndex","reason"} -> removes a state the
|
|
46
|
+
* network refused, behind the /submit bearer; 409 when the
|
|
47
|
+
* state is proven, spent or known to the chain tracker
|
|
48
|
+
* POST /requestSyncResponse, POST /requestForeignGASPNode
|
|
49
|
+
* -> the upstream GASP
|
|
50
|
+
* routes a peer synchronises from, exactly as the overlay
|
|
51
|
+
* protocol defines them
|
|
52
|
+
*
|
|
53
|
+
* Everything is environment; an unset variable switches its feature off or
|
|
54
|
+
* falls back, and only a missing identity key (or a policy file that does not
|
|
55
|
+
* verify) fails the boot. See the Configuration table in this package's
|
|
56
|
+
* README.
|
|
57
|
+
*
|
|
58
|
+
* This file is both the entry point and a library: it boots only when node
|
|
59
|
+
* runs it directly, so tests can drive `createRequestHandler` and
|
|
60
|
+
* `startOverlayService` without a container.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately absent: a Broadcaster and an Advertiser. The app's wallet has
|
|
63
|
+
* already broadcast the transaction by the time it announces it here, so a
|
|
64
|
+
* second broadcast would only add a failure mode; and SHIP/SLAP advertising
|
|
65
|
+
* would need this service to hold a funded wallet of its own. Neither is
|
|
66
|
+
* needed for the demo, and both can be added later without changing the wire.
|
|
67
|
+
* Peer synchronisation is present but static: SYNC_PEERS names the operators
|
|
68
|
+
* this node pulls from through the SDK's GASP (sync.ts), and a state a peer
|
|
69
|
+
* offers passes the same topic managers as one a writer announces.
|
|
70
|
+
* Because this host does not broadcast, no broadcaster's callback reaches it
|
|
71
|
+
* on its own: merkle proofs arrive through POST /arc-ingest, pushed by the
|
|
72
|
+
* writer once its wallet has them or by a broadcaster whose callback URL a
|
|
73
|
+
* writer pointed here (spec/writing.md section 7). Re-announcing a mined state
|
|
74
|
+
* would not do it: the engine skips a txid it already holds.
|
|
75
|
+
*/
|
|
76
|
+
import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
|
|
77
|
+
import { type ChainTracker } from '@bsv/sdk';
|
|
78
|
+
import { Engine, type LookupService } from '@bsv/overlay';
|
|
79
|
+
import { type DppRecordStore } from './storage.js';
|
|
80
|
+
import { type RetractableStorage } from './engineStorage.js';
|
|
81
|
+
import { UORA_SERVICE, UORA_TOPIC } from './lsUoraDpp.js';
|
|
82
|
+
import { ATTESTATION_TOPIC } from './tmAttestation.js';
|
|
83
|
+
import { ATTESTATION_SERVICE } from './lsAttestation.js';
|
|
84
|
+
import { type PublisherPolicyConfig } from './policyConfig.js';
|
|
85
|
+
import { type SyncSettings } from './sync.js';
|
|
86
|
+
export declare const TOPIC = "tm_dpp";
|
|
87
|
+
export declare const SERVICE = "ls_dpp";
|
|
88
|
+
export { UORA_SERVICE, UORA_TOPIC };
|
|
89
|
+
export { ATTESTATION_SERVICE, ATTESTATION_TOPIC };
|
|
90
|
+
/** The slice of the Engine the HTTP layer uses, so a test can stand one in. */
|
|
91
|
+
export type OverlayEngine = Pick<Engine, 'submit' | 'lookup' | 'listTopicManagers' | 'listLookupServiceProviders' | 'handleNewMerkleProof' | 'getDocumentationForTopicManager' | 'getDocumentationForLookupServiceProvider' | 'provideForeignSyncResponse' | 'provideForeignGASPNode'>;
|
|
92
|
+
/**
|
|
93
|
+
* The stores, services and policy the extension routes work on, which the
|
|
94
|
+
* Engine does not expose through its own surface. `/history` and
|
|
95
|
+
* `/evidence-package` read the record store and the engine storage;
|
|
96
|
+
* `/retract` removes from both and tells the lookup services; `/capabilities`
|
|
97
|
+
* reports the policy. Without them, the read routes answer 503 by name and the
|
|
98
|
+
* capability document describes a node with no configured policy.
|
|
99
|
+
*/
|
|
100
|
+
export interface NodeComponents {
|
|
101
|
+
records: DppRecordStore;
|
|
102
|
+
engineStorage: RetractableStorage;
|
|
103
|
+
lookupServices: Record<string, LookupService>;
|
|
104
|
+
publisherPolicy?: PublisherPolicyConfig;
|
|
105
|
+
serviceIdentityKey?: string;
|
|
106
|
+
anchorServiceKeys?: string[];
|
|
107
|
+
ownerConsent?: boolean | {
|
|
108
|
+
authorities: string[];
|
|
109
|
+
};
|
|
110
|
+
/** CONTROL_AUTHORITIES and ACCEPTANCE_COMMITMENT, the version 2 admission options (`spec/record-model-v2.md` §6, `spec/managed-custody.md`). */
|
|
111
|
+
controlAuthorities?: string[];
|
|
112
|
+
managedAcceptance?: boolean;
|
|
113
|
+
/** SYNC_PEERS and SYNC_INTERVAL_MS, reported by the capability document; the Engine holds the same peers as its syncConfiguration. */
|
|
114
|
+
sync?: Pick<SyncSettings, 'peers' | 'intervalMs'>;
|
|
115
|
+
}
|
|
116
|
+
export interface OverlayHttpOptions {
|
|
117
|
+
/**
|
|
118
|
+
* Shared secret required as `Authorization: Bearer` on POST /submit and
|
|
119
|
+
* POST /retract, the two routes that change what the index holds.
|
|
120
|
+
* `/lookup` and `/health` stay open: browser verification is a stated goal,
|
|
121
|
+
* and a read costs no header quota. Unset leaves both open too, which is only
|
|
122
|
+
* acceptable on a container nobody else can reach.
|
|
123
|
+
*/
|
|
124
|
+
submitToken?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Shared secret required on POST /arc-ingest, as `Authorization: Bearer` or
|
|
127
|
+
* as `X-Callback-Token`, the two ways an ARC-compatible broadcaster sends the
|
|
128
|
+
* token it was given at submission. Unset leaves the route open; every proof
|
|
129
|
+
* is still verified against the header source before it is stored, so an
|
|
130
|
+
* open route costs header quota rather than truth.
|
|
131
|
+
*/
|
|
132
|
+
proofToken?: string;
|
|
133
|
+
/**
|
|
134
|
+
* The header source a pushed proof is validated against before it is
|
|
135
|
+
* applied, normally the same one the engine verifies submissions with.
|
|
136
|
+
* 'scripts only' or unset applies a proof on its local check alone, which
|
|
137
|
+
* is the local-development setting the engine's own option already is.
|
|
138
|
+
*/
|
|
139
|
+
chainTracker?: ChainTracker | 'scripts only';
|
|
140
|
+
/** Reported by /health. */
|
|
141
|
+
network?: string;
|
|
142
|
+
/** Reported by /health. Defaults to the moment the handler was made. */
|
|
143
|
+
startedAt?: string;
|
|
144
|
+
/**
|
|
145
|
+
* The address to bind. Unset binds the wildcard, which is what a container
|
|
146
|
+
* wants and what `main()` leaves it as.
|
|
147
|
+
*
|
|
148
|
+
* **A test must set this, and the reason is not tidiness.** A wildcard bind
|
|
149
|
+
* on an ephemeral port succeeds even when another process already holds that
|
|
150
|
+
* exact port bound to `127.0.0.1` alone: there is no `EADDRINUSE`, and
|
|
151
|
+
* `address().port` reports the port as though it were ours. A request to
|
|
152
|
+
* `127.0.0.1` then goes to the more specific binding, so the caller is
|
|
153
|
+
* answered by the other process and the service it started never sees the
|
|
154
|
+
* request. Found on 2026-08-06 behind an intermittent failure in
|
|
155
|
+
* `test/http.test.ts`: a Logitech daemon holding three ports at the bottom of
|
|
156
|
+
* the ephemeral range answered `501` with an empty body, which surfaces
|
|
157
|
+
* either as a wrong status or as `Unexpected end of JSON input`, roughly once
|
|
158
|
+
* in a hundred server starts. Binding the loopback explicitly makes the
|
|
159
|
+
* socket a caller reaches the socket that was bound.
|
|
160
|
+
*/
|
|
161
|
+
host?: string;
|
|
162
|
+
/** What the extension routes work on; see `NodeComponents`. */
|
|
163
|
+
components?: NodeComponents;
|
|
164
|
+
/**
|
|
165
|
+
* EXPORT_SIGNING_KEY: the private key, hex, that signs evidence-package
|
|
166
|
+
* manifests. Unset, GET /evidence-package answers 503 export-unavailable and
|
|
167
|
+
* the capability document lists the export as unsupported. Malformed, the
|
|
168
|
+
* handler refuses to be built, which is the boot.
|
|
169
|
+
*/
|
|
170
|
+
exportSigningKey?: string;
|
|
171
|
+
/**
|
|
172
|
+
* EXPORT_TOKEN: shared secret required as `Authorization: Bearer` on GET
|
|
173
|
+
* /evidence-export, the complete export, whose parts are each bounded but
|
|
174
|
+
* whose number is not. Unset leaves it open, as GET /history is; the bounded
|
|
175
|
+
* GET /evidence-package stays open either way, and the capability document
|
|
176
|
+
* says which.
|
|
177
|
+
*/
|
|
178
|
+
exportToken?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Whether the network knows a transaction, for POST /retract: asked of the
|
|
181
|
+
* header source's operator before an output is removed. Unset when there is
|
|
182
|
+
* no header source (CHAIN_TRACKER=scripts-only); the retraction then runs on
|
|
183
|
+
* the local merkle-path check alone and its answer says so.
|
|
184
|
+
*/
|
|
185
|
+
knownOnChain?: (txid: string) => Promise<boolean>;
|
|
186
|
+
/** The clock: snapshot expiry, export time and the policy's "active now" read it. Injectable for tests. */
|
|
187
|
+
now?: () => Date;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The whole HTTP surface, as a plain node request listener. Separated from the
|
|
191
|
+
* server so a test can exercise the wire contract against a stand-in engine.
|
|
192
|
+
*/
|
|
193
|
+
export declare function createRequestHandler(engine: OverlayEngine, options?: OverlayHttpOptions): (request: IncomingMessage, response: ServerResponse) => void;
|
|
194
|
+
export interface RunningService {
|
|
195
|
+
server: Server;
|
|
196
|
+
/** The port actually bound, which matters when the caller asked for 0. */
|
|
197
|
+
port: number;
|
|
198
|
+
close: () => Promise<void>;
|
|
199
|
+
}
|
|
200
|
+
/** Bind the HTTP surface. Resolves once the socket is listening. */
|
|
201
|
+
export declare function startOverlayService(engine: OverlayEngine, options?: OverlayHttpOptions & {
|
|
202
|
+
port?: number;
|
|
203
|
+
}): Promise<RunningService>;
|
|
204
|
+
/**
|
|
205
|
+
* The key `server_signature` is checked against (`spec/record-model.md` §5). Only the
|
|
206
|
+
* public half is needed, so the deployment should set SERVICE_IDENTITY_KEY
|
|
207
|
+
* and this service never holds the treasury secret. SERVER_PRIVATE_KEY is
|
|
208
|
+
* accepted as a fallback because it is what every other component is
|
|
209
|
+
* configured with, but it grants this container more than it needs.
|
|
210
|
+
*/
|
|
211
|
+
export declare function serviceIdentityKey(): string;
|
|
212
|
+
/**
|
|
213
|
+
* Identity keys of the anchoring services whose UORA anchors this instance
|
|
214
|
+
* admits, comma-separated in ANCHOR_SERVICE_KEYS.
|
|
215
|
+
*
|
|
216
|
+
* A list rather than one key, because the shared overlay instances this topic
|
|
217
|
+
* is meant for may serve more than one anchoring deployment, and because the
|
|
218
|
+
* resolver already established that this programme has anchored under two
|
|
219
|
+
* treasuries without being able to tell which.
|
|
220
|
+
*
|
|
221
|
+
* Public keys only. Unlike `serviceIdentityKey` there is no private-key
|
|
222
|
+
* fallback: the anchoring treasury is a different custody boundary from this
|
|
223
|
+
* container, and inviting an operator to paste its secret here to save a step
|
|
224
|
+
* is exactly the convenience that should not exist.
|
|
225
|
+
*/
|
|
226
|
+
export declare function anchorServiceKeys(): string[];
|
|
227
|
+
/**
|
|
228
|
+
* The owner-signed transfer (`spec/custody.md` §4) is a profile's choice, and
|
|
229
|
+
* a deployment selects it with OWNER_CONSENT=required. TRANSFER_AUTHORITIES
|
|
230
|
+
* then names, comma-separated, the identity keys that may move ownership
|
|
231
|
+
* without proving consent (recovery); public keys only, as for
|
|
232
|
+
* ANCHOR_SERVICE_KEYS, and validated when the topic manager is built. Any
|
|
233
|
+
* other value of OWNER_CONSENT fails the boot rather than being read as a
|
|
234
|
+
* guess, and authorities set without the policy are a misconfiguration worth a
|
|
235
|
+
* warning, because they would silently do nothing.
|
|
236
|
+
*/
|
|
237
|
+
export declare function ownerConsentPolicy(): boolean | {
|
|
238
|
+
authorities: string[];
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* The version 2 admission options (`spec/record-model-v2.md` §6,
|
|
242
|
+
* `spec/managed-custody.md`). ACCEPTANCE_COMMITMENT=required selects the
|
|
243
|
+
* managed-custody profile: a version 2 TRANSFER is admitted only with its
|
|
244
|
+
* acceptance commitment. CONTROL_AUTHORITIES names, comma-separated, the
|
|
245
|
+
* identity keys whose version 2 UPDATE, TRANSFER or RETIRE is admitted without
|
|
246
|
+
* a control proof; absent, the TRANSFER_AUTHORITIES serve both versions.
|
|
247
|
+
* Public keys only, validated when the topic manager is built; any other value
|
|
248
|
+
* of ACCEPTANCE_COMMITMENT fails the boot.
|
|
249
|
+
*/
|
|
250
|
+
export declare function versionTwoAdmission(): {
|
|
251
|
+
managedAcceptance: boolean;
|
|
252
|
+
controlAuthorities?: string[];
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* EXPORT_SIGNING_KEY: the private key that signs evidence-package manifests
|
|
256
|
+
* (`spec/portable-evidence.md` section 2). Optional, because a node that
|
|
257
|
+
* exports nothing needs no key; malformed, it fails the boot, because a key
|
|
258
|
+
* that cannot sign is a promise the capability document would break.
|
|
259
|
+
*/
|
|
260
|
+
export declare function exportSigningKey(): string | undefined;
|
|
261
|
+
/**
|
|
262
|
+
* Whether the network knows a transaction, asked of the same explorer the
|
|
263
|
+
* default header source reads, for POST /retract: a transaction it answers is
|
|
264
|
+
* one the network took, and an index does not retract what a block holds. A
|
|
265
|
+
* 404 is the one negative; any other failure is neither and is reported as
|
|
266
|
+
* the source being unavailable.
|
|
267
|
+
*/
|
|
268
|
+
export declare function whatsOnChainKnows(network: 'main' | 'test', apiKey?: string): (txid: string) => Promise<boolean>;
|
|
269
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|