@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,152 @@
|
|
|
1
|
+
import { tryParseUoraAnchor, UORA_ANCHOR_PREFIX } from './uoraAnchor.js';
|
|
2
|
+
export const UORA_TOPIC = 'tm_uora_dpp';
|
|
3
|
+
export const UORA_SERVICE = 'ls_uora_dpp';
|
|
4
|
+
/**
|
|
5
|
+
* `ls_uora_dpp`: the DID-keyed lookup.
|
|
6
|
+
*
|
|
7
|
+
* The question this exists to answer is "what has this party attested", keyed
|
|
8
|
+
* on the issuer's `did:key`, and it is the question no index over the v1 anchor
|
|
9
|
+
* format could answer at all: a v1 anchor names no issuer, so an indexer
|
|
10
|
+
* holding one has a digest and no idea whose claim it covers.
|
|
11
|
+
*
|
|
12
|
+
* Four other selectors come free from the same fields and are worth having:
|
|
13
|
+
* the subject (every claim about one passport, from every party), the
|
|
14
|
+
* attestation id (the anchor for one known claim), the digest (given an
|
|
15
|
+
* attestation in hand, has anyone anchored exactly this), and the UORA type,
|
|
16
|
+
* which narrows any of the others.
|
|
17
|
+
*
|
|
18
|
+
* ## What the answer is, and what it is not
|
|
19
|
+
*
|
|
20
|
+
* The answer is outputs, as BRC-24 requires, so the caller receives the anchors
|
|
21
|
+
* as chain data and checks them without trusting this service. That matters
|
|
22
|
+
* more here than for a passport lookup: this index is derived from the outputs,
|
|
23
|
+
* so an index that lied would be caught by the caller reading the same outputs
|
|
24
|
+
* it just returned.
|
|
25
|
+
*
|
|
26
|
+
* The answer is **not** the attestations. Those are never on chain, by the
|
|
27
|
+
* privacy rule, so a caller who wants the claim itself fetches it from the
|
|
28
|
+
* resolver and checks its digest against the anchor this returned. The anchor
|
|
29
|
+
* is the proof; the resolver is merely convenient.
|
|
30
|
+
*/
|
|
31
|
+
export class UoraAnchorLookupService {
|
|
32
|
+
storage;
|
|
33
|
+
admissionMode = 'locking-script';
|
|
34
|
+
spendNotificationMode = 'txid';
|
|
35
|
+
constructor(storage) {
|
|
36
|
+
this.storage = storage;
|
|
37
|
+
}
|
|
38
|
+
async outputAdmittedByTopic(payload) {
|
|
39
|
+
if (payload.mode !== 'locking-script')
|
|
40
|
+
throw new Error('Invalid admission mode');
|
|
41
|
+
if (payload.topic !== UORA_TOPIC)
|
|
42
|
+
return;
|
|
43
|
+
const anchor = tryParseUoraAnchor(payload.lockingScript);
|
|
44
|
+
// Admission already validated this; a null here would mean the topic
|
|
45
|
+
// manager and this service disagree, and indexing a half-read output is
|
|
46
|
+
// worse than not indexing it.
|
|
47
|
+
if (anchor == null)
|
|
48
|
+
return;
|
|
49
|
+
await this.storage.insert({
|
|
50
|
+
txid: payload.txid,
|
|
51
|
+
outputIndex: payload.outputIndex,
|
|
52
|
+
digest: anchor.digest,
|
|
53
|
+
attestationId: anchor.attestationId,
|
|
54
|
+
issuer: anchor.issuer,
|
|
55
|
+
issuerKey: anchor.issuerKey,
|
|
56
|
+
subject: anchor.subject,
|
|
57
|
+
uoraType: anchor.uoraType,
|
|
58
|
+
anchoredBy: anchor.anchoredBy,
|
|
59
|
+
lockingKey: anchor.lockingKey,
|
|
60
|
+
createdAt: new Date(),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* An anchor is a leaf and should never be spent. If one is, the claim it
|
|
65
|
+
* carries is unaffected: the digest sat at that point in the chain's order
|
|
66
|
+
* whatever later happened to the satoshi. So the record stays, and the spend
|
|
67
|
+
* is not recorded, because nothing about this index turns on it.
|
|
68
|
+
*/
|
|
69
|
+
async outputSpent(payload) {
|
|
70
|
+
if (payload.mode !== 'txid')
|
|
71
|
+
throw new Error('Invalid spend notification mode');
|
|
72
|
+
}
|
|
73
|
+
async outputNoLongerRetainedInHistory(txid, outputIndex, topic) {
|
|
74
|
+
if (topic !== UORA_TOPIC)
|
|
75
|
+
return;
|
|
76
|
+
await this.storage.delete(txid, outputIndex);
|
|
77
|
+
}
|
|
78
|
+
async outputEvicted(txid, outputIndex) {
|
|
79
|
+
await this.storage.delete(txid, outputIndex);
|
|
80
|
+
}
|
|
81
|
+
async lookup(question) {
|
|
82
|
+
if (question.service !== UORA_SERVICE) {
|
|
83
|
+
throw new Error(`Unsupported service ${question.service}`);
|
|
84
|
+
}
|
|
85
|
+
const query = (question.query ?? {});
|
|
86
|
+
// Refused rather than ignored: `selectors()` in the storage layer would
|
|
87
|
+
// drop a non-string field, and a query that silently loses a selector
|
|
88
|
+
// answers broader than it was asked, up to a full scan under the cap.
|
|
89
|
+
const fields = [
|
|
90
|
+
'issuer',
|
|
91
|
+
'issuerKey',
|
|
92
|
+
'subject',
|
|
93
|
+
'attestationId',
|
|
94
|
+
'digest',
|
|
95
|
+
'uoraType',
|
|
96
|
+
'anchoredBy',
|
|
97
|
+
];
|
|
98
|
+
for (const field of fields) {
|
|
99
|
+
const value = query[field];
|
|
100
|
+
if (value != null && typeof value !== 'string') {
|
|
101
|
+
throw new Error(`Query field ${field} must be a string`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// `uoraType` alone is every anchor of a common type, which is a table scan
|
|
105
|
+
// wearing a query, so it narrows and never selects.
|
|
106
|
+
const selective = [
|
|
107
|
+
query.issuer,
|
|
108
|
+
query.issuerKey,
|
|
109
|
+
query.subject,
|
|
110
|
+
query.attestationId,
|
|
111
|
+
query.digest,
|
|
112
|
+
query.anchoredBy,
|
|
113
|
+
].some((value) => typeof value === 'string' && value !== '');
|
|
114
|
+
if (!selective) {
|
|
115
|
+
throw new Error('Query must provide issuer, issuerKey, subject, attestationId, digest or anchoredBy');
|
|
116
|
+
}
|
|
117
|
+
const records = await this.storage.find(query);
|
|
118
|
+
return records.map((record) => ({ txid: record.txid, outputIndex: record.outputIndex }));
|
|
119
|
+
}
|
|
120
|
+
async getDocumentation() {
|
|
121
|
+
return [
|
|
122
|
+
`# ${UORA_SERVICE}`,
|
|
123
|
+
'',
|
|
124
|
+
`Lookup for ${UORA_ANCHOR_PREFIX} attestation anchors, keyed on the issuer DID.`,
|
|
125
|
+
'',
|
|
126
|
+
'Query with at least one of `issuer` (a did:key), `issuerKey` (the same',
|
|
127
|
+
'key as hex), `subject` (a passport id), `attestationId`, `digest`, or',
|
|
128
|
+
'`anchoredBy` (the treasury that wrote the anchor). `uoraType` narrows any',
|
|
129
|
+
'of them and cannot select on its own. All are',
|
|
130
|
+
'exact matches. `limit` caps the answer and is itself capped.',
|
|
131
|
+
'',
|
|
132
|
+
'Every indexed field is one the anchoring service signed, at the boundary',
|
|
133
|
+
'it is read at: the signature covers each field behind its length, so the',
|
|
134
|
+
'subject this index is keyed on is the subject that was written. Anchors',
|
|
135
|
+
'in the superseded uora-anchor-v2 layout are not admitted and are',
|
|
136
|
+
'therefore not indexed, because their signature left that boundary open.',
|
|
137
|
+
'',
|
|
138
|
+
'The answer is the anchor outputs as BEEF, so the caller verifies them',
|
|
139
|
+
'against the chain rather than trusting this index. The attestations',
|
|
140
|
+
'themselves are never on chain: fetch one from the resolver and check its',
|
|
141
|
+
"canonical digest against the anchor's.",
|
|
142
|
+
].join('\n');
|
|
143
|
+
}
|
|
144
|
+
async getMetaData() {
|
|
145
|
+
return {
|
|
146
|
+
name: UORA_SERVICE,
|
|
147
|
+
shortDescription: 'UORA attestation anchor lookup, keyed on issuer DID',
|
|
148
|
+
version: '1.0.0',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=lsUoraDpp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lsUoraDpp.js","sourceRoot":"","sources":["../src/lsUoraDpp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type PublisherPolicy, type PublisherRole } from '@bsv/dpp-core';
|
|
2
|
+
export interface PublisherPolicyConfig {
|
|
3
|
+
/** The verified chain, oldest first. */
|
|
4
|
+
chain: PublisherPolicy[];
|
|
5
|
+
/** Operator identifier to compressed identity key, from the environment. */
|
|
6
|
+
operators: Record<string, string>;
|
|
7
|
+
/** The versions `verifyPolicyChain` accepted, in chain order. */
|
|
8
|
+
versions: number[];
|
|
9
|
+
/** Where the chain came from, for logs. */
|
|
10
|
+
source: string;
|
|
11
|
+
}
|
|
12
|
+
/** `operator=compressedKey` pairs, comma-separated. Malformed input stops the boot rather than naming no operator. */
|
|
13
|
+
export declare function parseOperatorIdentityKeys(value: string | undefined): Record<string, string>;
|
|
14
|
+
/** Read and verify a chain file. Every failure is an Error whose message names the file, the version and the reason. */
|
|
15
|
+
export declare function loadPublisherPolicy(file: string, operators: Record<string, string>): PublisherPolicyConfig;
|
|
16
|
+
/** The policy the environment configures, or undefined when it configures none. */
|
|
17
|
+
export declare function publisherPolicyFromEnvironment(): PublisherPolicyConfig | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* The publisher keys a topic accepts at an instant under the chain.
|
|
20
|
+
*
|
|
21
|
+
* `undefined` means the version in force does not cover the topic: a policy
|
|
22
|
+
* may name `scope.topics`, and a topic outside them keeps its static
|
|
23
|
+
* configuration (the identity key, or ANCHOR_SERVICE_KEYS). An empty array
|
|
24
|
+
* means the policy covers the topic and no key is active, which admits
|
|
25
|
+
* nothing, and is also the answer for an instant before the first version was
|
|
26
|
+
* issued: a key admits nothing dated before its activation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function policyKeysFor(chain: PublisherPolicy[], at: string | Date, role: PublisherRole, topic: string): string[] | undefined;
|
|
29
|
+
/** The newest version of the chain, the one that names the operator profile and the current keys. */
|
|
30
|
+
export declare function newestPolicy(config: PublisherPolicyConfig): PublisherPolicy;
|
|
31
|
+
//# sourceMappingURL=policyConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyConfig.d.ts","sourceRoot":"","sources":["../src/policyConfig.ts"],"names":[],"mappings":"AAkBA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAA;AAEtB,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,iEAAiE;IACjE,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;CACf;AAID,sHAAsH;AACtH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyB3F;AAED,wHAAwH;AACxH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,qBAAqB,CAyB1G;AAED,mFAAmF;AACnF,wBAAgB,8BAA8B,IAAI,qBAAqB,GAAG,SAAS,CAalF;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,eAAe,EAAE,EACxB,EAAE,EAAE,MAAM,GAAG,IAAI,EACjB,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,GACZ,MAAM,EAAE,GAAG,SAAS,CAKtB;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAE3E"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The publisher key policy as this node is configured with it
|
|
3
|
+
* (`spec/services.md` section 1, `contracts/publisher-policy.schema.json`).
|
|
4
|
+
*
|
|
5
|
+
* `PUBLISHER_POLICY_FILE` names a JSON file holding the chain of
|
|
6
|
+
* `dpp-publisher-policy@1` versions, oldest first, and `OPERATOR_IDENTITY_KEYS`
|
|
7
|
+
* names each operator's identity key, because the chain's genesis is signed
|
|
8
|
+
* by an operator and the policy itself is never the source of the key that
|
|
9
|
+
* authorises it. The chain is verified once, at boot, with the core's
|
|
10
|
+
* `verifyPolicyChain`; a chain that does not verify stops the boot and names
|
|
11
|
+
* the version and the reason, since admitting under an unverified list would
|
|
12
|
+
* be admitting under whatever a file said.
|
|
13
|
+
*
|
|
14
|
+
* Without a policy file nothing here is consulted: the single
|
|
15
|
+
* `SERVICE_IDENTITY_KEY` is an implicit single-operator policy with no
|
|
16
|
+
* rotation history, and the capability document says so.
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
import { policyInForceAt, publisherKeysAt, verifyPolicyChain, } from '@bsv/dpp-core';
|
|
20
|
+
const COMPRESSED_KEY = /^0[23][0-9a-f]{64}$/;
|
|
21
|
+
/** `operator=compressedKey` pairs, comma-separated. Malformed input stops the boot rather than naming no operator. */
|
|
22
|
+
export function parseOperatorIdentityKeys(value) {
|
|
23
|
+
const operators = {};
|
|
24
|
+
const pairs = (value ?? '')
|
|
25
|
+
.split(',')
|
|
26
|
+
.map((pair) => pair.trim())
|
|
27
|
+
.filter((pair) => pair !== '');
|
|
28
|
+
for (const pair of pairs) {
|
|
29
|
+
const separator = pair.indexOf('=');
|
|
30
|
+
if (separator <= 0) {
|
|
31
|
+
throw new Error(`OPERATOR_IDENTITY_KEYS entry "${pair}" is not of the form operator=compressedKey`);
|
|
32
|
+
}
|
|
33
|
+
const operator = pair.slice(0, separator).trim();
|
|
34
|
+
const key = pair.slice(separator + 1).trim();
|
|
35
|
+
if (!COMPRESSED_KEY.test(key)) {
|
|
36
|
+
throw new Error(`OPERATOR_IDENTITY_KEYS: the key given for ${operator} is not a compressed public key`);
|
|
37
|
+
}
|
|
38
|
+
if (operators[operator] != null)
|
|
39
|
+
throw new Error(`OPERATOR_IDENTITY_KEYS names ${operator} twice`);
|
|
40
|
+
// One key under two names would let it sign a federation rotation and
|
|
41
|
+
// countersign it as "another operator": the whole point of the
|
|
42
|
+
// countersignature is that it comes from a different key.
|
|
43
|
+
const holder = Object.entries(operators).find(([, existing]) => existing === key)?.[0];
|
|
44
|
+
if (holder != null)
|
|
45
|
+
throw new Error(`OPERATOR_IDENTITY_KEYS gives ${operator} the same key as ${holder}`);
|
|
46
|
+
operators[operator] = key;
|
|
47
|
+
}
|
|
48
|
+
return operators;
|
|
49
|
+
}
|
|
50
|
+
/** Read and verify a chain file. Every failure is an Error whose message names the file, the version and the reason. */
|
|
51
|
+
export function loadPublisherPolicy(file, operators) {
|
|
52
|
+
let text;
|
|
53
|
+
try {
|
|
54
|
+
text = readFileSync(file, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
catch (cause) {
|
|
57
|
+
throw new Error(`PUBLISHER_POLICY_FILE ${file} could not be read: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
58
|
+
}
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(text);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error(`PUBLISHER_POLICY_FILE ${file} is not JSON`);
|
|
65
|
+
}
|
|
66
|
+
if (!Array.isArray(parsed) || parsed.some((p) => typeof p !== 'object' || p === null || Array.isArray(p))) {
|
|
67
|
+
throw new Error(`PUBLISHER_POLICY_FILE ${file} must hold a JSON array of dpp-publisher-policy@1 documents, oldest first`);
|
|
68
|
+
}
|
|
69
|
+
const chain = parsed;
|
|
70
|
+
const result = verifyPolicyChain(chain, operators);
|
|
71
|
+
if (!result.ok || result.failure != null) {
|
|
72
|
+
const failure = result.failure ?? { version: 0, reason: 'format', detail: 'the chain did not verify' };
|
|
73
|
+
throw new Error(`PUBLISHER_POLICY_FILE ${file}: policy version ${failure.version} refused, ${failure.reason}: ${failure.detail}`);
|
|
74
|
+
}
|
|
75
|
+
return { chain, operators, versions: result.versions, source: file };
|
|
76
|
+
}
|
|
77
|
+
/** The policy the environment configures, or undefined when it configures none. */
|
|
78
|
+
export function publisherPolicyFromEnvironment() {
|
|
79
|
+
const file = (process.env.PUBLISHER_POLICY_FILE ?? '').trim();
|
|
80
|
+
const operators = process.env.OPERATOR_IDENTITY_KEYS;
|
|
81
|
+
if (file === '') {
|
|
82
|
+
if (operators != null && operators.trim() !== '') {
|
|
83
|
+
console.warn('OPERATOR_IDENTITY_KEYS is set but PUBLISHER_POLICY_FILE is not: the operator keys are ignored, ' +
|
|
84
|
+
'because there is no policy chain for them to authorise');
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return loadPublisherPolicy(file, parseOperatorIdentityKeys(operators));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The publisher keys a topic accepts at an instant under the chain.
|
|
92
|
+
*
|
|
93
|
+
* `undefined` means the version in force does not cover the topic: a policy
|
|
94
|
+
* may name `scope.topics`, and a topic outside them keeps its static
|
|
95
|
+
* configuration (the identity key, or ANCHOR_SERVICE_KEYS). An empty array
|
|
96
|
+
* means the policy covers the topic and no key is active, which admits
|
|
97
|
+
* nothing, and is also the answer for an instant before the first version was
|
|
98
|
+
* issued: a key admits nothing dated before its activation.
|
|
99
|
+
*/
|
|
100
|
+
export function policyKeysFor(chain, at, role, topic) {
|
|
101
|
+
const inForce = policyInForceAt(chain, at);
|
|
102
|
+
if (inForce == null)
|
|
103
|
+
return [];
|
|
104
|
+
if (inForce.scope.topics != null && !inForce.scope.topics.includes(topic))
|
|
105
|
+
return undefined;
|
|
106
|
+
return publisherKeysAt(chain, at, role);
|
|
107
|
+
}
|
|
108
|
+
/** The newest version of the chain, the one that names the operator profile and the current keys. */
|
|
109
|
+
export function newestPolicy(config) {
|
|
110
|
+
return config.chain[config.chain.length - 1];
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=policyConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyConfig.js","sourceRoot":"","sources":["../src/policyConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { LookupService } from '@bsv/overlay';
|
|
2
|
+
import type { RetractableStorage } from './engineStorage.js';
|
|
3
|
+
import type { DppRecordStore } from './storage.js';
|
|
4
|
+
export type RetractionRefusalCode = 'output-unknown' | 'retraction-refused' | 'chain-tracker-unavailable';
|
|
5
|
+
export declare class RetractionRefused extends Error {
|
|
6
|
+
readonly status: number;
|
|
7
|
+
readonly code: RetractionRefusalCode;
|
|
8
|
+
constructor(status: number, code: RetractionRefusalCode, message: string);
|
|
9
|
+
}
|
|
10
|
+
export interface RetractionRequest {
|
|
11
|
+
storage: RetractableStorage;
|
|
12
|
+
lookupServices: Record<string, LookupService>;
|
|
13
|
+
records: DppRecordStore;
|
|
14
|
+
topic: string;
|
|
15
|
+
txid: string;
|
|
16
|
+
outputIndex: number;
|
|
17
|
+
reason: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the network knows the transaction, asked of the configured chain
|
|
20
|
+
* tracker's source. Undefined when the deployment has no header source
|
|
21
|
+
* (CHAIN_TRACKER=scripts-only): the check is then not run, and the answer
|
|
22
|
+
* says so.
|
|
23
|
+
*/
|
|
24
|
+
knownOnChain?: (txid: string) => Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
export interface Retraction {
|
|
27
|
+
txid: string;
|
|
28
|
+
outputIndex: number;
|
|
29
|
+
reason: string;
|
|
30
|
+
/** Whether the network was asked; false under scripts-only, where the local proof check is the whole check. */
|
|
31
|
+
networkChecked: boolean;
|
|
32
|
+
/** The predecessor made the tip again, when the retracted state had one. */
|
|
33
|
+
restoredTip?: {
|
|
34
|
+
txid: string;
|
|
35
|
+
outputIndex: number;
|
|
36
|
+
};
|
|
37
|
+
/** Repairs the engine storage could not make, by name, when it is not one of this package's. */
|
|
38
|
+
unrepaired: string[];
|
|
39
|
+
}
|
|
40
|
+
export declare function retractOutput(request: RetractionRequest): Promise<Retraction>;
|
|
41
|
+
//# sourceMappingURL=retraction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retraction.d.ts","sourceRoot":"","sources":["../src/retraction.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,aAAa,EAAU,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,oBAAoB,GAAG,2BAA2B,CAAA;AAEzG,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,qBAAqB;gBAD3B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,qBAAqB,EACpC,OAAO,EAAE,MAAM;CAIlB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,kBAAkB,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAC7C,OAAO,EAAE,cAAc,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,+GAA+G;IAC/G,cAAc,EAAE,OAAO,CAAA;IACvB,4EAA4E;IAC5E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,gGAAgG;IAChG,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAyBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiFnF"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retraction (`POST /retract`): removing a state the index admitted on
|
|
3
|
+
* announcement and the network then refused.
|
|
4
|
+
*
|
|
5
|
+
* `spec/writing.md` lets a writer announce a state before it sends it, so the
|
|
6
|
+
* index can hold an output no block will ever contain. That phantom sits on
|
|
7
|
+
* the passport as its tip: the writer's next state builds on nothing, and a
|
|
8
|
+
* reader's `chainFromBeef` finds two successors of one state and can order
|
|
9
|
+
* neither. The writer, who alone knows the network's answer, asks the index
|
|
10
|
+
* to forget the output.
|
|
11
|
+
*
|
|
12
|
+
* What is never retracted: a state with a merkle path (the network did take
|
|
13
|
+
* it), a state the configured chain tracker knows (same), and a spent state
|
|
14
|
+
* (its successor was admitted against it; retracting the middle of a history
|
|
15
|
+
* would leave the successor pointing at nothing).
|
|
16
|
+
*
|
|
17
|
+
* How the removal is done. The Engine's own removal path is its stale-output
|
|
18
|
+
* eviction, `deleteUTXODeep`: delete the output, tell every lookup service it
|
|
19
|
+
* is no longer retained, then recurse into the outputs it consumed and delete
|
|
20
|
+
* each of those too once nothing else consumes them. That recursion is right
|
|
21
|
+
* for the Engine's own case, a topic that did not retain a spent coin, and
|
|
22
|
+
* wrong here: on a history-retaining topic the consumed lineage is the whole
|
|
23
|
+
* lifecycle, and the retraction of one phantom would erase the passport back
|
|
24
|
+
* to its genesis. `deleteUTXODeep` is also private to the Engine. So this
|
|
25
|
+
* module performs the eviction's first step through the same storage and
|
|
26
|
+
* lookup-service calls (`deleteOutput`, then the eviction hook), repairs the
|
|
27
|
+
* predecessor's `consumedBy` exactly as the eviction does, and stops there;
|
|
28
|
+
* then it makes the two repairs the eviction never needs, clearing the
|
|
29
|
+
* predecessor's spent mark and the phantom's applied-transaction record, so
|
|
30
|
+
* the real tip is a tip again and the same transaction can be announced
|
|
31
|
+
* again should the network take it after all.
|
|
32
|
+
*/
|
|
33
|
+
import { Beef } from '@bsv/sdk';
|
|
34
|
+
export class RetractionRefused extends Error {
|
|
35
|
+
status;
|
|
36
|
+
code;
|
|
37
|
+
constructor(status, code, message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.status = status;
|
|
40
|
+
this.code = code;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Whether the stored BEEF proves the transaction: a bump for its txid, or a merkle path on it. */
|
|
44
|
+
function beefProves(beef, txid) {
|
|
45
|
+
if (beef == null)
|
|
46
|
+
return false;
|
|
47
|
+
try {
|
|
48
|
+
const parsed = Beef.fromBinary(beef);
|
|
49
|
+
const held = parsed.findTxid(txid);
|
|
50
|
+
return held != null && (held.bumpIndex != null || held.tx?.merklePath != null);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Why an output as it stands may not be retracted, or undefined when it may. */
|
|
57
|
+
function refusalFor(output, txid) {
|
|
58
|
+
if (output.spent || output.consumedBy.length > 0) {
|
|
59
|
+
return 'the output is spent: a later state was admitted against it, and a history is never cut in the middle';
|
|
60
|
+
}
|
|
61
|
+
if (output.blockHeight != null || beefProves(output.beef, txid)) {
|
|
62
|
+
return 'this index holds a merkle path for the output: the network took the transaction, so there is nothing to retract';
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
export async function retractOutput(request) {
|
|
67
|
+
const { storage, topic, txid, outputIndex } = request;
|
|
68
|
+
const output = await storage.findOutput(txid, outputIndex, topic, undefined, true);
|
|
69
|
+
if (output == null) {
|
|
70
|
+
throw new RetractionRefused(404, 'output-unknown', `this index holds no ${topic} output ${txid}:${outputIndex}`);
|
|
71
|
+
}
|
|
72
|
+
const refusal = refusalFor(output, txid);
|
|
73
|
+
if (refusal != null)
|
|
74
|
+
throw new RetractionRefused(409, 'retraction-refused', refusal);
|
|
75
|
+
let networkChecked = false;
|
|
76
|
+
if (request.knownOnChain != null) {
|
|
77
|
+
let known;
|
|
78
|
+
try {
|
|
79
|
+
known = await request.knownOnChain(txid);
|
|
80
|
+
}
|
|
81
|
+
catch (cause) {
|
|
82
|
+
throw new RetractionRefused(503, 'chain-tracker-unavailable', `the chain tracker could not be asked whether it knows ${txid}; the retraction was not applied, try again: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
83
|
+
}
|
|
84
|
+
if (known) {
|
|
85
|
+
throw new RetractionRefused(409, 'retraction-refused', 'the chain tracker knows this transaction: the network took it, so there is nothing to retract');
|
|
86
|
+
}
|
|
87
|
+
networkChecked = true;
|
|
88
|
+
}
|
|
89
|
+
// Nothing here is atomic with the Engine: while the network was asked, a
|
|
90
|
+
// /submit may have admitted a successor or an /arc-ingest may have proven
|
|
91
|
+
// the output. So the output is read again immediately before the delete
|
|
92
|
+
// and refused if it no longer qualifies; the window left is the delete
|
|
93
|
+
// itself, which the storage interface gives no way to close.
|
|
94
|
+
const current = await storage.findOutput(txid, outputIndex, topic, undefined, true);
|
|
95
|
+
if (current == null) {
|
|
96
|
+
throw new RetractionRefused(404, 'output-unknown', `this index no longer holds ${topic} output ${txid}:${outputIndex}`);
|
|
97
|
+
}
|
|
98
|
+
const changed = refusalFor(current, txid);
|
|
99
|
+
if (changed != null) {
|
|
100
|
+
throw new RetractionRefused(409, 'retraction-refused', `the output changed while the retraction was being checked and nothing was removed: ${changed}`);
|
|
101
|
+
}
|
|
102
|
+
// The eviction's first step, through the same calls it makes.
|
|
103
|
+
await storage.deleteOutput(txid, outputIndex, topic);
|
|
104
|
+
for (const service of Object.values(request.lookupServices)) {
|
|
105
|
+
try {
|
|
106
|
+
await service.outputEvicted(txid, outputIndex);
|
|
107
|
+
}
|
|
108
|
+
catch (cause) {
|
|
109
|
+
console.error(`retraction of ${txid}:${outputIndex}: a lookup service failed to evict it:`, cause);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// The eviction's repair of the predecessor, then the two it never needs.
|
|
113
|
+
const unrepaired = [];
|
|
114
|
+
let restoredTip;
|
|
115
|
+
for (const consumed of output.outputsConsumed) {
|
|
116
|
+
const predecessor = await storage.findOutput(consumed.txid, consumed.outputIndex, topic);
|
|
117
|
+
if (predecessor == null)
|
|
118
|
+
continue;
|
|
119
|
+
const consumedBy = predecessor.consumedBy.filter((c) => !(c.txid === txid && c.outputIndex === outputIndex));
|
|
120
|
+
await storage.updateConsumedBy(consumed.txid, consumed.outputIndex, topic, consumedBy);
|
|
121
|
+
if (consumedBy.length > 0)
|
|
122
|
+
continue;
|
|
123
|
+
if (storage.markUTXOAsUnspent != null) {
|
|
124
|
+
await storage.markUTXOAsUnspent(consumed.txid, consumed.outputIndex, topic);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
unrepaired.push(`engine storage still marks ${consumed.txid}:${consumed.outputIndex} spent`);
|
|
128
|
+
}
|
|
129
|
+
await request.records.markUnspent(consumed.txid, consumed.outputIndex);
|
|
130
|
+
restoredTip = { txid: consumed.txid, outputIndex: consumed.outputIndex };
|
|
131
|
+
}
|
|
132
|
+
if (storage.deleteAppliedTransaction != null) {
|
|
133
|
+
await storage.deleteAppliedTransaction(txid, topic);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
unrepaired.push(`engine storage still records ${txid} as applied to ${topic}, so a re-announcement would be skipped as a duplicate`);
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
txid,
|
|
140
|
+
outputIndex,
|
|
141
|
+
reason: request.reason,
|
|
142
|
+
networkChecked,
|
|
143
|
+
...(restoredTip == null ? {} : { restoredTip }),
|
|
144
|
+
unrepaired,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=retraction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retraction.js","sourceRoot":"","sources":["../src/retraction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { Db } from 'mongodb';
|
|
2
|
+
/** One indexed DPP state (admitted output, kept after spend as history). */
|
|
3
|
+
export interface DppRecord {
|
|
4
|
+
txid: string;
|
|
5
|
+
outputIndex: number;
|
|
6
|
+
passportId: string;
|
|
7
|
+
/** Chip UID from the payload's data-carrier property; '' when absent. */
|
|
8
|
+
uid: string;
|
|
9
|
+
op: string;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
previousTxid: string;
|
|
12
|
+
spent: boolean;
|
|
13
|
+
/** txid of the state that spent this one; '' while unspent. */
|
|
14
|
+
spendingTxid: string;
|
|
15
|
+
createdAt: Date;
|
|
16
|
+
/**
|
|
17
|
+
* The store's own insertion sequence: assigned once, at insert, and never
|
|
18
|
+
* rewritten. It is the order `spec/portable-evidence.md` section 1 pages by,
|
|
19
|
+
* so two readers of one snapshot receive the same pages and a cursor names
|
|
20
|
+
* a position that a concurrent insert cannot shift. Rows written before the
|
|
21
|
+
* field existed are numbered at boot in createdAt, txid, outputIndex order,
|
|
22
|
+
* which is the order the bounded lookup has always answered them in.
|
|
23
|
+
*/
|
|
24
|
+
sequence: number;
|
|
25
|
+
}
|
|
26
|
+
/** What the lookup service hands the store; the store assigns `sequence`. */
|
|
27
|
+
export type DppRecordInput = Omit<DppRecord, 'sequence'>;
|
|
28
|
+
/** Which records a page or an export is about: one of the two keys, non-empty. */
|
|
29
|
+
export interface RecordSelector {
|
|
30
|
+
passportId?: string;
|
|
31
|
+
uid?: string;
|
|
32
|
+
}
|
|
33
|
+
/** The sequences a page reads: after < sequence <= upTo. */
|
|
34
|
+
export interface SequenceRange {
|
|
35
|
+
after: number;
|
|
36
|
+
upTo: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Record store for ls_dpp. Abstract so tests (and the backend dev mode) can
|
|
40
|
+
* run in memory; production uses Mongo via the overlay's lookup-service
|
|
41
|
+
* factory.
|
|
42
|
+
*/
|
|
43
|
+
export interface DppRecordStore {
|
|
44
|
+
insert: (record: DppRecordInput) => Promise<void>;
|
|
45
|
+
markSpent: (txid: string, outputIndex: number, spendingTxid: string) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Undo `markSpent`: the state that spent this one was retracted, an announced
|
|
48
|
+
* transaction the network refused (`POST /retract`), so this one is the tip
|
|
49
|
+
* again.
|
|
50
|
+
*/
|
|
51
|
+
markUnspent: (txid: string, outputIndex: number) => Promise<void>;
|
|
52
|
+
delete: (txid: string, outputIndex: number) => Promise<void>;
|
|
53
|
+
findByPassport: (passportId: string) => Promise<DppRecord[]>;
|
|
54
|
+
findByUid: (uid: string) => Promise<DppRecord[]>;
|
|
55
|
+
/**
|
|
56
|
+
* The highest sequence assigned so far, 0 when nothing was ever inserted:
|
|
57
|
+
* what a snapshot pins. A deleted row's sequence stays assigned, so the
|
|
58
|
+
* answer never goes backwards.
|
|
59
|
+
*/
|
|
60
|
+
highestSequence: () => Promise<number>;
|
|
61
|
+
/**
|
|
62
|
+
* The records matching the selector whose sequence lies in the range,
|
|
63
|
+
* ascending by sequence, at most `limit` of them. The one query a page is
|
|
64
|
+
* made of, so both stores answer it from an index rather than a scan.
|
|
65
|
+
*/
|
|
66
|
+
findPage: (selector: RecordSelector, range: SequenceRange, limit: number) => Promise<DppRecord[]>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The one accepted spelling of a selector: exactly one key, a non-empty string.
|
|
70
|
+
* Checked in the store and not only at the HTTP handler, because the Mongo
|
|
71
|
+
* store would run whatever object it is handed as a filter.
|
|
72
|
+
*/
|
|
73
|
+
export declare function selectorFilter(selector: RecordSelector): {
|
|
74
|
+
passportId: string;
|
|
75
|
+
} | {
|
|
76
|
+
uid: string;
|
|
77
|
+
};
|
|
78
|
+
export declare class MongoDppStorage implements DppRecordStore {
|
|
79
|
+
private readonly records;
|
|
80
|
+
private readonly counters;
|
|
81
|
+
private ready?;
|
|
82
|
+
constructor(db: Db);
|
|
83
|
+
/**
|
|
84
|
+
* Indexes, and the one migration this store has ever needed: rows written
|
|
85
|
+
* before `sequence` existed are numbered in createdAt, txid, outputIndex
|
|
86
|
+
* order, so every row has a position before the first page is served and the
|
|
87
|
+
* pages of an old deployment read in the order its lookups always answered.
|
|
88
|
+
* Runs once per process on first use, and `main()` awaits it at boot so the
|
|
89
|
+
* numbering never races a request. Idempotent: a second boot finds nothing
|
|
90
|
+
* to number.
|
|
91
|
+
*/
|
|
92
|
+
ensureReady(): Promise<void>;
|
|
93
|
+
/** One atomic increment per insert: the counter is the order, gaps included. */
|
|
94
|
+
private nextSequence;
|
|
95
|
+
insert(record: DppRecordInput): Promise<void>;
|
|
96
|
+
markSpent(txid: string, outputIndex: number, spendingTxid: string): Promise<void>;
|
|
97
|
+
markUnspent(txid: string, outputIndex: number): Promise<void>;
|
|
98
|
+
delete(txid: string, outputIndex: number): Promise<void>;
|
|
99
|
+
findByPassport(passportId: string): Promise<DppRecord[]>;
|
|
100
|
+
findByUid(uid: string): Promise<DppRecord[]>;
|
|
101
|
+
highestSequence(): Promise<number>;
|
|
102
|
+
findPage(selector: RecordSelector, range: SequenceRange, limit: number): Promise<DppRecord[]>;
|
|
103
|
+
}
|
|
104
|
+
export declare class InMemoryDppStorage implements DppRecordStore {
|
|
105
|
+
private readonly records;
|
|
106
|
+
private assigned;
|
|
107
|
+
private key;
|
|
108
|
+
insert(record: DppRecordInput): Promise<void>;
|
|
109
|
+
markSpent(txid: string, outputIndex: number, spendingTxid: string): Promise<void>;
|
|
110
|
+
markUnspent(txid: string, outputIndex: number): Promise<void>;
|
|
111
|
+
delete(txid: string, outputIndex: number): Promise<void>;
|
|
112
|
+
findByPassport(passportId: string): Promise<DppRecord[]>;
|
|
113
|
+
findByUid(uid: string): Promise<DppRecord[]>;
|
|
114
|
+
highestSequence(): Promise<number>;
|
|
115
|
+
findPage(selector: RecordSelector, range: SequenceRange, limit: number): Promise<DppRecord[]>;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|