@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.
Files changed (87) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +169 -0
  3. package/dist/anchorStorage.d.ts +58 -0
  4. package/dist/anchorStorage.d.ts.map +1 -0
  5. package/dist/anchorStorage.js +72 -0
  6. package/dist/anchorStorage.js.map +1 -0
  7. package/dist/attestationAnchor.d.ts +10 -0
  8. package/dist/attestationAnchor.d.ts.map +1 -0
  9. package/dist/attestationAnchor.js +10 -0
  10. package/dist/attestationAnchor.js.map +1 -0
  11. package/dist/attestationStorage.d.ts +49 -0
  12. package/dist/attestationStorage.d.ts.map +1 -0
  13. package/dist/attestationStorage.js +78 -0
  14. package/dist/attestationStorage.js.map +1 -0
  15. package/dist/capabilities.d.ts +127 -0
  16. package/dist/capabilities.d.ts.map +1 -0
  17. package/dist/capabilities.js +210 -0
  18. package/dist/capabilities.js.map +1 -0
  19. package/dist/engineStorage.d.ts +106 -0
  20. package/dist/engineStorage.d.ts.map +1 -0
  21. package/dist/engineStorage.js +253 -0
  22. package/dist/engineStorage.js.map +1 -0
  23. package/dist/evidenceExport.d.ts +189 -0
  24. package/dist/evidenceExport.d.ts.map +1 -0
  25. package/dist/evidenceExport.js +517 -0
  26. package/dist/evidenceExport.js.map +1 -0
  27. package/dist/history.d.ts +114 -0
  28. package/dist/history.d.ts.map +1 -0
  29. package/dist/history.js +215 -0
  30. package/dist/history.js.map +1 -0
  31. package/dist/index.d.ts +269 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +1223 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/lib.d.ts +31 -0
  36. package/dist/lib.d.ts.map +1 -0
  37. package/dist/lib.js +31 -0
  38. package/dist/lib.js.map +1 -0
  39. package/dist/limits.d.ts +44 -0
  40. package/dist/limits.d.ts.map +1 -0
  41. package/dist/limits.js +44 -0
  42. package/dist/limits.js.map +1 -0
  43. package/dist/lsAttestation.d.ts +21 -0
  44. package/dist/lsAttestation.d.ts.map +1 -0
  45. package/dist/lsAttestation.js +45 -0
  46. package/dist/lsAttestation.js.map +1 -0
  47. package/dist/lsDpp.d.ts +53 -0
  48. package/dist/lsDpp.d.ts.map +1 -0
  49. package/dist/lsDpp.js +148 -0
  50. package/dist/lsDpp.js.map +1 -0
  51. package/dist/lsUoraDpp.d.ts +55 -0
  52. package/dist/lsUoraDpp.d.ts.map +1 -0
  53. package/dist/lsUoraDpp.js +152 -0
  54. package/dist/lsUoraDpp.js.map +1 -0
  55. package/dist/policyConfig.d.ts +31 -0
  56. package/dist/policyConfig.d.ts.map +1 -0
  57. package/dist/policyConfig.js +112 -0
  58. package/dist/policyConfig.js.map +1 -0
  59. package/dist/retraction.d.ts +41 -0
  60. package/dist/retraction.d.ts.map +1 -0
  61. package/dist/retraction.js +147 -0
  62. package/dist/retraction.js.map +1 -0
  63. package/dist/storage.d.ts +117 -0
  64. package/dist/storage.d.ts.map +1 -0
  65. package/dist/storage.js +158 -0
  66. package/dist/storage.js.map +1 -0
  67. package/dist/sync.d.ts +75 -0
  68. package/dist/sync.d.ts.map +1 -0
  69. package/dist/sync.js +118 -0
  70. package/dist/sync.js.map +1 -0
  71. package/dist/tmAttestation.d.ts +36 -0
  72. package/dist/tmAttestation.d.ts.map +1 -0
  73. package/dist/tmAttestation.js +50 -0
  74. package/dist/tmAttestation.js.map +1 -0
  75. package/dist/tmDpp.d.ts +154 -0
  76. package/dist/tmDpp.d.ts.map +1 -0
  77. package/dist/tmDpp.js +397 -0
  78. package/dist/tmDpp.js.map +1 -0
  79. package/dist/tmUoraDpp.d.ts +73 -0
  80. package/dist/tmUoraDpp.d.ts.map +1 -0
  81. package/dist/tmUoraDpp.js +150 -0
  82. package/dist/tmUoraDpp.js.map +1 -0
  83. package/dist/uoraAnchor.d.ts +209 -0
  84. package/dist/uoraAnchor.d.ts.map +1 -0
  85. package/dist/uoraAnchor.js +433 -0
  86. package/dist/uoraAnchor.js.map +1 -0
  87. package/package.json +51 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The one accepted spelling of a selector: exactly one key, a non-empty string.
3
+ * Checked in the store and not only at the HTTP handler, because the Mongo
4
+ * store would run whatever object it is handed as a filter.
5
+ */
6
+ export function selectorFilter(selector) {
7
+ const passportId = typeof selector.passportId === 'string' && selector.passportId !== '' ? selector.passportId : undefined;
8
+ const uid = typeof selector.uid === 'string' && selector.uid !== '' ? selector.uid : undefined;
9
+ if (passportId != null && uid == null)
10
+ return { passportId };
11
+ if (uid != null && passportId == null)
12
+ return { uid };
13
+ throw new Error('selector must name exactly one of passportId or uid, non-empty');
14
+ }
15
+ const COUNTER_ID = 'dppRecords';
16
+ export class MongoDppStorage {
17
+ records;
18
+ counters;
19
+ ready;
20
+ constructor(db) {
21
+ this.records = db.collection('dppRecords');
22
+ this.counters = db.collection('dppCounters');
23
+ }
24
+ /**
25
+ * Indexes, and the one migration this store has ever needed: rows written
26
+ * before `sequence` existed are numbered in createdAt, txid, outputIndex
27
+ * order, so every row has a position before the first page is served and the
28
+ * pages of an old deployment read in the order its lookups always answered.
29
+ * Runs once per process on first use, and `main()` awaits it at boot so the
30
+ * numbering never races a request. Idempotent: a second boot finds nothing
31
+ * to number.
32
+ */
33
+ async ensureReady() {
34
+ this.ready ??= (async () => {
35
+ await this.records.createIndex({ passportId: 1, sequence: 1 });
36
+ await this.records.createIndex({ uid: 1, sequence: 1 });
37
+ await this.records.createIndex({ txid: 1, outputIndex: 1 }, { unique: true });
38
+ const legacy = this.records
39
+ .find({ sequence: { $exists: false } })
40
+ .sort({ createdAt: 1, txid: 1, outputIndex: 1 });
41
+ for await (const row of legacy) {
42
+ const sequence = await this.nextSequence();
43
+ // The filter repeats the existence test so a row a concurrent insert
44
+ // numbered in the meantime keeps the number it was given.
45
+ await this.records.updateOne({ txid: row.txid, outputIndex: row.outputIndex, sequence: { $exists: false } }, { $set: { sequence } });
46
+ }
47
+ })().catch((error) => {
48
+ this.ready = undefined;
49
+ throw error;
50
+ });
51
+ await this.ready;
52
+ }
53
+ /** One atomic increment per insert: the counter is the order, gaps included. */
54
+ async nextSequence() {
55
+ const counter = await this.counters.findOneAndUpdate({ _id: COUNTER_ID }, { $inc: { value: 1 } }, { upsert: true, returnDocument: 'after' });
56
+ if (counter == null)
57
+ throw new Error('the sequence counter did not answer');
58
+ return counter.value;
59
+ }
60
+ async insert(record) {
61
+ await this.ensureReady();
62
+ // The sequence is allotted before the row is written, so a snapshot taken
63
+ // between the two may pin a number whose row lands a moment later. The
64
+ // Engine admits one transaction at a time per request, so the window is
65
+ // the width of one write; a page that has already passed that position
66
+ // does not go back for it, and the next snapshot has it.
67
+ const sequence = await this.nextSequence();
68
+ await this.records.updateOne({ txid: record.txid, outputIndex: record.outputIndex },
69
+ // $setOnInsert: a re-admitted output keeps the position it was first
70
+ // given, and only its other fields are refreshed.
71
+ { $set: record, $setOnInsert: { sequence } }, { upsert: true });
72
+ }
73
+ async markSpent(txid, outputIndex, spendingTxid) {
74
+ await this.ensureReady();
75
+ await this.records.updateOne({ txid, outputIndex }, { $set: { spent: true, spendingTxid } });
76
+ }
77
+ async markUnspent(txid, outputIndex) {
78
+ await this.ensureReady();
79
+ await this.records.updateOne({ txid, outputIndex }, { $set: { spent: false, spendingTxid: '' } });
80
+ }
81
+ async delete(txid, outputIndex) {
82
+ await this.ensureReady();
83
+ await this.records.deleteOne({ txid, outputIndex });
84
+ }
85
+ async findByPassport(passportId) {
86
+ await this.ensureReady();
87
+ return await this.records.find({ passportId }).sort({ sequence: 1 }).toArray();
88
+ }
89
+ async findByUid(uid) {
90
+ await this.ensureReady();
91
+ return await this.records.find({ uid }).sort({ sequence: 1 }).toArray();
92
+ }
93
+ async highestSequence() {
94
+ await this.ensureReady();
95
+ const counter = await this.counters.findOne({ _id: COUNTER_ID });
96
+ return counter?.value ?? 0;
97
+ }
98
+ async findPage(selector, range, limit) {
99
+ await this.ensureReady();
100
+ const filter = {
101
+ ...selectorFilter(selector),
102
+ sequence: { $gt: range.after, $lte: range.upTo },
103
+ };
104
+ return await this.records.find(filter).sort({ sequence: 1 }).limit(limit).toArray();
105
+ }
106
+ }
107
+ export class InMemoryDppStorage {
108
+ records = new Map();
109
+ assigned = 0;
110
+ key(txid, outputIndex) {
111
+ return `${txid}.${outputIndex}`;
112
+ }
113
+ async insert(record) {
114
+ const key = this.key(record.txid, record.outputIndex);
115
+ // As the Mongo store: a re-admitted output keeps its first position.
116
+ const sequence = this.records.get(key)?.sequence ?? ++this.assigned;
117
+ this.records.set(key, { ...record, sequence });
118
+ }
119
+ async markSpent(txid, outputIndex, spendingTxid) {
120
+ const record = this.records.get(this.key(txid, outputIndex));
121
+ if (record != null) {
122
+ record.spent = true;
123
+ record.spendingTxid = spendingTxid;
124
+ }
125
+ }
126
+ async markUnspent(txid, outputIndex) {
127
+ const record = this.records.get(this.key(txid, outputIndex));
128
+ if (record != null) {
129
+ record.spent = false;
130
+ record.spendingTxid = '';
131
+ }
132
+ }
133
+ async delete(txid, outputIndex) {
134
+ this.records.delete(this.key(txid, outputIndex));
135
+ }
136
+ async findByPassport(passportId) {
137
+ return [...this.records.values()]
138
+ .filter((r) => r.passportId === passportId)
139
+ .sort((a, b) => a.sequence - b.sequence);
140
+ }
141
+ async findByUid(uid) {
142
+ return [...this.records.values()]
143
+ .filter((r) => r.uid === uid)
144
+ .sort((a, b) => a.sequence - b.sequence);
145
+ }
146
+ async highestSequence() {
147
+ return this.assigned;
148
+ }
149
+ async findPage(selector, range, limit) {
150
+ const filter = selectorFilter(selector);
151
+ const matches = (r) => 'passportId' in filter ? r.passportId === filter.passportId : r.uid === filter.uid;
152
+ return [...this.records.values()]
153
+ .filter((r) => matches(r) && r.sequence > range.after && r.sequence <= range.upTo)
154
+ .sort((a, b) => a.sequence - b.sequence)
155
+ .slice(0, limit);
156
+ }
157
+ }
158
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
package/dist/sync.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Peer synchronisation (`spec/services.md` section 1, operator profile
3
+ * `federated-operators@1`): pulling the states and anchors another operator
4
+ * holds, through the overlay SDK's GASP, from a declared list of peers.
5
+ *
6
+ * The SDK does the protocol: `Engine.startGASPSync` walks every topic the
7
+ * `syncConfiguration` names, asks each peer for the outputs it holds since
8
+ * the last checkpoint, fetches each unknown output's graph, checks it against
9
+ * Bitcoin's rules and this node's own topic managers, and admits it through
10
+ * `Engine.submit` as a historical transaction. Admission during
11
+ * synchronisation is therefore the same admission as `/submit`: a state a
12
+ * peer offers is admitted only under the publisher policy and the chain
13
+ * rules, and a peer offering anything else is refused by name in the log.
14
+ *
15
+ * This module adds what the SDK leaves to the host: reading the peers from
16
+ * the environment, building the configuration for the two current topics
17
+ * (the legacy topic only when asked), running rounds on an interval, keeping
18
+ * a failing peer from stopping the node or blocking a request, and logging
19
+ * what each round did. Discovery is static: the peers are the ones named,
20
+ * never ones a lookup found, so the capability document says
21
+ * `static-peers` and nothing here authorises a publisher.
22
+ */
23
+ import type { Engine } from '@bsv/overlay';
24
+ /** The Engine's shape for `syncConfiguration`: peers per topic, 'SHIP' for discovered peers, or false. */
25
+ export type SyncConfiguration = Record<string, string[] | 'SHIP' | false>;
26
+ export declare const DEFAULT_SYNC_INTERVAL_MS = 60000;
27
+ export interface SyncSettings {
28
+ /** Peer base URLs, normalised: trimmed, no trailing slash, de-duplicated. */
29
+ peers: string[];
30
+ /** Milliseconds between rounds; 0 runs the round after startup and no other. */
31
+ intervalMs: number;
32
+ /** Whether the historical UORA topic synchronises too. */
33
+ legacy: boolean;
34
+ }
35
+ export declare function parseSyncPeers(value: string | undefined): string[];
36
+ export declare function syncSettingsFromEnvironment(): SyncSettings;
37
+ /**
38
+ * The Engine's configuration for the named topics: the peers for the passport
39
+ * and attestation topics, the legacy topic only when asked, false everywhere
40
+ * without peers, which is exactly the configuration the node has always run.
41
+ */
42
+ export declare function syncConfigurationFor(settings: Pick<SyncSettings, 'peers' | 'legacy'>, topics: {
43
+ passport: string;
44
+ attestation: string;
45
+ legacy: string;
46
+ }): SyncConfiguration;
47
+ export interface SyncRound {
48
+ round: number;
49
+ startedAt: string;
50
+ durationMs: number;
51
+ ok: boolean;
52
+ error?: string;
53
+ }
54
+ export interface PeerSynchronisation {
55
+ /** Run one round now, or join the round in flight; never rejects. */
56
+ runOnce: () => Promise<SyncRound>;
57
+ /** Stop the interval; a round in flight finishes. */
58
+ stop: () => void;
59
+ /** The rounds run so far, newest last, at most the last hundred. */
60
+ readonly rounds: SyncRound[];
61
+ }
62
+ /**
63
+ * Rounds on an interval, each logged, none overlapping, none able to fail the
64
+ * node: `startGASPSync` already isolates one peer's failure from the next,
65
+ * and whatever escapes it is caught here and named. Requests are never
66
+ * blocked, because a round is ordinary asynchronous work on the same event
67
+ * loop and nothing awaits it but the round itself.
68
+ */
69
+ export declare function startPeerSynchronisation(engine: Pick<Engine, 'startGASPSync'>, options: {
70
+ intervalMs: number;
71
+ peers: string[];
72
+ log?: (line: string) => void;
73
+ warn?: (line: string) => void;
74
+ }): PeerSynchronisation;
75
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,0GAA0G;AAC1G,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC,CAAA;AAEzE,eAAO,MAAM,wBAAwB,QAAS,CAAA;AAE9C,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAA;IAClB,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAA;CAChB;AAUD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAkBlE;AAED,wBAAgB,2BAA2B,IAAI,YAAY,CAa1D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,QAAQ,CAAC,EAChD,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChE,iBAAiB,CAOnB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,EAAE,EAAE,OAAO,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,OAAO,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAA;IACjC,qDAAqD;IACrD,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAA;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,EACrC,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAC5G,mBAAmB,CAmDrB"}
package/dist/sync.js ADDED
@@ -0,0 +1,118 @@
1
+ export const DEFAULT_SYNC_INTERVAL_MS = 60_000;
2
+ /** Comma-separated base URLs. Anything that is not an http or https URL stops the boot; a peer is not a thing to guess. */
3
+ /** A peer URL without its trailing slashes, scanned rather than matched so a long run of slashes costs linear time. */
4
+ function stripTrailingSlashes(value) {
5
+ let end = value.length;
6
+ while (end > 0 && value[end - 1] === '/')
7
+ end--;
8
+ return value.slice(0, end);
9
+ }
10
+ export function parseSyncPeers(value) {
11
+ const peers = [];
12
+ for (const raw of (value ?? '').split(',')) {
13
+ const candidate = raw.trim();
14
+ if (candidate === '')
15
+ continue;
16
+ let url;
17
+ try {
18
+ url = new URL(candidate);
19
+ }
20
+ catch {
21
+ throw new Error(`SYNC_PEERS entry "${candidate}" is not a URL`);
22
+ }
23
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
24
+ throw new Error(`SYNC_PEERS entry "${candidate}" must be an http or https URL`);
25
+ }
26
+ const normalised = stripTrailingSlashes(candidate);
27
+ if (!peers.includes(normalised))
28
+ peers.push(normalised);
29
+ }
30
+ return peers;
31
+ }
32
+ export function syncSettingsFromEnvironment() {
33
+ const peers = parseSyncPeers(process.env.SYNC_PEERS);
34
+ const rawInterval = (process.env.SYNC_INTERVAL_MS ?? '').trim();
35
+ let intervalMs = DEFAULT_SYNC_INTERVAL_MS;
36
+ if (rawInterval !== '') {
37
+ if (!/^\d+$/.test(rawInterval))
38
+ throw new Error('SYNC_INTERVAL_MS must be a non-negative integer of milliseconds');
39
+ intervalMs = Number(rawInterval);
40
+ }
41
+ const legacy = (process.env.SYNC_LEGACY ?? '').trim() === '1';
42
+ if (peers.length === 0 && (rawInterval !== '' || legacy)) {
43
+ console.warn('SYNC_INTERVAL_MS or SYNC_LEGACY is set but SYNC_PEERS is not: nothing synchronises');
44
+ }
45
+ return { peers, intervalMs, legacy };
46
+ }
47
+ /**
48
+ * The Engine's configuration for the named topics: the peers for the passport
49
+ * and attestation topics, the legacy topic only when asked, false everywhere
50
+ * without peers, which is exactly the configuration the node has always run.
51
+ */
52
+ export function syncConfigurationFor(settings, topics) {
53
+ const peers = settings.peers.length === 0 ? false : [...settings.peers];
54
+ return {
55
+ [topics.passport]: peers,
56
+ [topics.attestation]: peers,
57
+ [topics.legacy]: settings.legacy ? peers : false,
58
+ };
59
+ }
60
+ /**
61
+ * Rounds on an interval, each logged, none overlapping, none able to fail the
62
+ * node: `startGASPSync` already isolates one peer's failure from the next,
63
+ * and whatever escapes it is caught here and named. Requests are never
64
+ * blocked, because a round is ordinary asynchronous work on the same event
65
+ * loop and nothing awaits it but the round itself.
66
+ */
67
+ export function startPeerSynchronisation(engine, options) {
68
+ const log = options.log ?? ((line) => console.log(line));
69
+ const warn = options.warn ?? ((line) => console.warn(line));
70
+ const rounds = [];
71
+ let count = 0;
72
+ let inFlight;
73
+ const run = async () => {
74
+ const round = ++count;
75
+ const started = Date.now();
76
+ log(`peer synchronisation round ${round} started with ${options.peers.length} peer${options.peers.length === 1 ? '' : 's'}: ${options.peers.join(', ')}`);
77
+ let result;
78
+ try {
79
+ await engine.startGASPSync();
80
+ result = { round, startedAt: new Date(started).toISOString(), durationMs: Date.now() - started, ok: true };
81
+ log(`peer synchronisation round ${round} finished in ${result.durationMs} ms`);
82
+ }
83
+ catch (cause) {
84
+ const error = cause instanceof Error ? cause.message : String(cause);
85
+ result = { round, startedAt: new Date(started).toISOString(), durationMs: Date.now() - started, ok: false, error };
86
+ warn(`peer synchronisation round ${round} failed after ${result.durationMs} ms: ${error}`);
87
+ }
88
+ rounds.push(result);
89
+ if (rounds.length > 100)
90
+ rounds.shift();
91
+ return result;
92
+ };
93
+ const runOnce = () => {
94
+ if (inFlight == null) {
95
+ inFlight = run().finally(() => {
96
+ inFlight = undefined;
97
+ });
98
+ }
99
+ return inFlight;
100
+ };
101
+ let timer;
102
+ if (options.intervalMs > 0) {
103
+ timer = setInterval(() => {
104
+ void runOnce();
105
+ }, options.intervalMs);
106
+ timer.unref();
107
+ }
108
+ return {
109
+ runOnce,
110
+ stop: () => {
111
+ if (timer != null)
112
+ clearInterval(timer);
113
+ timer = undefined;
114
+ },
115
+ rounds,
116
+ };
117
+ }
118
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { AdmittanceInstructions, TopicManager } from '@bsv/overlay';
2
+ import type { PublisherPolicy } from '@bsv/dpp-core';
3
+ export declare const ATTESTATION_TOPIC = "tm_attestation";
4
+ export interface AttestationAdmissionOptions {
5
+ /**
6
+ * The publisher key policy chain (`spec/services.md` section 1), verified by
7
+ * the caller. When present and in force for this topic, an anchor is
8
+ * admitted only when its anchoring service is an anchor-publisher key active
9
+ * at admission time, and the static list is not consulted. Admission time,
10
+ * not the anchor's time: an anchor carries no timestamp, so the instant a
11
+ * key's window is held against is the only instant the index has. A late
12
+ * announcement of an anchor written under a since-retired key is therefore
13
+ * refused, which the operator resolves by re-announcing before retiring the
14
+ * key or by keeping the key active until the backlog is announced.
15
+ */
16
+ publisherPolicy?: PublisherPolicy[];
17
+ /** The clock admission time is read from; injectable for tests. */
18
+ now?: () => Date;
19
+ }
20
+ /** Independent anchor admission. Credential proof, authority and status require the document. */
21
+ export declare class AttestationTopicManager implements TopicManager {
22
+ private readonly acceptedAnchoringServices;
23
+ private readonly policy?;
24
+ private readonly now;
25
+ constructor(acceptedAnchoringServices?: string[], options?: AttestationAdmissionOptions);
26
+ /** The anchoring services admitted at this instant; undefined means any well-formed anchor. */
27
+ private acceptedAt;
28
+ identifyAdmissibleOutputs(beef: number[]): Promise<AdmittanceInstructions>;
29
+ getDocumentation(): Promise<string>;
30
+ getMetaData(): Promise<{
31
+ name: string;
32
+ shortDescription: string;
33
+ version: string;
34
+ }>;
35
+ }
36
+ //# sourceMappingURL=tmAttestation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmAttestation.d.ts","sourceRoot":"","sources":["../src/tmAttestation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,eAAO,MAAM,iBAAiB,mBAAmB,CAAA;AAEjD,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,eAAe,EAAE,CAAA;IACnC,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,iGAAiG;AACjG,qBAAa,uBAAwB,YAAW,YAAY;IAKxD,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAJ5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;gBAGb,yBAAyB,GAAE,MAAM,EAAO,EACzD,OAAO,GAAE,2BAAgC;IAM3C,+FAA+F;IAC/F,OAAO,CAAC,UAAU;IAQZ,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAa1E,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAMnC,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAG1F"}
@@ -0,0 +1,50 @@
1
+ import { Transaction } from '@bsv/sdk';
2
+ import { decodeAttestationAnchor, ATTESTATION_ANCHOR_PREFIX } from './attestationAnchor.js';
3
+ import { policyKeysFor } from './policyConfig.js';
4
+ export const ATTESTATION_TOPIC = 'tm_attestation';
5
+ /** Independent anchor admission. Credential proof, authority and status require the document. */
6
+ export class AttestationTopicManager {
7
+ acceptedAnchoringServices;
8
+ policy;
9
+ now;
10
+ constructor(acceptedAnchoringServices = [], options = {}) {
11
+ this.acceptedAnchoringServices = acceptedAnchoringServices;
12
+ this.policy = options.publisherPolicy != null && options.publisherPolicy.length > 0 ? options.publisherPolicy : undefined;
13
+ this.now = options.now ?? (() => new Date());
14
+ }
15
+ /** The anchoring services admitted at this instant; undefined means any well-formed anchor. */
16
+ acceptedAt(at) {
17
+ if (this.policy != null) {
18
+ const keys = policyKeysFor(this.policy, at, 'anchor-publisher', ATTESTATION_TOPIC);
19
+ if (keys != null)
20
+ return keys;
21
+ }
22
+ return this.acceptedAnchoringServices.length === 0 ? undefined : this.acceptedAnchoringServices;
23
+ }
24
+ async identifyAdmissibleOutputs(beef) {
25
+ const outputsToAdmit = [];
26
+ try {
27
+ const transaction = Transaction.fromBEEF(beef);
28
+ if (!Array.isArray(transaction.inputs) || transaction.inputs.length === 0 || !Array.isArray(transaction.outputs) || transaction.outputs.length === 0)
29
+ return { outputsToAdmit: [], coinsToRetain: [] };
30
+ const accepted = this.acceptedAt(this.now());
31
+ for (let index = 0; index < transaction.outputs.length; index++) {
32
+ const anchor = decodeAttestationAnchor(transaction.outputs[index].lockingScript);
33
+ if (anchor && (accepted == null || accepted.includes(anchor.anchoredBy)))
34
+ outputsToAdmit.push(index);
35
+ }
36
+ }
37
+ catch { /* Invalid transaction input admits no outputs. */ }
38
+ return { outputsToAdmit, coinsToRetain: [] };
39
+ }
40
+ async getDocumentation() {
41
+ const policy = this.policy == null
42
+ ? ''
43
+ : ' This index admits anchors only from anchor-publisher keys active at admission time under its publisher key policy (spec/services.md section 1); an anchor carries no timestamp, so admission time is the instant held against the key window. GET /capabilities lists the keys active now.';
44
+ return `${ATTESTATION_TOPIC} admits ${ATTESTATION_ANCHOR_PREFIX} outputs with ten fields and an exact five-OP_2DROP tail. The anchoring service signs nine length-prefixed fields. The digest commits to the complete secured representation. Carried issuer/subject/type/representation/media metadata must be checked against that document. Admission proves anchoring-service attribution, not credential validity, issuer authority, status, availability or VSC conformance. Legacy UORA layouts belong to their separately named historical topic.${policy}`;
45
+ }
46
+ async getMetaData() {
47
+ return { name: ATTESTATION_TOPIC, shortDescription: 'BSV attestation commitments with explicit representation formats', version: '1.1.0' };
48
+ }
49
+ }
50
+ //# sourceMappingURL=tmAttestation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmAttestation.js","sourceRoot":"","sources":["../src/tmAttestation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ import type { AdmittanceInstructions, Storage, TopicManager } from '@bsv/overlay';
2
+ import { type PublisherPolicy } from '@bsv/dpp-core';
3
+ export declare const DPP_TOPIC = "tm_dpp";
4
+ /**
5
+ * tm_dpp - DPP Token Standard admission, versions 1 and 2
6
+ * (`spec/record-model.md` §6 and §8, `spec/record-model-v2.md` §6 and §8).
7
+ *
8
+ * Admits a transaction's single DPP output when:
9
+ * - exactly one well-formed DPP output exists (invariant 1; field rules are
10
+ * enforced by the dpp-core codec at parse time),
11
+ * - user_signature verifies against actor_identity_key + actor_keyID,
12
+ * - server_signature verifies against the configured service key
13
+ * (admission policy, not token validity - §5),
14
+ * - genesis states satisfy the genesis rules. Non-genesis states spend the
15
+ * previously admitted tip (invariant 4, via previousCoins) and satisfy the
16
+ * per-link transition rules (invariants 5–7).
17
+ *
18
+ * Spent tips are retained (coinsToRetain) so the lifecycle history stays
19
+ * resolvable (§1: the spend history is the passport's lifecycle).
20
+ *
21
+ * One rule is a profile's choice rather than the record model's, and this
22
+ * manager enforces it only when told to: the owner-signed transfer of
23
+ * `spec/custody.md` §4, under which a TRANSFER is admitted only when its actor
24
+ * proves it is the previous owner, by equality or by owner_linkage, or is a
25
+ * named transfer authority. It is admission policy of the same kind as the
26
+ * service signature, and the topic documentation says whether this instance
27
+ * enforces it, so a writer learns the rule from the index and not from a
28
+ * refusal (`spec/services.md` §6).
29
+ */
30
+ export interface DppAdmissionOptions {
31
+ /**
32
+ * The owner-signed transfer (`spec/custody.md` §4). `true` runs the predicate
33
+ * on every TRANSFER; an object also names the transfer authorities, identity
34
+ * keys whose TRANSFER is admitted without proof (recovery). Off by default:
35
+ * the record model's baseline admits any signed TRANSFER that spends the tip.
36
+ * A malformed authority throws at construction, so a misconfigured
37
+ * deployment fails to boot rather than silently never matching.
38
+ */
39
+ ownerConsent?: boolean | {
40
+ authorities: string[];
41
+ };
42
+ /**
43
+ * The publisher key policy chain (`spec/services.md` §1), already verified
44
+ * by the caller with `verifyPolicyChain`. When present and in force for this
45
+ * topic, a state's countersignature is accepted only from a state-publisher
46
+ * key active at the state's own timestamp, and the positional identity key
47
+ * is not consulted: a retired key still admits the states it signed while
48
+ * active, and a key admits nothing timestamped before its activation.
49
+ * Absent, the identity key is the whole policy, as it always was.
50
+ */
51
+ publisherPolicy?: PublisherPolicy[];
52
+ /**
53
+ * Authorities for version 2 control proofs (`spec/record-model-v2.md` §6):
54
+ * identity keys whose UPDATE, TRANSFER or RETIRE is admitted without a
55
+ * control proof. Defaults to the `ownerConsent` authorities, so one list
56
+ * serves both versions unless a deployment names another. Malformed keys
57
+ * throw at construction.
58
+ */
59
+ controlAuthorities?: string[];
60
+ /**
61
+ * The managed-custody profile (`spec/managed-custody.md`): when selected, a
62
+ * version 2 TRANSFER is admitted only when it carries an
63
+ * `authorisation_commitment`. Off by default: the record model admits a
64
+ * TRANSFER with an empty commitment, and the topic documentation says which
65
+ * this instance runs.
66
+ */
67
+ managedAcceptance?: boolean;
68
+ /**
69
+ * The engine's own storage, so the manager can read an admitted
70
+ * predecessor's locking script when the BEEF it is handed does not carry
71
+ * the predecessor's bytes. Two callers hand it such a BEEF: a writer that
72
+ * announces a proven state alone, and the SDK's peer synchronisation, whose
73
+ * per-node BEEF stops at the node's proof and whose admission emulation
74
+ * lists as previous coins only what the temporary graph holds. Absent, the
75
+ * manager sees only what the BEEF and its own recent memory show, as it
76
+ * always did.
77
+ */
78
+ admittedOutputs?: Pick<Storage, 'findOutput'> & Partial<Pick<Storage, 'findOutputsForTransaction'>>;
79
+ }
80
+ export declare class DppTopicManager implements TopicManager {
81
+ private readonly serverIdentityKey;
82
+ private readonly consentSelected;
83
+ private readonly authorities;
84
+ private readonly controlAuthorities;
85
+ private readonly managedAcceptance;
86
+ private readonly policy?;
87
+ private readonly admitted?;
88
+ /** States inspected recently, by txid: the predecessor a synchronisation pass showed one call ago. */
89
+ private readonly recent;
90
+ constructor(serverIdentityKey: string, options?: DppAdmissionOptions);
91
+ /**
92
+ * The keys this state's countersignature may come from: under a policy, the
93
+ * state-publisher keys active at the state's timestamp; otherwise the one
94
+ * configured identity key. The state's own timestamp is the instant that
95
+ * matters, not the admission time, because a state announced late is still
96
+ * the state it was when it was signed.
97
+ */
98
+ private publisherKeysFor;
99
+ private remember;
100
+ /**
101
+ * The predecessor's locking script for one input, from the nearest source
102
+ * that has it: the hydrated input, the submitted BEEF, the engine's storage,
103
+ * and, on the historical paths only, the manager's recent memory. The txid
104
+ * the input names commits to the bytes each source yields, so none of them
105
+ * is trusted more than another; they differ only in who happened to hand
106
+ * the bytes over. Memory is kept for the synchronisation pass, where the SDK
107
+ * validates a graph one state at a time before any of it reaches storage;
108
+ * on the live path a writer's proven state announced without its parent is
109
+ * judged against what the index holds, never against what it happened to
110
+ * see, so the contract a submitter is held to stays the one it always was.
111
+ */
112
+ private predecessorScript;
113
+ /**
114
+ * The genesis outpoint of the lineage a version 2 successor must name
115
+ * (`spec/record-model-v2.md` §6). A version 2 predecessor carries it; the
116
+ * ISSUE is it. A version 1 predecessor carries none, so the manager walks
117
+ * the admitted history back to the genesis through the same sources the
118
+ * predecessor's own bytes came from: the submitted BEEF, the engine's
119
+ * storage, and on the historical paths its recent memory. Undefined when
120
+ * the walk cannot be completed, which refuses the state: a lineage this
121
+ * index cannot trace is not one it can vouch for.
122
+ */
123
+ private lineageGenesisFor;
124
+ /** The admitted DPP state a transaction carries, by txid, from the BEEF, the engine's storage or recent memory. */
125
+ private stateAt;
126
+ /**
127
+ * The inputs to treat as previously admitted coins. The Engine names them
128
+ * on the live path; the synchronisation emulation names only what its
129
+ * temporary graph holds, which for a state on top of a lineage this index
130
+ * already has is nothing. Then, and only when the storage is at hand, the
131
+ * inputs that spend an output this index has admitted for the topic stand
132
+ * in, which is the same question the Engine answers on the live path.
133
+ */
134
+ private previousCoinsFor;
135
+ identifyAdmissibleOutputs(beef: number[], previousCoins: number[], _offChainValues?: number[], mode?: 'historical-tx' | 'current-tx' | 'historical-tx-no-spv'): Promise<AdmittanceInstructions>;
136
+ /**
137
+ * What a synchronising peer must fetch before this state can be judged: the
138
+ * input that spends the predecessor, when the state is not a genesis. The
139
+ * SDK asks this only for a proven state it could not admit alone, and drops
140
+ * any input this index already holds, so the answer is the predecessor and
141
+ * nothing else. Anything that does not decode as one DPP state needs nothing.
142
+ */
143
+ identifyNeededInputs(beef: number[]): Promise<Array<{
144
+ txid: string;
145
+ outputIndex: number;
146
+ }>>;
147
+ getDocumentation(): Promise<string>;
148
+ getMetaData(): Promise<{
149
+ name: string;
150
+ shortDescription: string;
151
+ version?: string;
152
+ }>;
153
+ }
154
+ //# sourceMappingURL=tmDpp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmDpp.d.ts","sourceRoot":"","sources":["../src/tmDpp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACjF,OAAO,EAaL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAA;AAGtB,eAAO,MAAM,SAAS,WAAW,CAAA;AAqBjC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG;QAAE,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAClD;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,eAAe,EAAE,CAAA;IACnC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAA;CACpG;AAED,qBAAa,eAAgB,YAAW,YAAY;IAWhD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAVpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAmF;IAC7G,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAGrC,iBAAiB,EAAE,MAAM,EAC1C,OAAO,GAAE,mBAAwB;IAanC;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,QAAQ;IAUhB;;;;;;;;;;;OAWG;YACW,iBAAiB;IAsB/B;;;;;;;;;OASG;YACW,iBAAiB;IAuB/B,mHAAmH;YACrG,OAAO;IA2BrB;;;;;;;OAOG;YACW,gBAAgB;IAWxB,yBAAyB,CAC7B,IAAI,EAAE,MAAM,EAAE,EACd,aAAa,EAAE,MAAM,EAAE,EACvB,eAAe,CAAC,EAAE,MAAM,EAAE,EAC1B,IAAI,CAAC,EAAE,eAAe,GAAG,YAAY,GAAG,sBAAsB,GAC7D,OAAO,CAAC,sBAAsB,CAAC;IA4IlC;;;;;;OAMG;IACG,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAe3F,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IA6DnC,WAAW,IAAI,OAAO,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAA;QACZ,gBAAgB,EAAE,MAAM,CAAA;QACxB,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAC;CAOH"}