@hazbase/simplicity 0.0.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -1038
- package/dist/cli.js +3341 -269
- package/dist/client/SimplicityClient.d.ts +91 -290
- package/dist/client/SimplicityClient.js +93 -23
- package/dist/core/executor.js +67 -92
- package/dist/core/lineage.d.ts +18 -0
- package/dist/core/lineage.js +30 -0
- package/dist/core/outputBinding.d.ts +61 -0
- package/dist/core/outputBinding.js +552 -0
- package/dist/core/reporting.d.ts +22 -0
- package/dist/core/reporting.js +40 -0
- package/dist/core/schnorr.d.ts +5 -0
- package/dist/core/schnorr.js +46 -0
- package/dist/core/types.d.ts +799 -1
- package/dist/docs/definitions/bond-anchor.simf +19 -0
- package/dist/docs/definitions/bond-definition.json +10 -0
- package/dist/docs/definitions/bond-descriptor-bound-settlement-machine.simf +144 -0
- package/dist/docs/definitions/bond-issuance-anchor.simf +26 -0
- package/dist/docs/definitions/bond-issuance-state-partial-redemption.json +18 -0
- package/dist/docs/definitions/bond-issuance-state-redeemed.json +18 -0
- package/dist/docs/definitions/bond-issuance-state.json +12 -0
- package/dist/docs/definitions/bond-redemption-state-machine.simf +118 -0
- package/dist/docs/definitions/bond-redemption-transition.simf +41 -0
- package/dist/docs/definitions/bond-script-bound-settlement-machine.simf +142 -0
- package/dist/docs/definitions/fund-capital-call-open.simf +82 -0
- package/dist/docs/definitions/fund-capital-call-refund-only.simf +67 -0
- package/dist/docs/definitions/fund-capital-call-state.json +11 -0
- package/dist/docs/definitions/fund-definition.json +8 -0
- package/dist/docs/definitions/fund-distribution-claim.simf +57 -0
- package/dist/docs/definitions/receivable-definition.json +9 -0
- package/dist/docs/definitions/receivable-funding-claim.json +13 -0
- package/dist/docs/definitions/receivable-funding-claim.simf +58 -0
- package/dist/docs/definitions/receivable-repayment-claim.json +13 -0
- package/dist/docs/definitions/receivable-repayment-claim.simf +59 -0
- package/dist/docs/definitions/receivable-state-funded.json +20 -0
- package/dist/docs/definitions/receivable-state-originated.json +19 -0
- package/dist/docs/definitions/receivable-state-repaid.json +20 -0
- package/dist/docs/definitions/recursive-delay-direct-next.simf +60 -0
- package/dist/docs/definitions/recursive-delay-optional.simf +88 -0
- package/dist/docs/definitions/recursive-delay-required.simf +72 -0
- package/dist/docs/definitions/recursive-delay.simf +83 -0
- package/dist/docs/definitions/recursive-policy-transfer-machine.simf +65 -0
- package/dist/domain/bond.d.ts +8649 -720
- package/dist/domain/bond.js +1398 -8
- package/dist/domain/bondSettlementValidation.d.ts +2 -0
- package/dist/domain/bondSettlementValidation.js +28 -0
- package/dist/domain/bondValidation.d.ts +37 -1
- package/dist/domain/bondValidation.js +137 -11
- package/dist/domain/fund.d.ts +2452 -0
- package/dist/domain/fund.js +1756 -0
- package/dist/domain/fundValidation.d.ts +152 -0
- package/dist/domain/fundValidation.js +767 -0
- package/dist/domain/policies.d.ts +1064 -0
- package/dist/domain/policies.js +1625 -0
- package/dist/domain/receivable.d.ts +824 -0
- package/dist/domain/receivable.js +1375 -0
- package/dist/domain/receivableValidation.d.ts +147 -0
- package/dist/domain/receivableValidation.js +831 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +137 -21
- package/package.json +20 -2
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OUTPUT_BINDING_SUPPORT_MATRIX = void 0;
|
|
4
|
+
exports.describeOutputBindingSupport = describeOutputBindingSupport;
|
|
5
|
+
exports.normalizeOutputForm = normalizeOutputForm;
|
|
6
|
+
exports.normalizeOutputRawFields = normalizeOutputRawFields;
|
|
7
|
+
exports.analyzeOutputRawFields = analyzeOutputRawFields;
|
|
8
|
+
exports.isExplicitV1OutputForm = isExplicitV1OutputForm;
|
|
9
|
+
exports.isExplicitAssetInputSupported = isExplicitAssetInputSupported;
|
|
10
|
+
exports.resolveOutputBindingMode = resolveOutputBindingMode;
|
|
11
|
+
exports.hashHexBytes = hashHexBytes;
|
|
12
|
+
exports.computeExplicitV1OutputHash = computeExplicitV1OutputHash;
|
|
13
|
+
exports.computeRawOutputV1Hash = computeRawOutputV1Hash;
|
|
14
|
+
exports.getScriptPubKeyHexViaRpc = getScriptPubKeyHexViaRpc;
|
|
15
|
+
exports.resolveExplicitAssetHex = resolveExplicitAssetHex;
|
|
16
|
+
exports.resolveOutputBindingDecision = resolveOutputBindingDecision;
|
|
17
|
+
exports.evaluateOutputBindingSupport = evaluateOutputBindingSupport;
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const EMPTY_BUFFER_SHA256 = (0, node_crypto_1.createHash)("sha256").update(Buffer.alloc(0)).digest();
|
|
20
|
+
exports.OUTPUT_BINDING_SUPPORT_MATRIX = {
|
|
21
|
+
supportedForms: [
|
|
22
|
+
{
|
|
23
|
+
form: "explicit-v1",
|
|
24
|
+
description: "Explicit asset + explicit amount + null nonce + empty range proof, with asset input supplied as bitcoin or 64-hex asset id.",
|
|
25
|
+
autoDerived: true,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
form: "raw-output-v1",
|
|
29
|
+
description: "Caller supplies asset/amount/nonce bytes plus scriptPubKey and range-proof components as raw bytes or pre-hashed values, and the SDK derives output_hash deterministically.",
|
|
30
|
+
autoDerived: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
form: "unsupported",
|
|
34
|
+
description: "Any output form outside explicit-v1/raw-output-v1. descriptor-bound falls back to script-bound unless a manual nextOutputHash is supplied.",
|
|
35
|
+
autoDerived: false,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
unsupportedOutputFeatures: [
|
|
39
|
+
{
|
|
40
|
+
feature: "assetInput=non-bitcoin-nonhex",
|
|
41
|
+
description: "Asset inputs outside bitcoin or a 64-hex asset id cannot be resolved into the explicit-v1 hash derivation path.",
|
|
42
|
+
fallbackReasonCode: "FALLBACK_UNSUPPORTED_ASSET",
|
|
43
|
+
manualHashSupported: true,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
feature: "assetForm=confidential",
|
|
47
|
+
description: "Confidential asset commitments are outside the current explicit-v1 auto-derive path. Use raw-output-v1 or manual nextOutputHash.",
|
|
48
|
+
fallbackReasonCode: "FALLBACK_UNSUPPORTED_OUTPUT_FORM",
|
|
49
|
+
manualHashSupported: true,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
feature: "amountForm=confidential",
|
|
53
|
+
description: "Confidential amounts are outside the current explicit-v1 auto-derive path. Use raw-output-v1 or manual nextOutputHash.",
|
|
54
|
+
fallbackReasonCode: "FALLBACK_UNSUPPORTED_OUTPUT_FORM",
|
|
55
|
+
manualHashSupported: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
feature: "nonceForm=confidential",
|
|
59
|
+
description: "Confidential nonces are outside the current explicit-v1 auto-derive path. Use raw-output-v1 or manual nextOutputHash.",
|
|
60
|
+
fallbackReasonCode: "FALLBACK_UNSUPPORTED_OUTPUT_FORM",
|
|
61
|
+
manualHashSupported: true,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
feature: "rangeProofForm=non-empty",
|
|
65
|
+
description: "Non-empty range proofs are outside the current explicit-v1 auto-derive path. Use raw-output-v1 or manual nextOutputHash.",
|
|
66
|
+
fallbackReasonCode: "FALLBACK_UNSUPPORTED_OUTPUT_FORM",
|
|
67
|
+
manualHashSupported: true,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
outputBindingModes: {
|
|
71
|
+
none: {
|
|
72
|
+
description: "No runtime output binding. The transfer remains SDK-verified only.",
|
|
73
|
+
runtimeBinding: "none",
|
|
74
|
+
fallbackBehavior: "No fallback is needed because runtime output binding is disabled.",
|
|
75
|
+
},
|
|
76
|
+
"script-bound": {
|
|
77
|
+
description: "Runtime binds the next output script hash and fee shape.",
|
|
78
|
+
runtimeBinding: "script-hash",
|
|
79
|
+
fallbackBehavior: "Acts as the default downgrade path when descriptor-bound cannot be supported safely.",
|
|
80
|
+
},
|
|
81
|
+
"descriptor-bound": {
|
|
82
|
+
description: "Runtime binds output_hash(0) for the next constrained output.",
|
|
83
|
+
runtimeBinding: "output-hash",
|
|
84
|
+
fallbackBehavior: "If the output form cannot be derived safely and no manual nextOutputHash is supplied, the SDK falls back to script-bound with an explicit reason code.",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
autoDeriveConditions: {
|
|
88
|
+
assetInput: ["bitcoin", "64-hex asset id"],
|
|
89
|
+
amountForm: "explicit amount",
|
|
90
|
+
nonceForm: "null nonce",
|
|
91
|
+
rangeProofForm: "empty range proof",
|
|
92
|
+
rawOutputFields: [
|
|
93
|
+
"assetBytesHex",
|
|
94
|
+
"amountBytesHex",
|
|
95
|
+
"nonceBytesHex",
|
|
96
|
+
"scriptPubKeyHex",
|
|
97
|
+
"scriptPubKeyHashHex",
|
|
98
|
+
"rangeProofHex",
|
|
99
|
+
"rangeProofHashHex",
|
|
100
|
+
],
|
|
101
|
+
rawOutputFieldAlternatives: {
|
|
102
|
+
scriptComponent: ["scriptPubKeyHex", "scriptPubKeyHashHex"],
|
|
103
|
+
rangeProofComponent: ["rangeProofHex", "rangeProofHashHex"],
|
|
104
|
+
},
|
|
105
|
+
outputHashExclusions: ["surjectionProofHex", "surjectionProofHashHex"],
|
|
106
|
+
},
|
|
107
|
+
manualHashPath: {
|
|
108
|
+
supported: true,
|
|
109
|
+
description: "Callers may supply nextOutputHash manually to keep descriptor-bound runtime binding even when explicit-v1/raw-output-v1 auto-derivation is unavailable or intentionally bypassed.",
|
|
110
|
+
},
|
|
111
|
+
fallbackBehavior: {
|
|
112
|
+
defaultMode: "script-bound",
|
|
113
|
+
reasonCodes: [
|
|
114
|
+
"OK_EXPLICIT",
|
|
115
|
+
"OK_RAW_OUTPUT",
|
|
116
|
+
"OK_MANUAL_HASH",
|
|
117
|
+
"OK_SCRIPT_BOUND",
|
|
118
|
+
"OK_NONE",
|
|
119
|
+
"FALLBACK_UNSUPPORTED_ASSET",
|
|
120
|
+
"FALLBACK_UNSUPPORTED_OUTPUT_FORM",
|
|
121
|
+
"FALLBACK_MISSING_HASH_INPUT",
|
|
122
|
+
"FALLBACK_INCOMPLETE_RAW_OUTPUT",
|
|
123
|
+
"FALLBACK_INVALID_RAW_OUTPUT",
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
publicValidationMatrix: {
|
|
127
|
+
local: [
|
|
128
|
+
"required + script-bound",
|
|
129
|
+
"required + descriptor-bound",
|
|
130
|
+
"optional + plain",
|
|
131
|
+
"optional + recursive",
|
|
132
|
+
],
|
|
133
|
+
testnet: [
|
|
134
|
+
"required + script-bound",
|
|
135
|
+
"required + descriptor-bound",
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
nonGoals: [
|
|
139
|
+
"Wallet/RPC-backed confidential output reconstruction from chain data.",
|
|
140
|
+
"Including surjection proofs in output_hash derivation (Elements excludes them from output_hash semantics).",
|
|
141
|
+
"Policy generalized binding beyond Policy Core.",
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
function describeOutputBindingSupport() {
|
|
145
|
+
return JSON.parse(JSON.stringify(exports.OUTPUT_BINDING_SUPPORT_MATRIX));
|
|
146
|
+
}
|
|
147
|
+
function normalizeOutputForm(input) {
|
|
148
|
+
return {
|
|
149
|
+
assetForm: input?.assetForm ?? "explicit",
|
|
150
|
+
amountForm: input?.amountForm ?? "explicit",
|
|
151
|
+
nonceForm: input?.nonceForm ?? "null",
|
|
152
|
+
rangeProofForm: input?.rangeProofForm ?? "empty",
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function normalizeHexValue(value) {
|
|
156
|
+
return value.trim().toLowerCase();
|
|
157
|
+
}
|
|
158
|
+
function isEvenHex(value) {
|
|
159
|
+
return /^[0-9a-f]*$/i.test(value) && value.length % 2 === 0;
|
|
160
|
+
}
|
|
161
|
+
function isFixedHexBytes(value, bytes) {
|
|
162
|
+
return isEvenHex(value) && value.length === bytes * 2;
|
|
163
|
+
}
|
|
164
|
+
function normalizeOutputRawFields(input) {
|
|
165
|
+
if (!input)
|
|
166
|
+
return undefined;
|
|
167
|
+
const normalized = {};
|
|
168
|
+
if (input.assetBytesHex !== undefined)
|
|
169
|
+
normalized.assetBytesHex = normalizeHexValue(input.assetBytesHex);
|
|
170
|
+
if (input.amountBytesHex !== undefined)
|
|
171
|
+
normalized.amountBytesHex = normalizeHexValue(input.amountBytesHex);
|
|
172
|
+
if (input.nonceBytesHex !== undefined)
|
|
173
|
+
normalized.nonceBytesHex = normalizeHexValue(input.nonceBytesHex);
|
|
174
|
+
if (input.scriptPubKeyHex !== undefined)
|
|
175
|
+
normalized.scriptPubKeyHex = normalizeHexValue(input.scriptPubKeyHex);
|
|
176
|
+
if (input.scriptPubKeyHashHex !== undefined) {
|
|
177
|
+
normalized.scriptPubKeyHashHex = normalizeHexValue(input.scriptPubKeyHashHex);
|
|
178
|
+
}
|
|
179
|
+
if (input.rangeProofHex !== undefined)
|
|
180
|
+
normalized.rangeProofHex = normalizeHexValue(input.rangeProofHex);
|
|
181
|
+
if (input.rangeProofHashHex !== undefined) {
|
|
182
|
+
normalized.rangeProofHashHex = normalizeHexValue(input.rangeProofHashHex);
|
|
183
|
+
}
|
|
184
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
185
|
+
}
|
|
186
|
+
function analyzeOutputRawFields(input) {
|
|
187
|
+
const normalized = normalizeOutputRawFields(input);
|
|
188
|
+
if (!normalized) {
|
|
189
|
+
return {
|
|
190
|
+
provided: false,
|
|
191
|
+
complete: false,
|
|
192
|
+
valid: false,
|
|
193
|
+
missingFields: [],
|
|
194
|
+
invalidFields: [],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const missingFields = [];
|
|
198
|
+
if (normalized.assetBytesHex === undefined)
|
|
199
|
+
missingFields.push("assetBytesHex");
|
|
200
|
+
if (normalized.amountBytesHex === undefined)
|
|
201
|
+
missingFields.push("amountBytesHex");
|
|
202
|
+
if (normalized.nonceBytesHex === undefined)
|
|
203
|
+
missingFields.push("nonceBytesHex");
|
|
204
|
+
if (normalized.scriptPubKeyHex === undefined && normalized.scriptPubKeyHashHex === undefined) {
|
|
205
|
+
missingFields.push("scriptPubKeyHex|scriptPubKeyHashHex");
|
|
206
|
+
}
|
|
207
|
+
if (normalized.rangeProofHex === undefined && normalized.rangeProofHashHex === undefined) {
|
|
208
|
+
missingFields.push("rangeProofHex|rangeProofHashHex");
|
|
209
|
+
}
|
|
210
|
+
if (missingFields.length > 0) {
|
|
211
|
+
return {
|
|
212
|
+
provided: true,
|
|
213
|
+
complete: false,
|
|
214
|
+
valid: false,
|
|
215
|
+
missingFields,
|
|
216
|
+
invalidFields: [],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const completed = normalized;
|
|
220
|
+
const invalidFields = [];
|
|
221
|
+
if (!isFixedHexBytes(completed.assetBytesHex, 33)) {
|
|
222
|
+
invalidFields.push("assetBytesHex");
|
|
223
|
+
}
|
|
224
|
+
if (!(isFixedHexBytes(completed.amountBytesHex, 9) || isFixedHexBytes(completed.amountBytesHex, 33))) {
|
|
225
|
+
invalidFields.push("amountBytesHex");
|
|
226
|
+
}
|
|
227
|
+
if (!(isFixedHexBytes(completed.nonceBytesHex, 1) || isFixedHexBytes(completed.nonceBytesHex, 33))) {
|
|
228
|
+
invalidFields.push("nonceBytesHex");
|
|
229
|
+
}
|
|
230
|
+
if (completed.scriptPubKeyHex !== undefined
|
|
231
|
+
&& !isEvenHex(completed.scriptPubKeyHex)) {
|
|
232
|
+
invalidFields.push("scriptPubKeyHex");
|
|
233
|
+
}
|
|
234
|
+
if (completed.scriptPubKeyHashHex !== undefined
|
|
235
|
+
&& !isFixedHexBytes(completed.scriptPubKeyHashHex, 32)) {
|
|
236
|
+
invalidFields.push("scriptPubKeyHashHex");
|
|
237
|
+
}
|
|
238
|
+
if (completed.rangeProofHex !== undefined && !isEvenHex(completed.rangeProofHex)) {
|
|
239
|
+
invalidFields.push("rangeProofHex");
|
|
240
|
+
}
|
|
241
|
+
if (completed.rangeProofHashHex !== undefined
|
|
242
|
+
&& !isFixedHexBytes(completed.rangeProofHashHex, 32)) {
|
|
243
|
+
invalidFields.push("rangeProofHashHex");
|
|
244
|
+
}
|
|
245
|
+
if (completed.scriptPubKeyHex !== undefined
|
|
246
|
+
&& completed.scriptPubKeyHashHex !== undefined
|
|
247
|
+
&& isEvenHex(completed.scriptPubKeyHex)
|
|
248
|
+
&& isFixedHexBytes(completed.scriptPubKeyHashHex, 32)
|
|
249
|
+
&& hashHexBytes(completed.scriptPubKeyHex) !== completed.scriptPubKeyHashHex) {
|
|
250
|
+
invalidFields.push("scriptPubKeyHashHex=mismatch");
|
|
251
|
+
}
|
|
252
|
+
if (completed.rangeProofHex !== undefined
|
|
253
|
+
&& completed.rangeProofHashHex !== undefined
|
|
254
|
+
&& isEvenHex(completed.rangeProofHex)
|
|
255
|
+
&& isFixedHexBytes(completed.rangeProofHashHex, 32)
|
|
256
|
+
&& hashHexBytes(completed.rangeProofHex) !== completed.rangeProofHashHex) {
|
|
257
|
+
invalidFields.push("rangeProofHashHex=mismatch");
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
provided: true,
|
|
261
|
+
complete: true,
|
|
262
|
+
valid: invalidFields.length === 0,
|
|
263
|
+
normalized: invalidFields.length === 0
|
|
264
|
+
? {
|
|
265
|
+
assetBytesHex: completed.assetBytesHex,
|
|
266
|
+
amountBytesHex: completed.amountBytesHex,
|
|
267
|
+
nonceBytesHex: completed.nonceBytesHex,
|
|
268
|
+
scriptPubKeyHashHex: completed.scriptPubKeyHashHex ?? hashHexBytes(completed.scriptPubKeyHex ?? ""),
|
|
269
|
+
rangeProofHashHex: completed.rangeProofHashHex ?? hashHexBytes(completed.rangeProofHex ?? ""),
|
|
270
|
+
scriptComponentSource: completed.scriptPubKeyHashHex ? "hash" : "raw-bytes",
|
|
271
|
+
rangeProofComponentSource: completed.rangeProofHashHex ? "hash" : "raw-bytes",
|
|
272
|
+
}
|
|
273
|
+
: undefined,
|
|
274
|
+
missingFields: [],
|
|
275
|
+
invalidFields,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function isExplicitV1OutputForm(input) {
|
|
279
|
+
return input.assetForm === "explicit"
|
|
280
|
+
&& input.amountForm === "explicit"
|
|
281
|
+
&& input.nonceForm === "null"
|
|
282
|
+
&& input.rangeProofForm === "empty";
|
|
283
|
+
}
|
|
284
|
+
function listUnsupportedOutputFeatures(input) {
|
|
285
|
+
const features = [];
|
|
286
|
+
if (input.assetForm !== "explicit")
|
|
287
|
+
features.push(`assetForm=${input.assetForm}`);
|
|
288
|
+
if (input.amountForm !== "explicit")
|
|
289
|
+
features.push(`amountForm=${input.amountForm}`);
|
|
290
|
+
if (input.nonceForm !== "null")
|
|
291
|
+
features.push(`nonceForm=${input.nonceForm}`);
|
|
292
|
+
if (input.rangeProofForm !== "empty")
|
|
293
|
+
features.push(`rangeProofForm=${input.rangeProofForm}`);
|
|
294
|
+
return features;
|
|
295
|
+
}
|
|
296
|
+
function isExplicitAssetInputSupported(assetId) {
|
|
297
|
+
return assetId.toLowerCase() === "bitcoin" || /^[0-9a-f]{64}$/i.test(assetId);
|
|
298
|
+
}
|
|
299
|
+
function resolveOutputBindingMode(requested, nextOutputHash, nextOutputScriptHash) {
|
|
300
|
+
if (requested === "descriptor-bound" && nextOutputHash) {
|
|
301
|
+
return "descriptor-bound";
|
|
302
|
+
}
|
|
303
|
+
if ((requested === "descriptor-bound" || requested === "script-bound") && nextOutputScriptHash) {
|
|
304
|
+
return "script-bound";
|
|
305
|
+
}
|
|
306
|
+
return "none";
|
|
307
|
+
}
|
|
308
|
+
function hashHexBytes(hex) {
|
|
309
|
+
return (0, node_crypto_1.createHash)("sha256").update(Buffer.from(hex, "hex")).digest("hex");
|
|
310
|
+
}
|
|
311
|
+
function reverseHexBytes(hex) {
|
|
312
|
+
return hex.match(/../g)?.reverse().join("") ?? hex;
|
|
313
|
+
}
|
|
314
|
+
function encodeExplicitAsset(assetHex) {
|
|
315
|
+
return Buffer.concat([
|
|
316
|
+
Buffer.from([0x01]),
|
|
317
|
+
Buffer.from(reverseHexBytes(assetHex), "hex"),
|
|
318
|
+
]);
|
|
319
|
+
}
|
|
320
|
+
function encodeExplicitAmount(amountSat) {
|
|
321
|
+
const amount = Buffer.alloc(9);
|
|
322
|
+
amount[0] = 0x01;
|
|
323
|
+
amount.writeBigUInt64BE(BigInt(amountSat), 1);
|
|
324
|
+
return amount;
|
|
325
|
+
}
|
|
326
|
+
function encodeNullNonce() {
|
|
327
|
+
return Buffer.from([0x00]);
|
|
328
|
+
}
|
|
329
|
+
function computeExplicitV1OutputHash(input) {
|
|
330
|
+
return (0, node_crypto_1.createHash)("sha256")
|
|
331
|
+
.update(encodeExplicitAsset(input.assetHex))
|
|
332
|
+
.update(encodeExplicitAmount(input.nextAmountSat))
|
|
333
|
+
.update(encodeNullNonce())
|
|
334
|
+
.update(Buffer.from(input.nextOutputScriptHash, "hex"))
|
|
335
|
+
.update(EMPTY_BUFFER_SHA256)
|
|
336
|
+
.digest("hex");
|
|
337
|
+
}
|
|
338
|
+
function computeRawOutputV1Hash(input) {
|
|
339
|
+
const analysis = analyzeOutputRawFields(input);
|
|
340
|
+
if (!analysis.valid || !analysis.normalized) {
|
|
341
|
+
const reason = analysis.complete
|
|
342
|
+
? `invalid raw-output-v1 input (${analysis.invalidFields.join(", ")})`
|
|
343
|
+
: `incomplete raw-output-v1 input (missing ${analysis.missingFields.join(", ")})`;
|
|
344
|
+
throw new Error(reason);
|
|
345
|
+
}
|
|
346
|
+
return (0, node_crypto_1.createHash)("sha256")
|
|
347
|
+
.update(Buffer.from(analysis.normalized.assetBytesHex, "hex"))
|
|
348
|
+
.update(Buffer.from(analysis.normalized.amountBytesHex, "hex"))
|
|
349
|
+
.update(Buffer.from(analysis.normalized.nonceBytesHex, "hex"))
|
|
350
|
+
.update(Buffer.from(analysis.normalized.scriptPubKeyHashHex, "hex"))
|
|
351
|
+
.update(Buffer.from(analysis.normalized.rangeProofHashHex, "hex"))
|
|
352
|
+
.digest("hex");
|
|
353
|
+
}
|
|
354
|
+
async function getScriptPubKeyHexViaRpc(sdk, address) {
|
|
355
|
+
const result = await sdk.rpc.call("getaddressinfo", [address]);
|
|
356
|
+
const scriptPubKey = result.scriptPubKey?.toLowerCase();
|
|
357
|
+
if (!scriptPubKey) {
|
|
358
|
+
throw new Error(`Could not derive scriptPubKey for address: ${address}`);
|
|
359
|
+
}
|
|
360
|
+
return scriptPubKey;
|
|
361
|
+
}
|
|
362
|
+
async function resolveExplicitAssetHex(sdk, assetId) {
|
|
363
|
+
if (/^[0-9a-f]{64}$/i.test(assetId)) {
|
|
364
|
+
return assetId.toLowerCase();
|
|
365
|
+
}
|
|
366
|
+
if (assetId.toLowerCase() === "bitcoin") {
|
|
367
|
+
const sidechain = await sdk.rpc.call("getsidechaininfo", []);
|
|
368
|
+
if (sidechain.pegged_asset && /^[0-9a-f]{64}$/i.test(sidechain.pegged_asset)) {
|
|
369
|
+
return sidechain.pegged_asset.toLowerCase();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
function resolveOutputBindingDecision(input) {
|
|
375
|
+
const requestedBindingMode = input.requestedBindingMode ?? "none";
|
|
376
|
+
const outputForm = normalizeOutputForm(input.outputForm);
|
|
377
|
+
const rawOutputAnalysis = analyzeOutputRawFields(input.rawOutput);
|
|
378
|
+
const explicitFormSupported = isExplicitV1OutputForm(outputForm);
|
|
379
|
+
const supportedForm = rawOutputAnalysis.valid
|
|
380
|
+
? "raw-output-v1"
|
|
381
|
+
: explicitFormSupported && input.explicitAssetSupported
|
|
382
|
+
? "explicit-v1"
|
|
383
|
+
: "unsupported";
|
|
384
|
+
const outputBindingMode = resolveOutputBindingMode(requestedBindingMode, input.nextOutputHash, input.nextOutputScriptHash);
|
|
385
|
+
const autoDerived = Boolean(input.autoDerivedNextOutputHash);
|
|
386
|
+
const unsupportedFeatures = rawOutputAnalysis.valid
|
|
387
|
+
? []
|
|
388
|
+
: [
|
|
389
|
+
...listUnsupportedOutputFeatures(outputForm),
|
|
390
|
+
...(rawOutputAnalysis.complete && !rawOutputAnalysis.valid
|
|
391
|
+
? rawOutputAnalysis.invalidFields.map((field) => `${field}=invalid`)
|
|
392
|
+
: []),
|
|
393
|
+
];
|
|
394
|
+
const buildFallbackForDescriptorBound = () => {
|
|
395
|
+
if (rawOutputAnalysis.valid) {
|
|
396
|
+
return {
|
|
397
|
+
reasonCode: "FALLBACK_MISSING_HASH_INPUT",
|
|
398
|
+
fallbackReason: "descriptor-bound requested and raw-output-v1 inputs are valid, but nextOutputHash was not available after derivation",
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (rawOutputAnalysis.provided && !rawOutputAnalysis.complete) {
|
|
402
|
+
return {
|
|
403
|
+
reasonCode: "FALLBACK_INCOMPLETE_RAW_OUTPUT",
|
|
404
|
+
fallbackReason: `descriptor-bound requested but raw-output-v1 is incomplete (missing ${rawOutputAnalysis.missingFields.join(", ")})`,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (rawOutputAnalysis.provided && rawOutputAnalysis.complete && !rawOutputAnalysis.valid) {
|
|
408
|
+
return {
|
|
409
|
+
reasonCode: "FALLBACK_INVALID_RAW_OUTPUT",
|
|
410
|
+
fallbackReason: `descriptor-bound requested but raw-output-v1 is invalid (${rawOutputAnalysis.invalidFields.join(", ")})`,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (!explicitFormSupported) {
|
|
414
|
+
return {
|
|
415
|
+
reasonCode: "FALLBACK_UNSUPPORTED_OUTPUT_FORM",
|
|
416
|
+
fallbackReason: `descriptor-bound requested but the current output form is outside the explicit-v1 support matrix (${unsupportedFeatures.join(", ")})`,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
if (!input.explicitAssetSupported) {
|
|
420
|
+
return {
|
|
421
|
+
reasonCode: "FALLBACK_UNSUPPORTED_ASSET",
|
|
422
|
+
fallbackReason: "descriptor-bound requested but assetId could not be resolved into an explicit 64-hex asset id (expected bitcoin or a 64-hex asset id)",
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
reasonCode: "FALLBACK_MISSING_HASH_INPUT",
|
|
427
|
+
fallbackReason: "descriptor-bound requested but nextOutputHash was not provided",
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
if (requestedBindingMode === "none") {
|
|
431
|
+
return {
|
|
432
|
+
requestedBindingMode,
|
|
433
|
+
outputBindingMode,
|
|
434
|
+
supportedForm,
|
|
435
|
+
reasonCode: "OK_NONE",
|
|
436
|
+
autoDerived: false,
|
|
437
|
+
outputForm,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
if (outputBindingMode === "descriptor-bound") {
|
|
441
|
+
return {
|
|
442
|
+
requestedBindingMode,
|
|
443
|
+
outputBindingMode,
|
|
444
|
+
supportedForm,
|
|
445
|
+
reasonCode: autoDerived
|
|
446
|
+
? supportedForm === "raw-output-v1"
|
|
447
|
+
? "OK_RAW_OUTPUT"
|
|
448
|
+
: "OK_EXPLICIT"
|
|
449
|
+
: "OK_MANUAL_HASH",
|
|
450
|
+
autoDerived,
|
|
451
|
+
outputForm,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (requestedBindingMode === "descriptor-bound") {
|
|
455
|
+
if (!input.nextOutputHash) {
|
|
456
|
+
const { reasonCode, fallbackReason } = buildFallbackForDescriptorBound();
|
|
457
|
+
return {
|
|
458
|
+
requestedBindingMode,
|
|
459
|
+
outputBindingMode,
|
|
460
|
+
supportedForm,
|
|
461
|
+
reasonCode,
|
|
462
|
+
autoDerived: false,
|
|
463
|
+
fallbackReason,
|
|
464
|
+
outputForm,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (requestedBindingMode === "script-bound") {
|
|
469
|
+
if (outputBindingMode === "script-bound") {
|
|
470
|
+
return {
|
|
471
|
+
requestedBindingMode,
|
|
472
|
+
outputBindingMode,
|
|
473
|
+
supportedForm,
|
|
474
|
+
reasonCode: "OK_SCRIPT_BOUND",
|
|
475
|
+
autoDerived: false,
|
|
476
|
+
outputForm,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
requestedBindingMode,
|
|
481
|
+
outputBindingMode,
|
|
482
|
+
supportedForm,
|
|
483
|
+
reasonCode: "FALLBACK_MISSING_HASH_INPUT",
|
|
484
|
+
autoDerived: false,
|
|
485
|
+
fallbackReason: "script-bound requested but nextOutputScriptHash was not available",
|
|
486
|
+
outputForm,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
requestedBindingMode,
|
|
491
|
+
outputBindingMode,
|
|
492
|
+
supportedForm,
|
|
493
|
+
reasonCode: "OK_SCRIPT_BOUND",
|
|
494
|
+
autoDerived: false,
|
|
495
|
+
outputForm,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function evaluateOutputBindingSupport(input) {
|
|
499
|
+
const outputForm = normalizeOutputForm(input.outputForm);
|
|
500
|
+
const rawOutputAnalysis = analyzeOutputRawFields(input.rawOutput);
|
|
501
|
+
const explicitAssetInputSupported = isExplicitAssetInputSupported(input.assetId);
|
|
502
|
+
const autoDerivedNextOutputHash = (input.requestedBindingMode ?? "none") === "descriptor-bound"
|
|
503
|
+
&& !input.nextOutputHash
|
|
504
|
+
&& rawOutputAnalysis.valid
|
|
505
|
+
&& rawOutputAnalysis.normalized
|
|
506
|
+
? computeRawOutputV1Hash(rawOutputAnalysis.normalized)
|
|
507
|
+
: undefined;
|
|
508
|
+
const unsupportedFeatures = rawOutputAnalysis.valid
|
|
509
|
+
? []
|
|
510
|
+
: [
|
|
511
|
+
...(explicitAssetInputSupported ? [] : ["assetInput=non-bitcoin-nonhex"]),
|
|
512
|
+
...listUnsupportedOutputFeatures(outputForm),
|
|
513
|
+
...(rawOutputAnalysis.provided && !rawOutputAnalysis.complete
|
|
514
|
+
? rawOutputAnalysis.missingFields.map((field) => `${field}=missing`)
|
|
515
|
+
: []),
|
|
516
|
+
...(rawOutputAnalysis.complete && !rawOutputAnalysis.valid
|
|
517
|
+
? rawOutputAnalysis.invalidFields.map((field) => `${field}=invalid`)
|
|
518
|
+
: []),
|
|
519
|
+
];
|
|
520
|
+
const decision = resolveOutputBindingDecision({
|
|
521
|
+
requestedBindingMode: input.requestedBindingMode,
|
|
522
|
+
nextOutputHash: input.nextOutputHash ?? autoDerivedNextOutputHash,
|
|
523
|
+
nextOutputScriptHash: input.nextOutputScriptAvailable === false ? undefined : "available",
|
|
524
|
+
autoDerivedNextOutputHash: Boolean(autoDerivedNextOutputHash && !input.nextOutputHash),
|
|
525
|
+
explicitAssetSupported: explicitAssetInputSupported,
|
|
526
|
+
outputForm,
|
|
527
|
+
rawOutput: input.rawOutput,
|
|
528
|
+
});
|
|
529
|
+
return {
|
|
530
|
+
requestedBindingMode: decision.requestedBindingMode,
|
|
531
|
+
resolvedBindingMode: decision.outputBindingMode,
|
|
532
|
+
supportedForm: decision.supportedForm,
|
|
533
|
+
reasonCode: decision.reasonCode,
|
|
534
|
+
autoDerived: decision.autoDerived,
|
|
535
|
+
fallbackReason: decision.fallbackReason,
|
|
536
|
+
assetId: input.assetId,
|
|
537
|
+
outputForm,
|
|
538
|
+
unsupportedFeatures,
|
|
539
|
+
explicitAssetInputSupported,
|
|
540
|
+
manualHashSupplied: Boolean(input.nextOutputHash),
|
|
541
|
+
nextOutputScriptAvailable: input.nextOutputScriptAvailable !== false,
|
|
542
|
+
rawOutputProvided: rawOutputAnalysis.provided,
|
|
543
|
+
...(rawOutputAnalysis.valid && rawOutputAnalysis.normalized
|
|
544
|
+
? {
|
|
545
|
+
rawOutputComponents: {
|
|
546
|
+
scriptPubKey: rawOutputAnalysis.normalized.scriptComponentSource,
|
|
547
|
+
rangeProof: rawOutputAnalysis.normalized.rangeProofComponentSource,
|
|
548
|
+
},
|
|
549
|
+
}
|
|
550
|
+
: {}),
|
|
551
|
+
};
|
|
552
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BondOutputBindingMode, DefinitionVerificationResult, DomainLineageTrust, LineageTrustBase, StateVerificationResult, VerificationTrustSummary } from "./types";
|
|
2
|
+
export declare function buildLineageTrustBase<LineageKind extends string>(input: LineageTrustBase<LineageKind>): LineageTrustBase<LineageKind>;
|
|
3
|
+
export declare function buildVerificationTrustSections<TDefinitionTrust, TStateTrust>(input: {
|
|
4
|
+
definitionTrust?: TDefinitionTrust;
|
|
5
|
+
stateTrust?: TStateTrust;
|
|
6
|
+
requireArtifactTrust?: boolean;
|
|
7
|
+
emptyDefinitionTrust: TDefinitionTrust;
|
|
8
|
+
emptyStateTrust: TStateTrust;
|
|
9
|
+
}): {
|
|
10
|
+
artifactTrust: {
|
|
11
|
+
definition: TDefinitionTrust;
|
|
12
|
+
state: TStateTrust;
|
|
13
|
+
};
|
|
14
|
+
stateTrust: TStateTrust;
|
|
15
|
+
};
|
|
16
|
+
export declare function buildDomainLineageTrustSummary(lineageTrust?: DomainLineageTrust): VerificationTrustSummary["lineage"] | undefined;
|
|
17
|
+
export declare function buildVerificationTrustSummary(input: {
|
|
18
|
+
definitionTrust?: DefinitionVerificationResult["trust"];
|
|
19
|
+
stateTrust?: StateVerificationResult["trust"];
|
|
20
|
+
bindingMode: BondOutputBindingMode;
|
|
21
|
+
lineageTrust?: DomainLineageTrust;
|
|
22
|
+
}): VerificationTrustSummary;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildLineageTrustBase = buildLineageTrustBase;
|
|
4
|
+
exports.buildVerificationTrustSections = buildVerificationTrustSections;
|
|
5
|
+
exports.buildDomainLineageTrustSummary = buildDomainLineageTrustSummary;
|
|
6
|
+
exports.buildVerificationTrustSummary = buildVerificationTrustSummary;
|
|
7
|
+
function buildLineageTrustBase(input) {
|
|
8
|
+
return { ...input };
|
|
9
|
+
}
|
|
10
|
+
function buildVerificationTrustSections(input) {
|
|
11
|
+
void input.requireArtifactTrust;
|
|
12
|
+
const definitionTrust = input.definitionTrust ?? input.emptyDefinitionTrust;
|
|
13
|
+
const stateTrust = input.stateTrust ?? input.emptyStateTrust;
|
|
14
|
+
return {
|
|
15
|
+
artifactTrust: {
|
|
16
|
+
definition: definitionTrust,
|
|
17
|
+
state: stateTrust,
|
|
18
|
+
},
|
|
19
|
+
stateTrust,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function buildDomainLineageTrustSummary(lineageTrust) {
|
|
23
|
+
if (!lineageTrust)
|
|
24
|
+
return undefined;
|
|
25
|
+
return {
|
|
26
|
+
lineageKind: lineageTrust.lineageKind,
|
|
27
|
+
latestOrdinal: lineageTrust.latestOrdinal,
|
|
28
|
+
allHashLinksVerified: lineageTrust.allHashLinksVerified,
|
|
29
|
+
identityConsistent: lineageTrust.identityConsistent,
|
|
30
|
+
fullLineageVerified: lineageTrust.fullLineageVerified,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function buildVerificationTrustSummary(input) {
|
|
34
|
+
return {
|
|
35
|
+
...(input.definitionTrust ? { definition: input.definitionTrust } : {}),
|
|
36
|
+
...(input.stateTrust ? { state: input.stateTrust } : {}),
|
|
37
|
+
bindingMode: input.bindingMode,
|
|
38
|
+
...(input.lineageTrust ? { lineage: buildDomainLineageTrustSummary(input.lineageTrust) } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function hexToBytes(hex: string, byteLength?: number | null): Uint8Array;
|
|
2
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
3
|
+
export declare function schnorrPublicKeyFromPrivkeyHex(privkeyHex: string): Promise<string>;
|
|
4
|
+
export declare function schnorrSignHex(messageHashHex: string, privkeyHex: string): Promise<string>;
|
|
5
|
+
export declare function schnorrVerifyHex(signatureHex: string, messageHashHex: string, publicKeyHex: string): Promise<boolean>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hexToBytes = hexToBytes;
|
|
4
|
+
exports.bytesToHex = bytesToHex;
|
|
5
|
+
exports.schnorrPublicKeyFromPrivkeyHex = schnorrPublicKeyFromPrivkeyHex;
|
|
6
|
+
exports.schnorrSignHex = schnorrSignHex;
|
|
7
|
+
exports.schnorrVerifyHex = schnorrVerifyHex;
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
// Keep native dynamic import intact under CommonJS output so Node can load the ESM-only noble package.
|
|
10
|
+
const nativeImport = new Function("specifier", "return import(specifier);");
|
|
11
|
+
let schnorrPromise;
|
|
12
|
+
async function loadSchnorr() {
|
|
13
|
+
schnorrPromise ??= nativeImport("@noble/curves/secp256k1.js").then((module) => {
|
|
14
|
+
const typed = module;
|
|
15
|
+
return typed.schnorr;
|
|
16
|
+
});
|
|
17
|
+
return schnorrPromise;
|
|
18
|
+
}
|
|
19
|
+
function assertHex(value, byteLength, code, message) {
|
|
20
|
+
if (!/^[0-9a-f]+$/i.test(value) || value.length % 2 !== 0) {
|
|
21
|
+
throw new errors_1.ValidationError(message, { code });
|
|
22
|
+
}
|
|
23
|
+
if (byteLength !== null && value.length !== byteLength * 2) {
|
|
24
|
+
throw new errors_1.ValidationError(message, { code });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function hexToBytes(hex, byteLength = null) {
|
|
28
|
+
const normalized = hex.trim().toLowerCase().replace(/^0x/, "");
|
|
29
|
+
assertHex(normalized, byteLength, "SCHNORR_HEX_INVALID", "Expected a valid hex string");
|
|
30
|
+
return Uint8Array.from(Buffer.from(normalized, "hex"));
|
|
31
|
+
}
|
|
32
|
+
function bytesToHex(bytes) {
|
|
33
|
+
return Buffer.from(bytes).toString("hex");
|
|
34
|
+
}
|
|
35
|
+
async function schnorrPublicKeyFromPrivkeyHex(privkeyHex) {
|
|
36
|
+
const schnorr = await loadSchnorr();
|
|
37
|
+
return bytesToHex(schnorr.getPublicKey(hexToBytes(privkeyHex, 32)));
|
|
38
|
+
}
|
|
39
|
+
async function schnorrSignHex(messageHashHex, privkeyHex) {
|
|
40
|
+
const schnorr = await loadSchnorr();
|
|
41
|
+
return bytesToHex(schnorr.sign(hexToBytes(messageHashHex, 32), hexToBytes(privkeyHex, 32)));
|
|
42
|
+
}
|
|
43
|
+
async function schnorrVerifyHex(signatureHex, messageHashHex, publicKeyHex) {
|
|
44
|
+
const schnorr = await loadSchnorr();
|
|
45
|
+
return schnorr.verify(hexToBytes(signatureHex, 64), hexToBytes(messageHashHex, 32), hexToBytes(publicKeyHex, 32));
|
|
46
|
+
}
|