@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
package/dist/tmDpp.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { Beef, LockingScript, Transaction } from '@bsv/sdk';
|
|
2
|
+
import { ACCEPTANCE_COMMITMENT_REFUSAL, MANAGED_CUSTODY_PROFILE, checkGenesisState, checkOwnerConsent, checkTransition, findDppOutputs, normaliseTransferAuthorities, tryParseDppOutput, verifyServerSignature, verifyUserSignature, } from '@bsv/dpp-core';
|
|
3
|
+
import { policyKeysFor } from './policyConfig.js';
|
|
4
|
+
export const DPP_TOPIC = 'tm_dpp';
|
|
5
|
+
/**
|
|
6
|
+
* A fresh object each time, never a shared constant: the Engine writes
|
|
7
|
+
* `coinsRemoved` onto the instructions a topic manager returns, so a shared
|
|
8
|
+
* refusal object would carry the first refusal's mutation into every later
|
|
9
|
+
* answer.
|
|
10
|
+
*/
|
|
11
|
+
const none = () => ({ outputsToAdmit: [], coinsToRetain: [] });
|
|
12
|
+
/**
|
|
13
|
+
* How many recently inspected states the manager remembers, by txid. A
|
|
14
|
+
* synchronisation pass hands the manager one state at a time in lineage
|
|
15
|
+
* order, each as a BEEF that stops at its own proof, so the predecessor's
|
|
16
|
+
* bytes arrive one call earlier and nowhere else; this is where they wait.
|
|
17
|
+
*/
|
|
18
|
+
const RECENT_STATES = 4096;
|
|
19
|
+
/** The most predecessors the manager walks back to find a version 1 lineage's genesis before it gives up. */
|
|
20
|
+
const MAX_LINEAGE_WALK = 10_000;
|
|
21
|
+
export class DppTopicManager {
|
|
22
|
+
serverIdentityKey;
|
|
23
|
+
consentSelected;
|
|
24
|
+
authorities;
|
|
25
|
+
controlAuthorities;
|
|
26
|
+
managedAcceptance;
|
|
27
|
+
policy;
|
|
28
|
+
admitted;
|
|
29
|
+
/** States inspected recently, by txid: the predecessor a synchronisation pass showed one call ago. */
|
|
30
|
+
recent = new Map();
|
|
31
|
+
constructor(serverIdentityKey, options = {}) {
|
|
32
|
+
this.serverIdentityKey = serverIdentityKey;
|
|
33
|
+
this.consentSelected = options.ownerConsent != null && options.ownerConsent !== false;
|
|
34
|
+
this.authorities =
|
|
35
|
+
typeof options.ownerConsent === 'object'
|
|
36
|
+
? normaliseTransferAuthorities(options.ownerConsent.authorities)
|
|
37
|
+
: [];
|
|
38
|
+
this.controlAuthorities = options.controlAuthorities != null ? normaliseTransferAuthorities(options.controlAuthorities) : this.authorities;
|
|
39
|
+
this.managedAcceptance = options.managedAcceptance === true;
|
|
40
|
+
this.policy = options.publisherPolicy != null && options.publisherPolicy.length > 0 ? options.publisherPolicy : undefined;
|
|
41
|
+
this.admitted = options.admittedOutputs;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The keys this state's countersignature may come from: under a policy, the
|
|
45
|
+
* state-publisher keys active at the state's timestamp; otherwise the one
|
|
46
|
+
* configured identity key. The state's own timestamp is the instant that
|
|
47
|
+
* matters, not the admission time, because a state announced late is still
|
|
48
|
+
* the state it was when it was signed.
|
|
49
|
+
*/
|
|
50
|
+
publisherKeysFor(state) {
|
|
51
|
+
if (this.policy != null) {
|
|
52
|
+
const keys = policyKeysFor(this.policy, state.timestamp, 'state-publisher', DPP_TOPIC);
|
|
53
|
+
if (keys != null)
|
|
54
|
+
return keys;
|
|
55
|
+
}
|
|
56
|
+
return this.serverIdentityKey === '' ? [] : [this.serverIdentityKey];
|
|
57
|
+
}
|
|
58
|
+
remember(tx) {
|
|
59
|
+
const txid = tx.id('hex');
|
|
60
|
+
if (this.recent.has(txid))
|
|
61
|
+
return;
|
|
62
|
+
if (this.recent.size >= RECENT_STATES) {
|
|
63
|
+
const oldest = this.recent.keys().next().value;
|
|
64
|
+
if (oldest != null)
|
|
65
|
+
this.recent.delete(oldest);
|
|
66
|
+
}
|
|
67
|
+
this.recent.set(txid, tx);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The predecessor's locking script for one input, from the nearest source
|
|
71
|
+
* that has it: the hydrated input, the submitted BEEF, the engine's storage,
|
|
72
|
+
* and, on the historical paths only, the manager's recent memory. The txid
|
|
73
|
+
* the input names commits to the bytes each source yields, so none of them
|
|
74
|
+
* is trusted more than another; they differ only in who happened to hand
|
|
75
|
+
* the bytes over. Memory is kept for the synchronisation pass, where the SDK
|
|
76
|
+
* validates a graph one state at a time before any of it reaches storage;
|
|
77
|
+
* on the live path a writer's proven state announced without its parent is
|
|
78
|
+
* judged against what the index holds, never against what it happened to
|
|
79
|
+
* see, so the contract a submitter is held to stays the one it always was.
|
|
80
|
+
*/
|
|
81
|
+
async predecessorScript(input, prevTxid, parsedBeef, historical) {
|
|
82
|
+
const outputIndex = input.sourceOutputIndex;
|
|
83
|
+
const hydrated = input.sourceTransaction?.outputs[outputIndex]?.lockingScript;
|
|
84
|
+
if (hydrated != null)
|
|
85
|
+
return hydrated;
|
|
86
|
+
const fromBeef = parsedBeef?.findTxid(prevTxid)?.tx?.outputs[outputIndex]?.lockingScript;
|
|
87
|
+
if (fromBeef != null)
|
|
88
|
+
return fromBeef;
|
|
89
|
+
if (this.admitted != null) {
|
|
90
|
+
const output = await this.admitted.findOutput(prevTxid, outputIndex, DPP_TOPIC);
|
|
91
|
+
if (output != null)
|
|
92
|
+
return LockingScript.fromBinary(output.outputScript);
|
|
93
|
+
}
|
|
94
|
+
if (historical) {
|
|
95
|
+
const remembered = this.recent.get(prevTxid)?.outputs[outputIndex]?.lockingScript;
|
|
96
|
+
if (remembered != null)
|
|
97
|
+
return remembered;
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The genesis outpoint of the lineage a version 2 successor must name
|
|
103
|
+
* (`spec/record-model-v2.md` §6). A version 2 predecessor carries it; the
|
|
104
|
+
* ISSUE is it. A version 1 predecessor carries none, so the manager walks
|
|
105
|
+
* the admitted history back to the genesis through the same sources the
|
|
106
|
+
* predecessor's own bytes came from: the submitted BEEF, the engine's
|
|
107
|
+
* storage, and on the historical paths its recent memory. Undefined when
|
|
108
|
+
* the walk cannot be completed, which refuses the state: a lineage this
|
|
109
|
+
* index cannot trace is not one it can vouch for.
|
|
110
|
+
*/
|
|
111
|
+
async lineageGenesisFor(prev, prevTxid, prevOutputIndex, parsedBeef, historical) {
|
|
112
|
+
if (prev.version === '2')
|
|
113
|
+
return prev.lineageGenesis ?? { txid: prevTxid, outputIndex: prevOutputIndex };
|
|
114
|
+
let state = prev;
|
|
115
|
+
let txid = prevTxid;
|
|
116
|
+
let outputIndex = prevOutputIndex;
|
|
117
|
+
for (let hops = 0; hops < MAX_LINEAGE_WALK; hops++) {
|
|
118
|
+
if (state.previousTxid === '')
|
|
119
|
+
return { txid, outputIndex };
|
|
120
|
+
const earlierTxid = state.previousTxid;
|
|
121
|
+
const earlier = await this.stateAt(earlierTxid, parsedBeef, historical);
|
|
122
|
+
if (earlier == null)
|
|
123
|
+
return undefined;
|
|
124
|
+
state = earlier.state;
|
|
125
|
+
txid = earlierTxid;
|
|
126
|
+
outputIndex = earlier.outputIndex;
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
/** The admitted DPP state a transaction carries, by txid, from the BEEF, the engine's storage or recent memory. */
|
|
131
|
+
async stateAt(txid, parsedBeef, historical) {
|
|
132
|
+
const fromBeef = parsedBeef?.findTxid(txid)?.tx;
|
|
133
|
+
const candidates = [];
|
|
134
|
+
if (fromBeef != null)
|
|
135
|
+
candidates.push(fromBeef);
|
|
136
|
+
if (historical) {
|
|
137
|
+
const remembered = this.recent.get(txid);
|
|
138
|
+
if (remembered != null)
|
|
139
|
+
candidates.push(remembered);
|
|
140
|
+
}
|
|
141
|
+
for (const tx of candidates) {
|
|
142
|
+
const refs = findDppOutputs(tx);
|
|
143
|
+
if (refs.length === 1)
|
|
144
|
+
return { state: refs[0].state, outputIndex: refs[0].outputIndex };
|
|
145
|
+
}
|
|
146
|
+
if (this.admitted?.findOutputsForTransaction != null) {
|
|
147
|
+
const outputs = await this.admitted.findOutputsForTransaction(txid);
|
|
148
|
+
for (const output of outputs) {
|
|
149
|
+
if (output.topic !== DPP_TOPIC)
|
|
150
|
+
continue;
|
|
151
|
+
const parsed = tryParseDppOutput(LockingScript.fromBinary(output.outputScript));
|
|
152
|
+
if (parsed != null)
|
|
153
|
+
return { state: parsed.state, outputIndex: output.outputIndex };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The inputs to treat as previously admitted coins. The Engine names them
|
|
160
|
+
* on the live path; the synchronisation emulation names only what its
|
|
161
|
+
* temporary graph holds, which for a state on top of a lineage this index
|
|
162
|
+
* already has is nothing. Then, and only when the storage is at hand, the
|
|
163
|
+
* inputs that spend an output this index has admitted for the topic stand
|
|
164
|
+
* in, which is the same question the Engine answers on the live path.
|
|
165
|
+
*/
|
|
166
|
+
async previousCoinsFor(tx, state, previousCoins) {
|
|
167
|
+
if (previousCoins.length > 0 || this.admitted == null)
|
|
168
|
+
return previousCoins;
|
|
169
|
+
const found = [];
|
|
170
|
+
for (const [index, input] of tx.inputs.entries()) {
|
|
171
|
+
const sourceTxid = input.sourceTransaction?.id('hex') ?? input.sourceTXID;
|
|
172
|
+
if (sourceTxid !== state.previousTxid)
|
|
173
|
+
continue;
|
|
174
|
+
if ((await this.admitted.findOutput(sourceTxid, input.sourceOutputIndex, DPP_TOPIC)) != null)
|
|
175
|
+
found.push(index);
|
|
176
|
+
}
|
|
177
|
+
return found;
|
|
178
|
+
}
|
|
179
|
+
async identifyAdmissibleOutputs(beef, previousCoins, _offChainValues, mode) {
|
|
180
|
+
// The SDK's synchronisation names its passes: 'historical-tx' while it
|
|
181
|
+
// validates a fetched graph, 'historical-tx-no-spv' while it admits one.
|
|
182
|
+
const historical = mode === 'historical-tx' || mode === 'historical-tx-no-spv';
|
|
183
|
+
let tx;
|
|
184
|
+
try {
|
|
185
|
+
tx = Transaction.fromBEEF(beef);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return none();
|
|
189
|
+
}
|
|
190
|
+
/*
|
|
191
|
+
* A refusal keeps the coins the Engine offered. Without this the Engine
|
|
192
|
+
* reads a refused spend of an admitted tip as that tip's consumption by a
|
|
193
|
+
* transaction the topic did not care to retain, and its stale-output
|
|
194
|
+
* eviction removes the tip and, recursively, every predecessor nothing
|
|
195
|
+
* else consumes: one refused announcement emptied a passport's index
|
|
196
|
+
* entry in the 2026-08-12 rehearsal. Retaining the offered coins on
|
|
197
|
+
* refusal is the documented half of the contract that keeps the lineage.
|
|
198
|
+
* What the Engine still does with a refused transaction is written up in
|
|
199
|
+
* this package's README (a refused spend of the tip): it marks the tip
|
|
200
|
+
* spent in its own storage, tells the lookup services of the spend, and
|
|
201
|
+
* records the transaction as applied to the topic.
|
|
202
|
+
*/
|
|
203
|
+
const refuse = () => previousCoins.length === 0 ? none() : { outputsToAdmit: [], coinsToRetain: [...previousCoins] };
|
|
204
|
+
const refs = findDppOutputs(tx);
|
|
205
|
+
if (refs.length !== 1)
|
|
206
|
+
return refuse();
|
|
207
|
+
const { state, outputIndex } = refs[0];
|
|
208
|
+
if (!verifyUserSignature(state))
|
|
209
|
+
return refuse();
|
|
210
|
+
if (!this.publisherKeysFor(state).some((key) => verifyServerSignature(state, key))) {
|
|
211
|
+
if (this.policy != null) {
|
|
212
|
+
// Under a policy the refusal can be a matter of time rather than of
|
|
213
|
+
// key: a writer signing with a key that has since retired, or that
|
|
214
|
+
// was not yet active at the state's timestamp, learns which from the
|
|
215
|
+
// operator's log, since the wire says only that nothing was admitted.
|
|
216
|
+
console.warn(`${DPP_TOPIC} refused ${tx.id('hex')}: server_signature is not from a state-publisher key active at ${state.timestamp}`);
|
|
217
|
+
}
|
|
218
|
+
return refuse();
|
|
219
|
+
}
|
|
220
|
+
if (state.previousTxid === '') {
|
|
221
|
+
if (checkGenesisState(state) != null)
|
|
222
|
+
return refuse();
|
|
223
|
+
if (previousCoins.length > 0) {
|
|
224
|
+
// A genesis spends no passport state. One that consumes an admitted
|
|
225
|
+
// coin would end the lineage it spends without a RETIRE and without
|
|
226
|
+
// naming it, which the chain rules refuse as a second genesis
|
|
227
|
+
// (record-model-v2.md section 6, invariant 3); the coin stays.
|
|
228
|
+
console.warn(`${DPP_TOPIC} refused ${tx.id('hex')}: a genesis state must not spend an admitted passport output`);
|
|
229
|
+
return refuse();
|
|
230
|
+
}
|
|
231
|
+
this.remember(tx);
|
|
232
|
+
return { outputsToAdmit: [outputIndex], coinsToRetain: [] };
|
|
233
|
+
}
|
|
234
|
+
/*
|
|
235
|
+
* The predecessor's bytes, found in the submitted BEEF by txid rather than
|
|
236
|
+
* read off `input.sourceTransaction`, and the distinction is a defect the
|
|
237
|
+
* 2026-08-12 backfill rehearsal caught: every mined event on every
|
|
238
|
+
* multi-state passport was refused, and the refusal consumed the admitted
|
|
239
|
+
* genesis coin, so one backfill pass left those passports with an empty
|
|
240
|
+
* index entry.
|
|
241
|
+
*
|
|
242
|
+
* The SDK's writer and reader agree on a rule this check used to be on the
|
|
243
|
+
* wrong side of. A transaction carrying its own merkle proof needs no
|
|
244
|
+
* ancestry for SPV, so `Beef.mergeTransaction` stops walking parents at a
|
|
245
|
+
* proof (`Beef.js:333`) and the reader symmetrically leaves
|
|
246
|
+
* `input.sourceTransaction` unset on a proven subject, even when the
|
|
247
|
+
* parent's bytes sit in the same BEEF. SPV is the wrong standard here:
|
|
248
|
+
* this check wants the predecessor's locking script to hold the transition
|
|
249
|
+
* rules against, which is a stricter need than proof of inclusion. So the
|
|
250
|
+
* BEEF is searched directly, hydration is taken when the SDK happens to
|
|
251
|
+
* provide it (a fresh unproven state, which is the live announce path),
|
|
252
|
+
* and the submitter's side of the contract is to include the predecessor's
|
|
253
|
+
* bytes, which `scripts/announce.ts` does by submitting each state atomic
|
|
254
|
+
* over the whole stored chain. Since peer synchronisation, two more
|
|
255
|
+
* sources stand behind those (`predecessorScript`).
|
|
256
|
+
*/
|
|
257
|
+
let parsedBeef;
|
|
258
|
+
try {
|
|
259
|
+
parsedBeef = Beef.fromBinary(beef);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
parsedBeef = undefined;
|
|
263
|
+
}
|
|
264
|
+
const coins = await this.previousCoinsFor(tx, state, previousCoins);
|
|
265
|
+
for (const inputIndex of coins) {
|
|
266
|
+
const input = tx.inputs[inputIndex];
|
|
267
|
+
if (input == null)
|
|
268
|
+
continue;
|
|
269
|
+
const prevTxid = input.sourceTransaction?.id('hex') ?? input.sourceTXID;
|
|
270
|
+
if (prevTxid !== state.previousTxid)
|
|
271
|
+
continue;
|
|
272
|
+
const prevScript = await this.predecessorScript(input, prevTxid, parsedBeef, historical);
|
|
273
|
+
if (prevScript == null)
|
|
274
|
+
continue;
|
|
275
|
+
const prev = tryParseDppOutput(prevScript);
|
|
276
|
+
if (prev == null)
|
|
277
|
+
continue;
|
|
278
|
+
const lineageGenesis = state.version === '2'
|
|
279
|
+
? await this.lineageGenesisFor(prev.state, prevTxid, input.sourceOutputIndex, parsedBeef, historical)
|
|
280
|
+
: undefined;
|
|
281
|
+
if (state.version === '2' && lineageGenesis == null) {
|
|
282
|
+
console.warn(`${DPP_TOPIC} refused ${tx.id('hex')}: the lineage genesis could not be traced from the admitted history`);
|
|
283
|
+
return refuse();
|
|
284
|
+
}
|
|
285
|
+
const link = checkTransition(prev.state, state, prevTxid, {
|
|
286
|
+
prevOutputIndex: input.sourceOutputIndex,
|
|
287
|
+
lineageGenesis,
|
|
288
|
+
authorities: this.controlAuthorities,
|
|
289
|
+
});
|
|
290
|
+
if (link != null) {
|
|
291
|
+
// A version 2 refusal names a rule the writer may not know this
|
|
292
|
+
// instance applies (a named authority, the upgrade path), so the
|
|
293
|
+
// operator's log names it; version 1 refusals stay silent as before.
|
|
294
|
+
if (state.version === '2')
|
|
295
|
+
console.warn(`${DPP_TOPIC} refused ${tx.id('hex')}: ${link}`);
|
|
296
|
+
return refuse();
|
|
297
|
+
}
|
|
298
|
+
if (state.version === '1' && this.consentSelected) {
|
|
299
|
+
const reason = checkOwnerConsent(prev.state, state, this.authorities);
|
|
300
|
+
if (reason != null) {
|
|
301
|
+
// The other refusals are silent because the wire says everything a
|
|
302
|
+
// writer needs. This one is a policy the writer may not know this
|
|
303
|
+
// instance runs, so the operator's log names it.
|
|
304
|
+
console.warn(`${DPP_TOPIC} refused ${tx.id('hex')}: ${reason}`);
|
|
305
|
+
return refuse();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (state.version === '2' && state.op === 'TRANSFER' && this.managedAcceptance && state.authorisationCommitment === '') {
|
|
309
|
+
console.warn(`${DPP_TOPIC} refused ${tx.id('hex')}: ${ACCEPTANCE_COMMITMENT_REFUSAL}`);
|
|
310
|
+
return refuse();
|
|
311
|
+
}
|
|
312
|
+
this.remember(tx);
|
|
313
|
+
return { outputsToAdmit: [outputIndex], coinsToRetain: [inputIndex] };
|
|
314
|
+
}
|
|
315
|
+
// No previously admitted coin matches previous_txid: not the admitted tip.
|
|
316
|
+
return refuse();
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* What a synchronising peer must fetch before this state can be judged: the
|
|
320
|
+
* input that spends the predecessor, when the state is not a genesis. The
|
|
321
|
+
* SDK asks this only for a proven state it could not admit alone, and drops
|
|
322
|
+
* any input this index already holds, so the answer is the predecessor and
|
|
323
|
+
* nothing else. Anything that does not decode as one DPP state needs nothing.
|
|
324
|
+
*/
|
|
325
|
+
async identifyNeededInputs(beef) {
|
|
326
|
+
let tx;
|
|
327
|
+
try {
|
|
328
|
+
tx = Transaction.fromBEEF(beef);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
const refs = findDppOutputs(tx);
|
|
334
|
+
if (refs.length !== 1 || refs[0].state.previousTxid === '')
|
|
335
|
+
return [];
|
|
336
|
+
const prevTxid = refs[0].state.previousTxid;
|
|
337
|
+
return tx.inputs
|
|
338
|
+
.filter((input) => (input.sourceTransaction?.id('hex') ?? input.sourceTXID) === prevTxid)
|
|
339
|
+
.map((input) => ({ txid: prevTxid, outputIndex: input.sourceOutputIndex }));
|
|
340
|
+
}
|
|
341
|
+
async getDocumentation() {
|
|
342
|
+
const lines = [
|
|
343
|
+
`# ${DPP_TOPIC}`,
|
|
344
|
+
'',
|
|
345
|
+
'Admission for DPP Token Standard passports, record versions 1 and 2',
|
|
346
|
+
'(spec/record-model.md, spec/record-model-v2.md). One DPP output per',
|
|
347
|
+
'transaction; signatures over the version\'s own preimage (unframed for',
|
|
348
|
+
'version 1, framed and domain-tagged for version 2); non-genesis states',
|
|
349
|
+
'must spend the admitted tip; spent states are retained as lifecycle',
|
|
350
|
+
'history. A version 2 state also binds the predecessor outpoint and the',
|
|
351
|
+
'lineage genesis, proves control of the predecessor on every UPDATE,',
|
|
352
|
+
'TRANSFER and RETIRE, and nothing is admitted after a RETIRE. A version 2',
|
|
353
|
+
'UPDATE may spend a version 1 tip, which upgrades the lineage; a version 1',
|
|
354
|
+
'state never spends a version 2 tip. Admission additionally requires a',
|
|
355
|
+
'valid publisher signature (deployment policy, spec/record-model.md §8).',
|
|
356
|
+
'A refused state that spends the admitted tip leaves the tip and its',
|
|
357
|
+
'history in place. The same rules judge a state a peer offers during',
|
|
358
|
+
'synchronisation as one a writer announces.',
|
|
359
|
+
'',
|
|
360
|
+
'## The publisher key',
|
|
361
|
+
'',
|
|
362
|
+
];
|
|
363
|
+
if (this.policy != null) {
|
|
364
|
+
const newest = this.policy[this.policy.length - 1];
|
|
365
|
+
lines.push(`This index admits under a publisher key policy (spec/services.md section 1, dpp-publisher-policy@1), currently version ${newest.policyVersion} for the ${newest.scope.operatorProfile} scope. A state's server_signature must come from a state-publisher key that was active at the state's own timestamp: a retired key still admits the states it signed while active, and a key admits nothing timestamped before its activation. GET /capabilities lists the keys active now.`);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
lines.push('This index admits states countersigned by its one configured service identity key, an implicit single-operator policy with no rotation history. GET /capabilities names the key.');
|
|
369
|
+
}
|
|
370
|
+
lines.push('', '## Control authorities (version 2)', '');
|
|
371
|
+
lines.push(this.controlAuthorities.length === 0
|
|
372
|
+
? 'Control authorities: none. Every version 2 UPDATE, TRANSFER and RETIRE proves control of the predecessor by equality or by control_linkage.'
|
|
373
|
+
: `Control authorities: ${this.controlAuthorities.join(', ')}. A version 2 UPDATE, TRANSFER or RETIRE by one of these keys is admitted without a control proof; the profile expects it to be attested on the anchor rail.`);
|
|
374
|
+
lines.push('', `## The managed-custody profile (${MANAGED_CUSTODY_PROFILE})`, '');
|
|
375
|
+
lines.push(this.managedAcceptance
|
|
376
|
+
? 'This index is deployed for the managed-custody profile (spec/managed-custody.md): a version 2 TRANSFER is admitted only when its authorisation_commitment names the acceptance record the custodian retained.'
|
|
377
|
+
: 'Not enforced by this index, which admits a version 2 TRANSFER with an empty authorisation_commitment: the record model baseline. The managed-custody profile is switched on when the topic manager is configured for it (ACCEPTANCE_COMMITMENT=required).');
|
|
378
|
+
lines.push('', '## The owner-signed transfer (version 1)', '');
|
|
379
|
+
if (this.consentSelected) {
|
|
380
|
+
lines.push('This index is deployed for a profile that selects the owner-signed transfer (spec/custody.md section 4). A TRANSFER is admitted only when its actor is the previous owner, proven by equality (actor_identity_key equals the previous owner_identity_key) or by linkage (event_data carries owner_linkage, 64 lower-case hex characters, with the previous owner_identity_key equal to actor_identity_key plus owner_linkage times G), or when the actor is a transfer authority named below. The predicate is evaluated in that order: equality, authorities, linkage.', '', this.authorities.length === 0
|
|
381
|
+
? 'Transfer authorities: none. Every TRANSFER must prove consent.'
|
|
382
|
+
: `Transfer authorities: ${this.authorities.join(', ')}. A TRANSFER by one of these keys is admitted without proof of consent; the profile expects it to be attested on the anchor rail.`);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
lines.push('Not enforced by this index, which admits any signed TRANSFER that spends the tip: the record model baseline. The owner-signed transfer (spec/custody.md section 4) is a profile choice, switched on when the topic manager is configured for it.');
|
|
386
|
+
}
|
|
387
|
+
return lines.join('\n');
|
|
388
|
+
}
|
|
389
|
+
async getMetaData() {
|
|
390
|
+
return {
|
|
391
|
+
name: DPP_TOPIC,
|
|
392
|
+
shortDescription: 'Digital Product Passport token admission (DPP Standard record versions 1 and 2)',
|
|
393
|
+
version: '2.0.0',
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
//# sourceMappingURL=tmDpp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmDpp.js","sourceRoot":"","sources":["../src/tmDpp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AdmittanceInstructions, TopicManager } from '@bsv/overlay';
|
|
2
|
+
/**
|
|
3
|
+
* `tm_uora_dpp`: admission for UORA attestation anchors.
|
|
4
|
+
*
|
|
5
|
+
* The second rail. `tm_dpp` admits the passport itself, a chain of states each
|
|
6
|
+
* spending the last; this one admits the anchors that say what a named party
|
|
7
|
+
* claimed about a passport and when. They are deliberately separate topics
|
|
8
|
+
* rather than one: they carry different formats, they are written by different
|
|
9
|
+
* treasuries, and an instance may reasonably want one and not the other.
|
|
10
|
+
*
|
|
11
|
+
* ## How this differs from `tm_dpp`, and why the rules are shorter
|
|
12
|
+
*
|
|
13
|
+
* An anchor is a leaf. It is never spent, it has no predecessor and no
|
|
14
|
+
* transition rules apply, so `previousCoins` is not consulted and nothing is
|
|
15
|
+
* retained. Everything admissible about one is legible in the output itself,
|
|
16
|
+
* which is the property that makes the format worth versioning.
|
|
17
|
+
*
|
|
18
|
+
* **Every valid anchor in the transaction is admitted, not exactly one.**
|
|
19
|
+
* `tm_dpp` insists on exactly one output because a second passport state in the
|
|
20
|
+
* same transaction would make "the tip" ambiguous. Anchors have no such
|
|
21
|
+
* relation to each other: each is independent and self-validating. Admitting
|
|
22
|
+
* all of them means a service can batch a fleet's worth of attestations into
|
|
23
|
+
* one transaction later without the topic needing to change, which at battery
|
|
24
|
+
* scale is the difference between one anchor per event being affordable and not.
|
|
25
|
+
*
|
|
26
|
+
* ## The admission policy
|
|
27
|
+
*
|
|
28
|
+
* Structural validity is `tryParseUoraAnchor`, and it already includes the
|
|
29
|
+
* attribution: the prefix, a 64-character digest, a resolvable `did:key`
|
|
30
|
+
* issuer, a subject, a type, the anchoring service's identity key, the appended
|
|
31
|
+
* signature sealing all seven **at the boundaries they were written at**, and
|
|
32
|
+
* the locking key being the BRC-42 child of the service key the output itself
|
|
33
|
+
* names. Producing an output that passes needs that key's private half, so
|
|
34
|
+
* every admitted anchor is attributable without this topic being told anything.
|
|
35
|
+
*
|
|
36
|
+
* That is the part v1 could not express, and the reason the service key is a
|
|
37
|
+
* field rather than a configuration value. A shared overlay serving several DPP
|
|
38
|
+
* deployments should not need a key list amended every time one is added, and a
|
|
39
|
+
* key list is not a security boundary anyway: it is a preference about whose
|
|
40
|
+
* anchors to carry.
|
|
41
|
+
*
|
|
42
|
+
* Which is exactly what the optional list is. Name services and this instance
|
|
43
|
+
* carries only theirs; name none and it carries any well-formed anchor, each of
|
|
44
|
+
* which still says whose it is.
|
|
45
|
+
*
|
|
46
|
+
* ## Only v3
|
|
47
|
+
*
|
|
48
|
+
* v2 is not admitted, and the reason is not tidiness. A v2 signature covers the
|
|
49
|
+
* field bytes run together, so it does not fix where the subject stops and the
|
|
50
|
+
* type starts, and any holder of a v2 anchor can re-cut that boundary and keep
|
|
51
|
+
* the signature. An index built on those fields would then be keyed on a
|
|
52
|
+
* subject nobody signed. `uoraAnchor.ts` sets out the whole of it.
|
|
53
|
+
*
|
|
54
|
+
* Nothing in the `TopicManager` contract lets a topic explain a refusal: the
|
|
55
|
+
* answer is a list of indices, and an absent index says only "not this one". So
|
|
56
|
+
* a v2 anchor is also warned about on the node's own console, because a
|
|
57
|
+
* treasury still writing v2 would otherwise get a 200 and an empty list back
|
|
58
|
+
* and have no way to tell that from a transaction carrying no anchors. A
|
|
59
|
+
* submitter that cares still has to read the admitted list rather than the HTTP
|
|
60
|
+
* status.
|
|
61
|
+
*/
|
|
62
|
+
export declare class UoraAnchorTopicManager implements TopicManager {
|
|
63
|
+
private readonly accepted;
|
|
64
|
+
constructor(anchorServiceKeys?: readonly string[]);
|
|
65
|
+
identifyAdmissibleOutputs(beef: number[]): Promise<AdmittanceInstructions>;
|
|
66
|
+
getDocumentation(): Promise<string>;
|
|
67
|
+
getMetaData(): Promise<{
|
|
68
|
+
name: string;
|
|
69
|
+
shortDescription: string;
|
|
70
|
+
version?: string;
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=tmUoraDpp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmUoraDpp.d.ts","sourceRoot":"","sources":["../src/tmUoraDpp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAUxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;gBAEhC,iBAAiB,GAAE,SAAS,MAAM,EAAO;IAI/C,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAmC1E,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IA0CnC,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAO3F"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Transaction } from '@bsv/sdk';
|
|
2
|
+
import { tryParseUoraAnchor, uoraAnchorPrefix, UORA_ANCHOR_PREFIX, UORA_ANCHOR_PREFIX_V2, } from './uoraAnchor.js';
|
|
3
|
+
const NONE = { outputsToAdmit: [], coinsToRetain: [] };
|
|
4
|
+
/**
|
|
5
|
+
* `tm_uora_dpp`: admission for UORA attestation anchors.
|
|
6
|
+
*
|
|
7
|
+
* The second rail. `tm_dpp` admits the passport itself, a chain of states each
|
|
8
|
+
* spending the last; this one admits the anchors that say what a named party
|
|
9
|
+
* claimed about a passport and when. They are deliberately separate topics
|
|
10
|
+
* rather than one: they carry different formats, they are written by different
|
|
11
|
+
* treasuries, and an instance may reasonably want one and not the other.
|
|
12
|
+
*
|
|
13
|
+
* ## How this differs from `tm_dpp`, and why the rules are shorter
|
|
14
|
+
*
|
|
15
|
+
* An anchor is a leaf. It is never spent, it has no predecessor and no
|
|
16
|
+
* transition rules apply, so `previousCoins` is not consulted and nothing is
|
|
17
|
+
* retained. Everything admissible about one is legible in the output itself,
|
|
18
|
+
* which is the property that makes the format worth versioning.
|
|
19
|
+
*
|
|
20
|
+
* **Every valid anchor in the transaction is admitted, not exactly one.**
|
|
21
|
+
* `tm_dpp` insists on exactly one output because a second passport state in the
|
|
22
|
+
* same transaction would make "the tip" ambiguous. Anchors have no such
|
|
23
|
+
* relation to each other: each is independent and self-validating. Admitting
|
|
24
|
+
* all of them means a service can batch a fleet's worth of attestations into
|
|
25
|
+
* one transaction later without the topic needing to change, which at battery
|
|
26
|
+
* scale is the difference between one anchor per event being affordable and not.
|
|
27
|
+
*
|
|
28
|
+
* ## The admission policy
|
|
29
|
+
*
|
|
30
|
+
* Structural validity is `tryParseUoraAnchor`, and it already includes the
|
|
31
|
+
* attribution: the prefix, a 64-character digest, a resolvable `did:key`
|
|
32
|
+
* issuer, a subject, a type, the anchoring service's identity key, the appended
|
|
33
|
+
* signature sealing all seven **at the boundaries they were written at**, and
|
|
34
|
+
* the locking key being the BRC-42 child of the service key the output itself
|
|
35
|
+
* names. Producing an output that passes needs that key's private half, so
|
|
36
|
+
* every admitted anchor is attributable without this topic being told anything.
|
|
37
|
+
*
|
|
38
|
+
* That is the part v1 could not express, and the reason the service key is a
|
|
39
|
+
* field rather than a configuration value. A shared overlay serving several DPP
|
|
40
|
+
* deployments should not need a key list amended every time one is added, and a
|
|
41
|
+
* key list is not a security boundary anyway: it is a preference about whose
|
|
42
|
+
* anchors to carry.
|
|
43
|
+
*
|
|
44
|
+
* Which is exactly what the optional list is. Name services and this instance
|
|
45
|
+
* carries only theirs; name none and it carries any well-formed anchor, each of
|
|
46
|
+
* which still says whose it is.
|
|
47
|
+
*
|
|
48
|
+
* ## Only v3
|
|
49
|
+
*
|
|
50
|
+
* v2 is not admitted, and the reason is not tidiness. A v2 signature covers the
|
|
51
|
+
* field bytes run together, so it does not fix where the subject stops and the
|
|
52
|
+
* type starts, and any holder of a v2 anchor can re-cut that boundary and keep
|
|
53
|
+
* the signature. An index built on those fields would then be keyed on a
|
|
54
|
+
* subject nobody signed. `uoraAnchor.ts` sets out the whole of it.
|
|
55
|
+
*
|
|
56
|
+
* Nothing in the `TopicManager` contract lets a topic explain a refusal: the
|
|
57
|
+
* answer is a list of indices, and an absent index says only "not this one". So
|
|
58
|
+
* a v2 anchor is also warned about on the node's own console, because a
|
|
59
|
+
* treasury still writing v2 would otherwise get a 200 and an empty list back
|
|
60
|
+
* and have no way to tell that from a transaction carrying no anchors. A
|
|
61
|
+
* submitter that cares still has to read the admitted list rather than the HTTP
|
|
62
|
+
* status.
|
|
63
|
+
*/
|
|
64
|
+
export class UoraAnchorTopicManager {
|
|
65
|
+
accepted;
|
|
66
|
+
constructor(anchorServiceKeys = []) {
|
|
67
|
+
this.accepted = [...anchorServiceKeys].filter((key) => key !== '');
|
|
68
|
+
}
|
|
69
|
+
async identifyAdmissibleOutputs(beef) {
|
|
70
|
+
let tx;
|
|
71
|
+
try {
|
|
72
|
+
tx = Transaction.fromBEEF(beef);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return NONE;
|
|
76
|
+
}
|
|
77
|
+
const outputsToAdmit = [];
|
|
78
|
+
for (const [outputIndex, output] of tx.outputs.entries()) {
|
|
79
|
+
const anchor = tryParseUoraAnchor(output.lockingScript);
|
|
80
|
+
if (anchor == null) {
|
|
81
|
+
// A v2 anchor is turned away out loud. `outputsToAdmit` cannot tell a
|
|
82
|
+
// refusal from an output that was never an anchor, and a treasury still
|
|
83
|
+
// writing v2 would otherwise see a 200 and an empty list and read it as
|
|
84
|
+
// "nothing to index here". Everything else that fails to parse is
|
|
85
|
+
// somebody else's output and is not worth a line.
|
|
86
|
+
if (uoraAnchorPrefix(output.lockingScript) === UORA_ANCHOR_PREFIX_V2) {
|
|
87
|
+
console.warn(`tm_uora_dpp: refused output ${outputIndex}, a ${UORA_ANCHOR_PREFIX_V2} anchor. ` +
|
|
88
|
+
'Its signature covers the field bytes run together, so the boundary between the ' +
|
|
89
|
+
`subject and the type is unsigned. Rewrite it as ${UORA_ANCHOR_PREFIX}.`);
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (this.accepted.length > 0 && !this.accepted.includes(anchor.anchoredBy))
|
|
94
|
+
continue;
|
|
95
|
+
outputsToAdmit.push(outputIndex);
|
|
96
|
+
}
|
|
97
|
+
// Nothing is ever retained: an anchor is never spent, so there is no
|
|
98
|
+
// history for a spend to consume.
|
|
99
|
+
return { outputsToAdmit, coinsToRetain: [] };
|
|
100
|
+
}
|
|
101
|
+
async getDocumentation() {
|
|
102
|
+
return [
|
|
103
|
+
`# tm_uora_dpp`,
|
|
104
|
+
'',
|
|
105
|
+
`Admission for UORA attestation anchors in the ${UORA_ANCHOR_PREFIX} format.`,
|
|
106
|
+
'',
|
|
107
|
+
'An anchor is a 1-satoshi PushDrop output carrying, in order: the version',
|
|
108
|
+
'prefix, the SHA-256 digest of the attestation in lower-case hex, the',
|
|
109
|
+
"attestation id, the issuer's did:key, the subject passport id, the UORA",
|
|
110
|
+
"attestation type, and the anchoring service's identity key. An eighth",
|
|
111
|
+
'field holds an ECDSA signature by the key that locks the output, over',
|
|
112
|
+
'each of the seven preceded by its length as a varint. The lengths are',
|
|
113
|
+
'the point: the signature fixes where every field stops, so no reader can',
|
|
114
|
+
'be shown the same bytes cut into a different subject and type.',
|
|
115
|
+
'',
|
|
116
|
+
'Admitted when all seven parse, the issuer resolves to a compressed',
|
|
117
|
+
'secp256k1 key, that signature verifies against the locking key, and the',
|
|
118
|
+
'locking key is the BRC-42 child of the service key in field 6 at protocol',
|
|
119
|
+
"[1, 'uora anchor v3'], key id the attestation id, counterparty 'anyone'.",
|
|
120
|
+
'The derivation is reproducible by anyone holding the output, and both it',
|
|
121
|
+
"and the signature need the service key's private half, so an admitted",
|
|
122
|
+
'anchor names its author checkably and carries no field its author did not',
|
|
123
|
+
'sign at the boundary it is read at.',
|
|
124
|
+
'',
|
|
125
|
+
`${UORA_ANCHOR_PREFIX_V2} is superseded and is not admitted. Its signature`,
|
|
126
|
+
'covered the field bytes run together, which left the boundary between the',
|
|
127
|
+
'subject and the type unsigned and re-cuttable by any holder.',
|
|
128
|
+
'',
|
|
129
|
+
'The issuer in field 3 is signed but not proved: anyone able to write an',
|
|
130
|
+
'anchor can write any DID there, and the signature says only that the',
|
|
131
|
+
'anchoring service wrote that DID and not that the party consented. What',
|
|
132
|
+
"the claim itself is worth is settled by the attestation's own signature,",
|
|
133
|
+
'which is not on chain.',
|
|
134
|
+
'',
|
|
135
|
+
'An instance may narrow to named anchoring services. That is a preference',
|
|
136
|
+
'about whose anchors to carry, not a security boundary.',
|
|
137
|
+
'',
|
|
138
|
+
'Anchors are leaves: never spent, nothing retained, no transition rules.',
|
|
139
|
+
'Every valid anchor in a transaction is admitted, so anchors may be batched.',
|
|
140
|
+
].join('\n');
|
|
141
|
+
}
|
|
142
|
+
async getMetaData() {
|
|
143
|
+
return {
|
|
144
|
+
name: 'tm_uora_dpp',
|
|
145
|
+
shortDescription: `UORA attestation anchor admission (${UORA_ANCHOR_PREFIX})`,
|
|
146
|
+
version: '3.0.0',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=tmUoraDpp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmUoraDpp.js","sourceRoot":"","sources":["../src/tmUoraDpp.ts"],"names":[],"mappings":"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"}
|