@hazbase/simplicity 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +679 -42
  2. package/dist/cli.js +2268 -5
  3. package/dist/client/SimplicityClient.d.ts +64 -25
  4. package/dist/client/SimplicityClient.js +65 -3
  5. package/dist/core/executor.js +67 -92
  6. package/dist/core/outputBinding.d.ts +61 -0
  7. package/dist/core/outputBinding.js +552 -0
  8. package/dist/core/schnorr.d.ts +5 -0
  9. package/dist/core/schnorr.js +34 -0
  10. package/dist/core/types.d.ts +586 -1
  11. package/dist/docs/definitions/bond-anchor.simf +19 -0
  12. package/dist/docs/definitions/bond-definition.json +10 -0
  13. package/dist/docs/definitions/bond-descriptor-bound-settlement-machine.simf +144 -0
  14. package/dist/docs/definitions/bond-issuance-anchor.simf +26 -0
  15. package/dist/docs/definitions/bond-issuance-state-partial-redemption.json +18 -0
  16. package/dist/docs/definitions/bond-issuance-state-redeemed.json +18 -0
  17. package/dist/docs/definitions/bond-issuance-state.json +12 -0
  18. package/dist/docs/definitions/bond-redemption-state-machine.simf +118 -0
  19. package/dist/docs/definitions/bond-redemption-transition.simf +41 -0
  20. package/dist/docs/definitions/bond-script-bound-settlement-machine.simf +142 -0
  21. package/dist/docs/definitions/fund-capital-call-open.simf +82 -0
  22. package/dist/docs/definitions/fund-capital-call-refund-only.simf +33 -0
  23. package/dist/docs/definitions/fund-capital-call-state.json +11 -0
  24. package/dist/docs/definitions/fund-definition.json +8 -0
  25. package/dist/docs/definitions/fund-distribution-claim.simf +57 -0
  26. package/dist/docs/definitions/recursive-delay-direct-next.simf +60 -0
  27. package/dist/docs/definitions/recursive-delay-optional.simf +88 -0
  28. package/dist/docs/definitions/recursive-delay-required.simf +72 -0
  29. package/dist/docs/definitions/recursive-delay.simf +83 -0
  30. package/dist/docs/definitions/recursive-policy-transfer-machine.simf +65 -0
  31. package/dist/domain/bond.d.ts +9855 -1
  32. package/dist/domain/bond.js +2156 -1
  33. package/dist/domain/bondSettlementValidation.d.ts +20 -0
  34. package/dist/domain/bondSettlementValidation.js +150 -0
  35. package/dist/domain/bondValidation.d.ts +26 -0
  36. package/dist/domain/bondValidation.js +278 -3
  37. package/dist/domain/fund.d.ts +2069 -0
  38. package/dist/domain/fund.js +1384 -0
  39. package/dist/domain/fundValidation.d.ts +122 -0
  40. package/dist/domain/fundValidation.js +635 -0
  41. package/dist/domain/policies.d.ts +1051 -0
  42. package/dist/domain/policies.js +1605 -0
  43. package/dist/index.d.ts +7 -2
  44. package/dist/index.js +92 -1
  45. package/package.json +15 -2
@@ -0,0 +1,1605 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.POLICY_EVIDENCE_BUNDLE_SCHEMA_VERSION = exports.POLICY_VERIFICATION_REPORT_SCHEMA_VERSION = exports.POLICY_TEMPLATE_MANIFEST_SCHEMA_VERSION = void 0;
7
+ exports.listPolicyTemplates = listPolicyTemplates;
8
+ exports.describePolicyTemplate = describePolicyTemplate;
9
+ exports.validatePolicyTemplateManifest = validatePolicyTemplateManifest;
10
+ exports.loadPolicyTemplateManifest = loadPolicyTemplateManifest;
11
+ exports.validatePolicyTemplateParams = validatePolicyTemplateParams;
12
+ exports.summarizePolicyState = summarizePolicyState;
13
+ exports.validatePolicyState = validatePolicyState;
14
+ exports.summarizePolicyOutputDescriptor = summarizePolicyOutputDescriptor;
15
+ exports.validatePolicyOutputDescriptor = validatePolicyOutputDescriptor;
16
+ exports.summarizePolicyTransferDescriptor = summarizePolicyTransferDescriptor;
17
+ exports.validatePolicyTransferDescriptor = validatePolicyTransferDescriptor;
18
+ exports.compilePolicyStateContract = compilePolicyStateContract;
19
+ exports.issue = issue;
20
+ exports.verifyState = verifyState;
21
+ exports.buildPolicyOutputDescriptor = buildPolicyOutputDescriptor;
22
+ exports.prepareTransfer = prepareTransfer;
23
+ exports.prepareDirectTransfer = prepareDirectTransfer;
24
+ exports.verifyDirectTransfer = verifyDirectTransfer;
25
+ exports.inspectDirectTransfer = inspectDirectTransfer;
26
+ exports.executeDirectTransfer = executeDirectTransfer;
27
+ exports.verifyTransfer = verifyTransfer;
28
+ exports.inspectTransfer = inspectTransfer;
29
+ exports.executeTransfer = executeTransfer;
30
+ exports.exportEvidence = exportEvidence;
31
+ const promises_1 = require("node:fs/promises");
32
+ const node_fs_1 = require("node:fs");
33
+ const node_path_1 = __importDefault(require("node:path"));
34
+ const summary_1 = require("../core/summary");
35
+ const errors_1 = require("../core/errors");
36
+ const outputBinding_1 = require("../core/outputBinding");
37
+ function resolvePolicyDocsAsset(filename) {
38
+ const cwdCandidate = node_path_1.default.resolve(process.cwd(), "docs/definitions", filename);
39
+ if ((0, node_fs_1.existsSync)(cwdCandidate)) {
40
+ return cwdCandidate;
41
+ }
42
+ const bundledCandidate = node_path_1.default.resolve(__dirname, "../docs/definitions", filename);
43
+ if ((0, node_fs_1.existsSync)(bundledCandidate)) {
44
+ return bundledCandidate;
45
+ }
46
+ return cwdCandidate;
47
+ }
48
+ exports.POLICY_TEMPLATE_MANIFEST_SCHEMA_VERSION = "policy-template-manifest/v1";
49
+ exports.POLICY_VERIFICATION_REPORT_SCHEMA_VERSION = "policy-verification-report/v1";
50
+ exports.POLICY_EVIDENCE_BUNDLE_SCHEMA_VERSION = "policy-evidence-bundle/v1";
51
+ const POLICY_TEMPLATE_MANIFESTS = {
52
+ "recursive-delay-required": {
53
+ templateId: "recursive-delay-required",
54
+ manifestVersion: exports.POLICY_TEMPLATE_MANIFEST_SCHEMA_VERSION,
55
+ title: "Recursive Delay (Required)",
56
+ description: "1tx recursive delay covenant that requires the next hop to remain policy-aware.",
57
+ stateSimfFilename: "recursive-delay-required.simf",
58
+ directStateSimfFilename: "recursive-delay-required.simf",
59
+ parameterSchema: { lockDistanceBlocks: "number" },
60
+ supportedBindingModes: ["script-bound", "descriptor-bound"],
61
+ supportsPlainExit: false,
62
+ defaultPropagationMode: "required",
63
+ },
64
+ "recursive-delay-optional": {
65
+ templateId: "recursive-delay-optional",
66
+ manifestVersion: exports.POLICY_TEMPLATE_MANIFEST_SCHEMA_VERSION,
67
+ title: "Recursive Delay (Optional)",
68
+ description: "1tx recursive delay covenant that can plain-exit or continue into the next policy-aware hop.",
69
+ stateSimfFilename: "recursive-delay-optional.simf",
70
+ directStateSimfFilename: "recursive-delay-required.simf",
71
+ parameterSchema: { lockDistanceBlocks: "number" },
72
+ supportedBindingModes: ["none", "script-bound", "descriptor-bound"],
73
+ supportsPlainExit: true,
74
+ defaultPropagationMode: "optional",
75
+ },
76
+ };
77
+ function resolveManifestKey(templateId, propagationMode) {
78
+ if (templateId in POLICY_TEMPLATE_MANIFESTS)
79
+ return templateId;
80
+ if (templateId === "recursive-delay") {
81
+ return propagationMode === "required" ? "recursive-delay-required" : "recursive-delay-optional";
82
+ }
83
+ throw new errors_1.ValidationError(`Unknown policy template: ${templateId}`, {
84
+ code: "POLICY_TEMPLATE_UNKNOWN",
85
+ });
86
+ }
87
+ function materializeBuiltInManifest(manifest) {
88
+ return {
89
+ templateId: manifest.templateId,
90
+ manifestVersion: manifest.manifestVersion,
91
+ title: manifest.title,
92
+ description: manifest.description,
93
+ stateSimfPath: resolvePolicyDocsAsset(manifest.stateSimfFilename),
94
+ ...(manifest.directStateSimfFilename
95
+ ? { directStateSimfPath: resolvePolicyDocsAsset(manifest.directStateSimfFilename) }
96
+ : {}),
97
+ parameterSchema: manifest.parameterSchema,
98
+ supportedBindingModes: manifest.supportedBindingModes,
99
+ supportsPlainExit: manifest.supportsPlainExit,
100
+ defaultPropagationMode: manifest.defaultPropagationMode,
101
+ };
102
+ }
103
+ function listPolicyTemplates() {
104
+ return Object.values(POLICY_TEMPLATE_MANIFESTS).map((manifest) => materializeBuiltInManifest(manifest));
105
+ }
106
+ function describePolicyTemplate(input) {
107
+ const manifest = POLICY_TEMPLATE_MANIFESTS[resolveManifestKey(input.templateId, input.propagationMode)];
108
+ return materializeBuiltInManifest(manifest);
109
+ }
110
+ function validatePolicyTemplateManifest(input) {
111
+ const value = input.manifestValue;
112
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
113
+ return {
114
+ ok: false,
115
+ reasonCode: "MANIFEST_FIELD_REQUIRED",
116
+ reason: "Policy template manifest must be an object",
117
+ };
118
+ }
119
+ const manifest = value;
120
+ if (manifest.manifestVersion !== exports.POLICY_TEMPLATE_MANIFEST_SCHEMA_VERSION) {
121
+ return {
122
+ ok: false,
123
+ reasonCode: "MANIFEST_VERSION_UNSUPPORTED",
124
+ reason: `manifestVersion must be ${exports.POLICY_TEMPLATE_MANIFEST_SCHEMA_VERSION}`,
125
+ };
126
+ }
127
+ if (typeof manifest.templateId !== "string" || manifest.templateId.trim().length === 0) {
128
+ return {
129
+ ok: false,
130
+ reasonCode: "MANIFEST_FIELD_REQUIRED",
131
+ reason: "templateId must be a non-empty string",
132
+ };
133
+ }
134
+ if (typeof manifest.title !== "string" || manifest.title.trim().length === 0) {
135
+ return {
136
+ ok: false,
137
+ reasonCode: "MANIFEST_FIELD_REQUIRED",
138
+ reason: "title must be a non-empty string",
139
+ };
140
+ }
141
+ if (typeof manifest.description !== "string" || manifest.description.trim().length === 0) {
142
+ return {
143
+ ok: false,
144
+ reasonCode: "MANIFEST_FIELD_REQUIRED",
145
+ reason: "description must be a non-empty string",
146
+ };
147
+ }
148
+ if (typeof manifest.stateSimfPath !== "string" || manifest.stateSimfPath.trim().length === 0) {
149
+ return {
150
+ ok: false,
151
+ reasonCode: "MANIFEST_FIELD_REQUIRED",
152
+ reason: "stateSimfPath must be a non-empty string",
153
+ };
154
+ }
155
+ if (manifest.directStateSimfPath !== undefined
156
+ && (typeof manifest.directStateSimfPath !== "string" || manifest.directStateSimfPath.trim().length === 0)) {
157
+ return {
158
+ ok: false,
159
+ reasonCode: "MANIFEST_FIELD_INVALID",
160
+ reason: "directStateSimfPath must be a non-empty string when provided",
161
+ };
162
+ }
163
+ if (!manifest.parameterSchema || typeof manifest.parameterSchema !== "object" || Array.isArray(manifest.parameterSchema)) {
164
+ return {
165
+ ok: false,
166
+ reasonCode: "MANIFEST_PARAMETER_SCHEMA_INVALID",
167
+ reason: "parameterSchema must be an object",
168
+ };
169
+ }
170
+ for (const [key, kind] of Object.entries(manifest.parameterSchema)) {
171
+ if (!["string", "number", "boolean"].includes(String(kind))) {
172
+ return {
173
+ ok: false,
174
+ reasonCode: "MANIFEST_PARAMETER_SCHEMA_INVALID",
175
+ reason: `parameterSchema.${key} must be string, number, or boolean`,
176
+ };
177
+ }
178
+ }
179
+ if (!Array.isArray(manifest.supportedBindingModes) || manifest.supportedBindingModes.length === 0) {
180
+ return {
181
+ ok: false,
182
+ reasonCode: "MANIFEST_BINDING_MODE_INVALID",
183
+ reason: "supportedBindingModes must be a non-empty array",
184
+ };
185
+ }
186
+ for (const mode of manifest.supportedBindingModes) {
187
+ if (!["none", "script-bound", "descriptor-bound"].includes(String(mode))) {
188
+ return {
189
+ ok: false,
190
+ reasonCode: "MANIFEST_BINDING_MODE_INVALID",
191
+ reason: `Unsupported binding mode: ${String(mode)}`,
192
+ };
193
+ }
194
+ }
195
+ if (typeof manifest.supportsPlainExit !== "boolean") {
196
+ return {
197
+ ok: false,
198
+ reasonCode: "MANIFEST_FIELD_INVALID",
199
+ reason: "supportsPlainExit must be a boolean",
200
+ };
201
+ }
202
+ if (!["required", "optional", "none"].includes(String(manifest.defaultPropagationMode))) {
203
+ return {
204
+ ok: false,
205
+ reasonCode: "MANIFEST_PROPAGATION_MODE_INVALID",
206
+ reason: "defaultPropagationMode must be required, optional, or none",
207
+ };
208
+ }
209
+ return {
210
+ ok: true,
211
+ reasonCode: "OK",
212
+ manifest: {
213
+ templateId: manifest.templateId,
214
+ manifestVersion: manifest.manifestVersion,
215
+ title: manifest.title,
216
+ description: manifest.description,
217
+ stateSimfPath: manifest.stateSimfPath,
218
+ ...(manifest.directStateSimfPath ? { directStateSimfPath: manifest.directStateSimfPath } : {}),
219
+ parameterSchema: manifest.parameterSchema,
220
+ supportedBindingModes: manifest.supportedBindingModes,
221
+ supportsPlainExit: manifest.supportsPlainExit,
222
+ defaultPropagationMode: manifest.defaultPropagationMode,
223
+ },
224
+ };
225
+ }
226
+ async function loadPolicyTemplateManifest(input) {
227
+ if (input.manifestValue !== undefined) {
228
+ const validated = validatePolicyTemplateManifest({ manifestValue: input.manifestValue });
229
+ if (!validated.ok || !validated.manifest) {
230
+ throw new errors_1.ValidationError(validated.reason ?? "Policy template manifest is invalid", {
231
+ code: validated.reasonCode,
232
+ });
233
+ }
234
+ return validated.manifest;
235
+ }
236
+ if (input.manifestPath) {
237
+ const raw = await (0, promises_1.readFile)(input.manifestPath, "utf8");
238
+ const validated = validatePolicyTemplateManifest({ manifestValue: JSON.parse(raw) });
239
+ if (!validated.ok || !validated.manifest) {
240
+ throw new errors_1.ValidationError(validated.reason ?? "Policy template manifest is invalid", {
241
+ code: validated.reasonCode,
242
+ });
243
+ }
244
+ return validated.manifest;
245
+ }
246
+ if (!input.templateId) {
247
+ throw new errors_1.ValidationError("templateId or manifestPath/manifestValue is required", {
248
+ code: "POLICY_TEMPLATE_MANIFEST_REQUIRED",
249
+ });
250
+ }
251
+ return describePolicyTemplate({
252
+ templateId: input.templateId,
253
+ propagationMode: input.propagationMode,
254
+ });
255
+ }
256
+ function resolveValueOrPath(options) {
257
+ if (options.pathValue)
258
+ return { jsonPath: options.pathValue };
259
+ if (options.objectValue !== undefined)
260
+ return { value: options.objectValue };
261
+ return {};
262
+ }
263
+ function validatePropagationMode(mode) {
264
+ if (mode !== "required" && mode !== "optional" && mode !== "none") {
265
+ throw new errors_1.ValidationError("propagationMode must be required, optional, or none", {
266
+ code: "POLICY_PROPAGATION_MODE_INVALID",
267
+ });
268
+ }
269
+ return mode;
270
+ }
271
+ function validateReceiver(receiver) {
272
+ if (receiver.mode === "plain") {
273
+ if (!receiver.address || receiver.address.trim().length === 0) {
274
+ throw new errors_1.ValidationError("plain receiver requires a non-empty address", {
275
+ code: "POLICY_RECEIVER_ADDRESS_REQUIRED",
276
+ });
277
+ }
278
+ return receiver;
279
+ }
280
+ if (!receiver.recipientXonly || receiver.recipientXonly.trim().length === 0) {
281
+ throw new errors_1.ValidationError("policy receiver requires recipientXonly", {
282
+ code: "POLICY_RECEIVER_XONLY_REQUIRED",
283
+ });
284
+ }
285
+ return receiver;
286
+ }
287
+ function validatePolicyTemplateDocument(value, templateId) {
288
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
289
+ return { policyTemplateId: templateId };
290
+ }
291
+ const record = value;
292
+ const policyTemplateId = typeof record.policyTemplateId === "string" && record.policyTemplateId.trim().length > 0
293
+ ? record.policyTemplateId
294
+ : templateId;
295
+ return {
296
+ policyTemplateId,
297
+ ...(typeof record.description === "string" ? { description: record.description } : {}),
298
+ ...(record.metadata && typeof record.metadata === "object" && !Array.isArray(record.metadata)
299
+ ? { metadata: record.metadata }
300
+ : {}),
301
+ };
302
+ }
303
+ function summarizePolicyTemplateDocument(doc) {
304
+ const canonicalJson = (0, summary_1.stableStringify)(doc);
305
+ return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
306
+ }
307
+ function normalizeParams(params) {
308
+ return Object.fromEntries(Object.entries(params ?? {}).sort(([left], [right]) => left.localeCompare(right)));
309
+ }
310
+ function validatePolicyTemplateParams(input) {
311
+ const manifest = input.manifestValue
312
+ ? input.manifestValue
313
+ : describePolicyTemplate({
314
+ templateId: input.templateId ?? "recursive-delay",
315
+ propagationMode: input.propagationMode,
316
+ });
317
+ const params = normalizeParams(input.params);
318
+ for (const [key, value] of Object.entries(params)) {
319
+ const expected = manifest.parameterSchema[key];
320
+ if (!expected) {
321
+ throw new errors_1.ValidationError(`Unknown policy parameter: ${key}`, {
322
+ code: "POLICY_PARAM_UNKNOWN",
323
+ });
324
+ }
325
+ if (expected === "number" && !(typeof value === "number" && Number.isFinite(value))) {
326
+ throw new errors_1.ValidationError(`Policy parameter ${key} must be a number`, {
327
+ code: "POLICY_PARAM_TYPE_INVALID",
328
+ });
329
+ }
330
+ if (expected === "string" && typeof value !== "string") {
331
+ throw new errors_1.ValidationError(`Policy parameter ${key} must be a string`, {
332
+ code: "POLICY_PARAM_TYPE_INVALID",
333
+ });
334
+ }
335
+ if (expected === "boolean" && typeof value !== "boolean") {
336
+ throw new errors_1.ValidationError(`Policy parameter ${key} must be a boolean`, {
337
+ code: "POLICY_PARAM_TYPE_INVALID",
338
+ });
339
+ }
340
+ }
341
+ return params;
342
+ }
343
+ function summarizePolicyHash(input) {
344
+ const canonicalJson = (0, summary_1.stableStringify)({
345
+ policyTemplateId: input.template.policyTemplateId,
346
+ recipient: input.recipient,
347
+ params: normalizeParams(input.params),
348
+ propagationMode: input.propagationMode,
349
+ });
350
+ return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
351
+ }
352
+ function summarizePolicyState(state) {
353
+ const canonicalJson = (0, summary_1.stableStringify)({
354
+ policyTemplateId: state.policyTemplateId,
355
+ policyHash: state.policyHash,
356
+ recipient: state.recipient,
357
+ amountSat: state.amountSat,
358
+ assetId: state.assetId,
359
+ params: normalizeParams(state.params),
360
+ propagationMode: state.propagationMode,
361
+ previousStateHash: state.previousStateHash ?? null,
362
+ hop: state.hop,
363
+ status: state.status,
364
+ });
365
+ return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
366
+ }
367
+ function validatePolicyState(state) {
368
+ if (!state.policyTemplateId || state.policyTemplateId.trim().length === 0) {
369
+ throw new errors_1.ValidationError("policyTemplateId must be a non-empty string", { code: "POLICY_TEMPLATE_ID_REQUIRED" });
370
+ }
371
+ if (!/^[0-9a-f]{64}$/i.test(state.policyHash)) {
372
+ throw new errors_1.ValidationError("policyHash must be a 64-character hex string", { code: "POLICY_HASH_INVALID" });
373
+ }
374
+ if (!state.recipient || state.recipient.trim().length === 0) {
375
+ throw new errors_1.ValidationError("recipient must be a non-empty string", { code: "POLICY_RECIPIENT_REQUIRED" });
376
+ }
377
+ if (!Number.isInteger(state.amountSat) || state.amountSat <= 0) {
378
+ throw new errors_1.ValidationError("amountSat must be a positive integer", { code: "POLICY_AMOUNT_INVALID" });
379
+ }
380
+ if (!state.assetId || state.assetId.trim().length === 0) {
381
+ throw new errors_1.ValidationError("assetId must be a non-empty string", { code: "POLICY_ASSET_REQUIRED" });
382
+ }
383
+ validatePropagationMode(state.propagationMode);
384
+ if (!Number.isInteger(state.hop) || state.hop < 0) {
385
+ throw new errors_1.ValidationError("hop must be a non-negative integer", { code: "POLICY_HOP_INVALID" });
386
+ }
387
+ if (state.status !== "LOCKED" && state.status !== "SPENT") {
388
+ throw new errors_1.ValidationError("status must be LOCKED or SPENT", { code: "POLICY_STATUS_INVALID" });
389
+ }
390
+ return state;
391
+ }
392
+ function summarizePolicyOutputDescriptor(descriptor) {
393
+ const canonicalJson = (0, summary_1.stableStringify)({
394
+ assetId: descriptor.assetId,
395
+ feeIndex: descriptor.feeIndex,
396
+ maxFeeSat: descriptor.maxFeeSat,
397
+ nextAmountSat: descriptor.nextAmountSat,
398
+ nextContractAddress: descriptor.nextContractAddress,
399
+ nextOutputHash: descriptor.nextOutputHash ?? null,
400
+ nextOutputScriptHash: descriptor.nextOutputScriptHash ?? null,
401
+ nextOutputIndex: descriptor.nextOutputIndex,
402
+ requestedOutputBindingMode: descriptor.requestedOutputBindingMode ?? descriptor.outputBindingMode,
403
+ outputForm: descriptor.outputForm ?? (0, outputBinding_1.normalizeOutputForm)(),
404
+ rawOutput: (0, outputBinding_1.normalizeOutputRawFields)(descriptor.rawOutput) ?? null,
405
+ outputBindingMode: descriptor.outputBindingMode,
406
+ });
407
+ return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
408
+ }
409
+ function validatePolicyOutputDescriptor(descriptor) {
410
+ if (!descriptor.nextContractAddress || descriptor.nextContractAddress.trim().length === 0) {
411
+ throw new errors_1.ValidationError("nextContractAddress must be a non-empty string", {
412
+ code: "POLICY_NEXT_CONTRACT_REQUIRED",
413
+ });
414
+ }
415
+ if (!Number.isInteger(descriptor.nextAmountSat) || descriptor.nextAmountSat <= 0) {
416
+ throw new errors_1.ValidationError("nextAmountSat must be a positive integer", { code: "POLICY_NEXT_AMOUNT_INVALID" });
417
+ }
418
+ if (!descriptor.assetId || descriptor.assetId.trim().length === 0) {
419
+ throw new errors_1.ValidationError("assetId must be a non-empty string", { code: "POLICY_OUTPUT_ASSET_REQUIRED" });
420
+ }
421
+ if (!Number.isInteger(descriptor.feeIndex) || descriptor.feeIndex < 0) {
422
+ throw new errors_1.ValidationError("feeIndex must be a non-negative integer", { code: "POLICY_FEE_INDEX_INVALID" });
423
+ }
424
+ if (!Number.isInteger(descriptor.nextOutputIndex) || descriptor.nextOutputIndex < 0) {
425
+ throw new errors_1.ValidationError("nextOutputIndex must be a non-negative integer", { code: "POLICY_NEXT_OUTPUT_INDEX_INVALID" });
426
+ }
427
+ if (!Number.isInteger(descriptor.maxFeeSat) || descriptor.maxFeeSat < 0) {
428
+ throw new errors_1.ValidationError("maxFeeSat must be a non-negative integer", { code: "POLICY_MAX_FEE_INVALID" });
429
+ }
430
+ if (!["none", "script-bound", "descriptor-bound"].includes(descriptor.outputBindingMode)) {
431
+ throw new errors_1.ValidationError("outputBindingMode must be none, script-bound, or descriptor-bound", {
432
+ code: "POLICY_OUTPUT_BINDING_MODE_INVALID",
433
+ });
434
+ }
435
+ if (descriptor.requestedOutputBindingMode
436
+ && !["none", "script-bound", "descriptor-bound"].includes(descriptor.requestedOutputBindingMode)) {
437
+ throw new errors_1.ValidationError("requestedOutputBindingMode must be none, script-bound, or descriptor-bound", {
438
+ code: "POLICY_REQUESTED_OUTPUT_BINDING_MODE_INVALID",
439
+ });
440
+ }
441
+ if (descriptor.nextOutputScriptHash && !/^[0-9a-f]{64}$/i.test(descriptor.nextOutputScriptHash)) {
442
+ throw new errors_1.ValidationError("nextOutputScriptHash must be a 64-character hex string", {
443
+ code: "POLICY_NEXT_OUTPUT_SCRIPT_HASH_INVALID",
444
+ });
445
+ }
446
+ if (descriptor.nextOutputHash && !/^[0-9a-f]{64}$/i.test(descriptor.nextOutputHash)) {
447
+ throw new errors_1.ValidationError("nextOutputHash must be a 64-character hex string", {
448
+ code: "POLICY_NEXT_OUTPUT_HASH_INVALID",
449
+ });
450
+ }
451
+ descriptor.rawOutput = (0, outputBinding_1.normalizeOutputRawFields)(descriptor.rawOutput);
452
+ const outputForm = (0, outputBinding_1.normalizeOutputForm)(descriptor.outputForm);
453
+ descriptor.outputForm = outputForm;
454
+ descriptor.requestedOutputBindingMode = descriptor.requestedOutputBindingMode ?? descriptor.outputBindingMode;
455
+ return descriptor;
456
+ }
457
+ function summarizePolicyTransferDescriptor(descriptor) {
458
+ const canonicalJson = (0, summary_1.stableStringify)({
459
+ policyTemplateId: descriptor.policyTemplateId,
460
+ previousPolicyHash: descriptor.previousPolicyHash,
461
+ nextPolicyHash: descriptor.nextPolicyHash ?? null,
462
+ previousStateHash: descriptor.previousStateHash,
463
+ nextStateHash: descriptor.nextStateHash ?? null,
464
+ propagationMode: descriptor.propagationMode,
465
+ plainExitAddress: descriptor.plainExitAddress ?? null,
466
+ outputDescriptor: descriptor.outputDescriptor
467
+ ? JSON.parse(summarizePolicyOutputDescriptor(descriptor.outputDescriptor).canonicalJson)
468
+ : null,
469
+ });
470
+ return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
471
+ }
472
+ function validatePolicyTransferDescriptor(descriptor) {
473
+ if (!descriptor.policyTemplateId || descriptor.policyTemplateId.trim().length === 0) {
474
+ throw new errors_1.ValidationError("policyTemplateId must be a non-empty string", {
475
+ code: "POLICY_TRANSFER_TEMPLATE_ID_REQUIRED",
476
+ });
477
+ }
478
+ if (!/^[0-9a-f]{64}$/i.test(descriptor.previousPolicyHash)) {
479
+ throw new errors_1.ValidationError("previousPolicyHash must be a 64-character hex string", {
480
+ code: "POLICY_TRANSFER_PREVIOUS_HASH_INVALID",
481
+ });
482
+ }
483
+ if (!/^[0-9a-f]{64}$/i.test(descriptor.previousStateHash)) {
484
+ throw new errors_1.ValidationError("previousStateHash must be a 64-character hex string", {
485
+ code: "POLICY_TRANSFER_PREVIOUS_STATE_HASH_INVALID",
486
+ });
487
+ }
488
+ if (descriptor.nextPolicyHash && !/^[0-9a-f]{64}$/i.test(descriptor.nextPolicyHash)) {
489
+ throw new errors_1.ValidationError("nextPolicyHash must be a 64-character hex string", {
490
+ code: "POLICY_TRANSFER_NEXT_HASH_INVALID",
491
+ });
492
+ }
493
+ if (descriptor.nextStateHash && !/^[0-9a-f]{64}$/i.test(descriptor.nextStateHash)) {
494
+ throw new errors_1.ValidationError("nextStateHash must be a 64-character hex string", {
495
+ code: "POLICY_TRANSFER_NEXT_STATE_HASH_INVALID",
496
+ });
497
+ }
498
+ validatePropagationMode(descriptor.propagationMode);
499
+ if (descriptor.outputDescriptor) {
500
+ validatePolicyOutputDescriptor(descriptor.outputDescriptor);
501
+ }
502
+ return descriptor;
503
+ }
504
+ function satToBtcAmount(sat) {
505
+ return Number((sat / 1e8).toFixed(8));
506
+ }
507
+ function resolvePolicySequence(state) {
508
+ const raw = state.params.lockDistanceBlocks;
509
+ if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
510
+ return raw;
511
+ }
512
+ return undefined;
513
+ }
514
+ function buildPolicyMachineWitness(prepared) {
515
+ return {
516
+ values: {
517
+ NEXT_POLICY_HASH: {
518
+ type: "u256",
519
+ value: `0x${prepared.transferDescriptor.nextPolicyHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
520
+ },
521
+ NEXT_OUTPUT_DESCRIPTOR_HASH: {
522
+ type: "u256",
523
+ value: `0x${prepared.transferSummary.hash}`,
524
+ },
525
+ EXPECTED_NEXT_OUTPUT_HASH: {
526
+ type: "u256",
527
+ value: `0x${prepared.transferDescriptor.outputDescriptor?.nextOutputHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
528
+ },
529
+ EXPECTED_NEXT_OUTPUT_SCRIPT_HASH: {
530
+ type: "u256",
531
+ value: `0x${prepared.transferDescriptor.outputDescriptor?.nextOutputScriptHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
532
+ },
533
+ OUTPUT_BINDING_MODE: {
534
+ type: "u8",
535
+ value: prepared.transferDescriptor.outputDescriptor?.outputBindingMode === "descriptor-bound"
536
+ ? "0x01"
537
+ : prepared.transferDescriptor.outputDescriptor?.outputBindingMode === "script-bound"
538
+ ? "0x02"
539
+ : "0x03",
540
+ },
541
+ },
542
+ };
543
+ }
544
+ function buildPolicyDirectWitness(prepared) {
545
+ return {
546
+ values: {
547
+ NEXT_POLICY_HASH: {
548
+ type: "u256",
549
+ value: `0x${prepared.transferDescriptor.nextPolicyHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
550
+ },
551
+ NEXT_OUTPUT_DESCRIPTOR_HASH: {
552
+ type: "u256",
553
+ value: `0x${prepared.transferSummary.hash}`,
554
+ },
555
+ EXPECTED_NEXT_OUTPUT_HASH: {
556
+ type: "u256",
557
+ value: `0x${prepared.transferDescriptor.outputDescriptor?.nextOutputHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
558
+ },
559
+ EXPECTED_NEXT_OUTPUT_SCRIPT_HASH: {
560
+ type: "u256",
561
+ value: `0x${prepared.transferDescriptor.outputDescriptor?.nextOutputScriptHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
562
+ },
563
+ OUTPUT_BINDING_MODE: {
564
+ type: "u8",
565
+ value: prepared.transferDescriptor.outputDescriptor?.outputBindingMode === "descriptor-bound"
566
+ ? "0x01"
567
+ : prepared.transferDescriptor.outputDescriptor?.outputBindingMode === "script-bound"
568
+ ? "0x02"
569
+ : "0x03",
570
+ },
571
+ },
572
+ };
573
+ }
574
+ function buildPolicyRecursiveWitness(prepared) {
575
+ return {
576
+ values: {
577
+ NEXT_POLICY_HASH: {
578
+ type: "u256",
579
+ value: `0x${prepared.transferDescriptor.nextPolicyHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
580
+ },
581
+ NEXT_OUTPUT_DESCRIPTOR_HASH: {
582
+ type: "u256",
583
+ value: `0x${prepared.transferSummary.hash}`,
584
+ },
585
+ EXPECTED_NEXT_OUTPUT_HASH: {
586
+ type: "u256",
587
+ value: `0x${prepared.transferDescriptor.outputDescriptor?.nextOutputHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
588
+ },
589
+ EXPECTED_NEXT_OUTPUT_SCRIPT_HASH: {
590
+ type: "u256",
591
+ value: `0x${prepared.transferDescriptor.outputDescriptor?.nextOutputScriptHash ?? "0000000000000000000000000000000000000000000000000000000000000000"}`,
592
+ },
593
+ OUTPUT_BINDING_MODE: {
594
+ type: "u8",
595
+ value: prepared.transferDescriptor.outputDescriptor?.outputBindingMode === "descriptor-bound"
596
+ ? "0x01"
597
+ : prepared.transferDescriptor.outputDescriptor?.outputBindingMode === "script-bound"
598
+ ? "0x02"
599
+ : "0x03",
600
+ },
601
+ },
602
+ };
603
+ }
604
+ function camelToUpperSnake(value) {
605
+ return value
606
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
607
+ .replace(/[^A-Za-z0-9]+/g, "_")
608
+ .replace(/^_+|_+$/g, "")
609
+ .toUpperCase();
610
+ }
611
+ function buildPolicyTemplateVars(input) {
612
+ const vars = {
613
+ SIGNER_XONLY: input.recipientXonly,
614
+ RECIPIENT_XONLY: input.recipientXonly,
615
+ POLICY_HASH: input.policyHash,
616
+ };
617
+ for (const [key, value] of Object.entries(input.params)) {
618
+ const templateKey = camelToUpperSnake(key);
619
+ vars[templateKey] = typeof value === "boolean" ? (value ? 1 : 0) : value;
620
+ if (key === "lockDistanceBlocks") {
621
+ vars.LOCK_DISTANCE = typeof value === "boolean" ? (value ? 1 : 0) : value;
622
+ }
623
+ }
624
+ if (input.propagationMode) {
625
+ vars.PROPAGATION_MODE_8 = input.propagationMode === "required" ? 1 : input.propagationMode === "optional" ? 2 : 3;
626
+ vars.ALLOW_PLAIN_EXIT_8 = input.propagationMode === "optional" || input.propagationMode === "none" ? 1 : 0;
627
+ vars.REQUIRE_RECURSIVE_NEXT_8 = input.propagationMode === "required" ? 1 : 0;
628
+ vars.REQUIRE_MACHINE_HOP_8 = input.propagationMode === "required" ? 1 : 0;
629
+ }
630
+ if (input.outputBindingMode) {
631
+ vars.OUTPUT_BINDING_MODE_8 =
632
+ input.outputBindingMode === "descriptor-bound"
633
+ ? 1
634
+ : input.outputBindingMode === "script-bound"
635
+ ? 2
636
+ : 3;
637
+ }
638
+ if (input.previousPolicyHash)
639
+ vars.PREVIOUS_POLICY_HASH = input.previousPolicyHash;
640
+ if (input.nextPolicyHash)
641
+ vars.NEXT_POLICY_HASH = input.nextPolicyHash;
642
+ if (input.nextOutputDescriptorHash)
643
+ vars.NEXT_OUTPUT_DESCRIPTOR_HASH = input.nextOutputDescriptorHash;
644
+ vars.EXPECTED_NEXT_OUTPUT_HASH_256 =
645
+ input.nextOutputHash ?? "0000000000000000000000000000000000000000000000000000000000000000";
646
+ vars.EXPECTED_NEXT_OUTPUT_SCRIPT_HASH_256 =
647
+ input.nextOutputScriptHash ?? "0000000000000000000000000000000000000000000000000000000000000000";
648
+ vars.ROUTER_MACHINE_SCRIPT_HASH_256 =
649
+ input.nextOutputScriptHash ?? "0000000000000000000000000000000000000000000000000000000000000000";
650
+ return vars;
651
+ }
652
+ async function resolveTemplateDocument(sdk, input, propagationMode) {
653
+ const manifest = await loadPolicyTemplateManifest({
654
+ templateId: input.templateId,
655
+ propagationMode,
656
+ manifestPath: input.manifestPath,
657
+ manifestValue: input.manifestValue,
658
+ });
659
+ const definitionSource = resolveValueOrPath({
660
+ pathValue: input.jsonPath,
661
+ objectValue: input.value,
662
+ });
663
+ const templateId = input.templateId ?? manifest.templateId;
664
+ const definition = await sdk.loadDefinition({
665
+ type: "policy-template",
666
+ id: templateId,
667
+ ...(definitionSource.jsonPath || definitionSource.value
668
+ ? definitionSource
669
+ : { value: { policyTemplateId: templateId } }),
670
+ });
671
+ const document = validatePolicyTemplateDocument(JSON.parse(definition.canonicalJson), templateId);
672
+ return {
673
+ document,
674
+ summary: summarizePolicyTemplateDocument(document),
675
+ manifest,
676
+ stateSimfPath: input.stateSimfPath ?? manifest.stateSimfPath,
677
+ directStateSimfPath: input.directStateSimfPath ?? manifest.directStateSimfPath ?? manifest.stateSimfPath,
678
+ transferMachineSimfPath: input.transferMachineSimfPath ?? resolvePolicyDocsAsset("recursive-policy-transfer-machine.simf"),
679
+ };
680
+ }
681
+ function createPolicyState(input) {
682
+ const policySummary = summarizePolicyHash({
683
+ template: input.template,
684
+ recipient: input.recipientXonly,
685
+ params: input.params,
686
+ propagationMode: input.propagationMode,
687
+ });
688
+ return validatePolicyState({
689
+ policyTemplateId: input.template.policyTemplateId,
690
+ policyHash: policySummary.hash,
691
+ recipient: input.recipientXonly,
692
+ amountSat: input.amountSat,
693
+ assetId: input.assetId,
694
+ params: normalizeParams(input.params),
695
+ propagationMode: input.propagationMode,
696
+ previousStateHash: input.previousStateHash ?? null,
697
+ hop: input.hop,
698
+ status: "LOCKED",
699
+ });
700
+ }
701
+ function resolvePolicyEnforcementMode(input) {
702
+ if (input.currentPropagationMode === "required") {
703
+ return "direct-hop";
704
+ }
705
+ if (input.currentPropagationMode === "optional" && input.nextReceiverMode === "policy") {
706
+ return "conditional-hop";
707
+ }
708
+ return "sdk-path";
709
+ }
710
+ function assertDirectArtifactCompatibility(artifact, template) {
711
+ const actual = artifact.source.simfPath ? node_path_1.default.basename(artifact.source.simfPath) : undefined;
712
+ const expected = node_path_1.default.basename(template.directStateSimfPath);
713
+ if (actual !== expected) {
714
+ throw new errors_1.ValidationError(`Current artifact is not using the direct-hop state contract (${expected})`, {
715
+ code: "POLICY_DIRECT_HOP_ARTIFACT_REQUIRED",
716
+ });
717
+ }
718
+ }
719
+ async function compilePolicyStateContractInternal(sdk, input) {
720
+ const template = await resolveTemplateDocument(sdk, input.template);
721
+ return sdk.compileFromFile({
722
+ simfPath: input.stateValue.propagationMode === "required"
723
+ ? template.directStateSimfPath
724
+ : template.stateSimfPath,
725
+ templateVars: buildPolicyTemplateVars({
726
+ recipientXonly: input.stateValue.recipient,
727
+ policyHash: input.stateValue.policyHash,
728
+ params: input.stateValue.params,
729
+ propagationMode: input.stateValue.propagationMode,
730
+ nextPolicyHash: input.nextPolicyHash,
731
+ nextOutputDescriptorHash: input.nextOutputDescriptorHash,
732
+ outputBindingMode: input.stateValue.propagationMode === "required" || input.stateValue.propagationMode === "optional"
733
+ ? "script-bound"
734
+ : undefined,
735
+ nextOutputScriptHash: input.stateValue.propagationMode === "required"
736
+ ? input.nextOutputScriptHash
737
+ : input.stateValue.propagationMode === "optional"
738
+ ? input.nextOutputScriptHash
739
+ : input.routerMachineScriptHash,
740
+ }),
741
+ definition: {
742
+ type: "policy-template",
743
+ id: template.document.policyTemplateId,
744
+ value: template.document,
745
+ anchorMode: "on-chain-constant-committed",
746
+ },
747
+ state: {
748
+ type: "policy-state",
749
+ id: `${input.stateValue.policyTemplateId}-${input.stateValue.hop}`,
750
+ value: input.stateValue,
751
+ anchorMode: "on-chain-constant-committed",
752
+ },
753
+ artifactPath: input.artifactPath,
754
+ });
755
+ }
756
+ async function compilePolicyRouterMachineInternal(sdk, input) {
757
+ const template = await resolveTemplateDocument(sdk, input.template);
758
+ return sdk.compileFromFile({
759
+ simfPath: template.transferMachineSimfPath,
760
+ templateVars: buildPolicyTemplateVars({
761
+ recipientXonly: input.currentState.recipient,
762
+ policyHash: input.currentState.policyHash,
763
+ params: input.currentState.params,
764
+ propagationMode: input.currentState.propagationMode,
765
+ outputBindingMode: "none",
766
+ previousPolicyHash: input.currentState.policyHash,
767
+ nextPolicyHash: input.currentState.policyHash,
768
+ nextOutputDescriptorHash: "0000000000000000000000000000000000000000000000000000000000000000",
769
+ }),
770
+ definition: {
771
+ type: "policy-template",
772
+ id: template.document.policyTemplateId,
773
+ value: template.document,
774
+ anchorMode: "on-chain-constant-committed",
775
+ },
776
+ state: {
777
+ type: "policy-state",
778
+ id: `${input.currentState.policyTemplateId}-${input.currentState.hop}`,
779
+ value: input.currentState,
780
+ anchorMode: "on-chain-constant-committed",
781
+ },
782
+ artifactPath: input.artifactPath,
783
+ });
784
+ }
785
+ async function getPolicyRouterMachineScriptHash(sdk, input) {
786
+ const router = await compilePolicyRouterMachineInternal(sdk, {
787
+ template: input.template,
788
+ currentState: input.currentState,
789
+ });
790
+ const scriptPubKeyHex = await (0, outputBinding_1.getScriptPubKeyHexViaRpc)(sdk, router.contractAddress);
791
+ return (0, outputBinding_1.hashHexBytes)(scriptPubKeyHex);
792
+ }
793
+ async function compilePolicyStateContract(sdk, input) {
794
+ const routerMachineScriptHash = input.stateValue.propagationMode !== "none"
795
+ ? await getPolicyRouterMachineScriptHash(sdk, {
796
+ template: input.template,
797
+ currentState: input.stateValue,
798
+ })
799
+ : undefined;
800
+ return compilePolicyStateContractInternal(sdk, {
801
+ ...input,
802
+ routerMachineScriptHash,
803
+ });
804
+ }
805
+ async function issue(sdk, input) {
806
+ const recipient = validateReceiver(input.recipient);
807
+ if (recipient.mode !== "policy") {
808
+ throw new errors_1.ValidationError("issue requires a policy receiver", { code: "POLICY_ISSUE_RECEIVER_MODE_INVALID" });
809
+ }
810
+ const propagationMode = validatePropagationMode(input.propagationMode ?? "required");
811
+ const template = await resolveTemplateDocument(sdk, input.template, propagationMode);
812
+ const params = validatePolicyTemplateParams({
813
+ templateId: template.manifest.templateId,
814
+ params: { ...(recipient.defaultParams ?? {}), ...(input.params ?? {}) },
815
+ propagationMode,
816
+ });
817
+ const stateValue = createPolicyState({
818
+ template: template.document,
819
+ recipientXonly: recipient.recipientXonly,
820
+ amountSat: input.amountSat,
821
+ assetId: input.assetId,
822
+ params,
823
+ propagationMode,
824
+ hop: 0,
825
+ });
826
+ const routerMachineScriptHash = propagationMode !== "none"
827
+ ? await getPolicyRouterMachineScriptHash(sdk, {
828
+ template: input.template,
829
+ currentState: stateValue,
830
+ })
831
+ : undefined;
832
+ const compiled = await compilePolicyStateContractInternal(sdk, {
833
+ template: input.template,
834
+ stateValue,
835
+ routerMachineScriptHash,
836
+ artifactPath: input.artifactPath,
837
+ });
838
+ return {
839
+ compiled,
840
+ state: stateValue,
841
+ policyTemplate: template.document,
842
+ policyHash: stateValue.policyHash,
843
+ };
844
+ }
845
+ async function verifyState(sdk, input) {
846
+ const artifact = input.artifact ?? (input.artifactPath ? (await sdk.loadArtifact(input.artifactPath)).artifact : undefined);
847
+ if (!artifact) {
848
+ throw new errors_1.ValidationError("artifactPath or artifact is required", { code: "POLICY_ARTIFACT_REQUIRED" });
849
+ }
850
+ const templateInput = input.template ?? { templateId: artifact.definition?.definitionId ?? "recursive-delay" };
851
+ const template = await resolveTemplateDocument(sdk, templateInput);
852
+ const definitionVerification = await sdk.verifyDefinitionAgainstArtifact({
853
+ artifact,
854
+ type: "policy-template",
855
+ id: template.document.policyTemplateId,
856
+ value: template.document,
857
+ });
858
+ const stateVerification = await sdk.verifyStateAgainstArtifact({
859
+ artifact,
860
+ type: "policy-state",
861
+ id: artifact.state?.stateId,
862
+ ...(input.statePath ? { jsonPath: input.statePath } : input.stateValue ? { value: input.stateValue } : {}),
863
+ });
864
+ const state = validatePolicyState(JSON.parse(stateVerification.state.canonicalJson));
865
+ const expectedPolicyHash = summarizePolicyHash({
866
+ template: template.document,
867
+ recipient: state.recipient,
868
+ params: state.params,
869
+ propagationMode: state.propagationMode,
870
+ }).hash;
871
+ const policyHashMatches = expectedPolicyHash === state.policyHash;
872
+ return {
873
+ ok: definitionVerification.ok && stateVerification.ok && policyHashMatches,
874
+ reason: policyHashMatches ? undefined : "Policy hash does not match template + params + recipient",
875
+ artifact,
876
+ template: definitionVerification,
877
+ state: stateVerification,
878
+ stateValue: state,
879
+ policyHashMatches,
880
+ report: {
881
+ schemaVersion: exports.POLICY_VERIFICATION_REPORT_SCHEMA_VERSION,
882
+ templateTrust: definitionVerification.trust,
883
+ stateTrust: stateVerification.trust,
884
+ propagationMode: state.propagationMode,
885
+ nextPolicyRequired: state.propagationMode === "required",
886
+ nextPolicyPresent: false,
887
+ plainExitAllowed: state.propagationMode !== "required",
888
+ enforcement: resolvePolicyEnforcementMode({
889
+ currentPropagationMode: state.propagationMode,
890
+ nextReceiverMode: state.propagationMode === "required" ? "policy" : undefined,
891
+ }),
892
+ },
893
+ };
894
+ }
895
+ async function buildPolicyOutputDescriptor(sdk, input) {
896
+ const nextScriptPubKeyHex = await (0, outputBinding_1.getScriptPubKeyHexViaRpc)(sdk, input.nextCompiledContractAddress);
897
+ const nextOutputScriptHash = (0, outputBinding_1.hashHexBytes)(nextScriptPubKeyHex);
898
+ const requestedBindingMode = input.outputBindingMode ?? "script-bound";
899
+ const outputForm = (0, outputBinding_1.normalizeOutputForm)(input.outputForm);
900
+ const rawOutput = (0, outputBinding_1.normalizeOutputRawFields)(input.rawOutput);
901
+ const rawOutputAnalysis = (0, outputBinding_1.analyzeOutputRawFields)(rawOutput);
902
+ const explicitAssetHex = requestedBindingMode !== "none" && (0, outputBinding_1.isExplicitV1OutputForm)(outputForm)
903
+ ? await (0, outputBinding_1.resolveExplicitAssetHex)(sdk, input.assetId)
904
+ : undefined;
905
+ const autoDerivedNextOutputHash = requestedBindingMode === "descriptor-bound" && !input.nextOutputHash
906
+ ? rawOutputAnalysis.valid && rawOutputAnalysis.normalized
907
+ ? (0, outputBinding_1.computeRawOutputV1Hash)(rawOutputAnalysis.normalized)
908
+ : explicitAssetHex
909
+ ? (0, outputBinding_1.computeExplicitV1OutputHash)({
910
+ assetHex: explicitAssetHex,
911
+ nextAmountSat: input.nextAmountSat,
912
+ nextOutputScriptHash,
913
+ })
914
+ : undefined
915
+ : undefined;
916
+ const nextOutputHash = input.nextOutputHash ?? autoDerivedNextOutputHash;
917
+ const bindingResolution = (0, outputBinding_1.resolveOutputBindingDecision)({
918
+ requestedBindingMode,
919
+ nextOutputHash,
920
+ nextOutputScriptHash,
921
+ autoDerivedNextOutputHash: Boolean(autoDerivedNextOutputHash && !input.nextOutputHash),
922
+ explicitAssetSupported: Boolean(explicitAssetHex),
923
+ outputForm,
924
+ rawOutput,
925
+ });
926
+ const descriptor = validatePolicyOutputDescriptor({
927
+ nextContractAddress: input.nextCompiledContractAddress,
928
+ nextOutputHash: bindingResolution.outputBindingMode === "descriptor-bound" ? nextOutputHash : undefined,
929
+ nextOutputScriptHash: bindingResolution.outputBindingMode !== "none" ? nextOutputScriptHash : undefined,
930
+ nextAmountSat: input.nextAmountSat,
931
+ assetId: input.assetId,
932
+ requestedOutputBindingMode: requestedBindingMode,
933
+ outputForm,
934
+ rawOutput,
935
+ feeIndex: input.feeIndex ?? 1,
936
+ nextOutputIndex: input.nextOutputIndex ?? 0,
937
+ maxFeeSat: input.maxFeeSat ?? 100,
938
+ outputBindingMode: bindingResolution.outputBindingMode,
939
+ });
940
+ return {
941
+ descriptor,
942
+ summary: summarizePolicyOutputDescriptor(descriptor),
943
+ supportedForm: bindingResolution.supportedForm,
944
+ autoDerivedNextOutputHash: bindingResolution.autoDerived,
945
+ reasonCode: bindingResolution.reasonCode,
946
+ fallbackReason: bindingResolution.fallbackReason,
947
+ bindingInputs: {
948
+ assetId: descriptor.assetId,
949
+ assetForm: descriptor.outputForm?.assetForm ?? "explicit",
950
+ amountForm: descriptor.outputForm?.amountForm ?? "explicit",
951
+ nonceForm: descriptor.outputForm?.nonceForm ?? "null",
952
+ rangeProofForm: descriptor.outputForm?.rangeProofForm ?? "empty",
953
+ nextAmountSat: descriptor.nextAmountSat,
954
+ nextOutputIndex: descriptor.nextOutputIndex,
955
+ feeIndex: descriptor.feeIndex,
956
+ maxFeeSat: descriptor.maxFeeSat,
957
+ ...(rawOutputAnalysis.valid && rawOutputAnalysis.normalized
958
+ ? {
959
+ rawOutputComponents: {
960
+ scriptPubKey: rawOutputAnalysis.normalized.scriptComponentSource,
961
+ rangeProof: rawOutputAnalysis.normalized.rangeProofComponentSource,
962
+ },
963
+ }
964
+ : {}),
965
+ },
966
+ };
967
+ }
968
+ function buildPolicyOutputBindingReport(input) {
969
+ if (!input.descriptor)
970
+ return undefined;
971
+ return {
972
+ mode: input.descriptor.outputBindingMode,
973
+ supportedForm: input.supportedForm ?? "unsupported",
974
+ committed: true,
975
+ runtimeBound: input.descriptor.outputBindingMode !== "none",
976
+ sdkVerified: input.sdkVerified,
977
+ amountRuntimeBound: input.descriptor.outputBindingMode === "descriptor-bound",
978
+ nextOutputHashRuntimeBound: input.descriptor.outputBindingMode === "descriptor-bound",
979
+ nextOutputScriptRuntimeBound: input.descriptor.outputBindingMode === "script-bound",
980
+ reasonCode: input.reasonCode
981
+ ?? (input.descriptor.outputBindingMode === "descriptor-bound" ? "OK_MANUAL_HASH" : input.descriptor.outputBindingMode === "script-bound" ? "OK_SCRIPT_BOUND" : "OK_NONE"),
982
+ nextOutputHash: input.descriptor.nextOutputHash,
983
+ autoDerived: input.autoDerived,
984
+ fallbackReason: input.fallbackReason,
985
+ bindingInputs: input.bindingInputs
986
+ ?? {
987
+ assetId: input.descriptor.assetId,
988
+ assetForm: input.descriptor.outputForm?.assetForm ?? "explicit",
989
+ amountForm: input.descriptor.outputForm?.amountForm ?? "explicit",
990
+ nonceForm: input.descriptor.outputForm?.nonceForm ?? "null",
991
+ rangeProofForm: input.descriptor.outputForm?.rangeProofForm ?? "empty",
992
+ nextAmountSat: input.descriptor.nextAmountSat,
993
+ nextOutputIndex: input.descriptor.nextOutputIndex,
994
+ feeIndex: input.descriptor.feeIndex,
995
+ maxFeeSat: input.descriptor.maxFeeSat,
996
+ },
997
+ };
998
+ }
999
+ async function resolvePolicyOutputBindingVerificationContext(sdk, descriptor) {
1000
+ if (!descriptor)
1001
+ return {};
1002
+ const rawOutput = (0, outputBinding_1.normalizeOutputRawFields)(descriptor.rawOutput);
1003
+ const rawOutputAnalysis = (0, outputBinding_1.analyzeOutputRawFields)(rawOutput);
1004
+ const bindingInputs = {
1005
+ assetId: descriptor.assetId,
1006
+ assetForm: descriptor.outputForm?.assetForm ?? "explicit",
1007
+ amountForm: descriptor.outputForm?.amountForm ?? "explicit",
1008
+ nonceForm: descriptor.outputForm?.nonceForm ?? "null",
1009
+ rangeProofForm: descriptor.outputForm?.rangeProofForm ?? "empty",
1010
+ nextAmountSat: descriptor.nextAmountSat,
1011
+ nextOutputIndex: descriptor.nextOutputIndex,
1012
+ feeIndex: descriptor.feeIndex,
1013
+ maxFeeSat: descriptor.maxFeeSat,
1014
+ ...(rawOutputAnalysis.valid && rawOutputAnalysis.normalized
1015
+ ? {
1016
+ rawOutputComponents: {
1017
+ scriptPubKey: rawOutputAnalysis.normalized.scriptComponentSource,
1018
+ rangeProof: rawOutputAnalysis.normalized.rangeProofComponentSource,
1019
+ },
1020
+ }
1021
+ : {}),
1022
+ };
1023
+ const requestedBindingMode = descriptor.requestedOutputBindingMode ?? descriptor.outputBindingMode;
1024
+ const outputForm = (0, outputBinding_1.normalizeOutputForm)(descriptor.outputForm);
1025
+ const outputFormSupported = (0, outputBinding_1.isExplicitV1OutputForm)(outputForm);
1026
+ const explicitAssetHex = !rawOutputAnalysis.valid && outputFormSupported
1027
+ ? await (0, outputBinding_1.resolveExplicitAssetHex)(sdk, descriptor.assetId)
1028
+ : undefined;
1029
+ const decision = (0, outputBinding_1.resolveOutputBindingDecision)({
1030
+ requestedBindingMode,
1031
+ nextOutputHash: descriptor.nextOutputHash,
1032
+ nextOutputScriptHash: descriptor.nextOutputScriptHash,
1033
+ autoDerivedNextOutputHash: false,
1034
+ explicitAssetSupported: Boolean(explicitAssetHex),
1035
+ outputForm,
1036
+ rawOutput,
1037
+ });
1038
+ if (descriptor.outputBindingMode === "none" || requestedBindingMode === "none") {
1039
+ return {
1040
+ supportedForm: decision.supportedForm,
1041
+ reasonCode: "OK_NONE",
1042
+ bindingInputs,
1043
+ };
1044
+ }
1045
+ if (descriptor.outputBindingMode === "script-bound") {
1046
+ return {
1047
+ supportedForm: decision.supportedForm,
1048
+ reasonCode: requestedBindingMode === "descriptor-bound" ? decision.reasonCode : "OK_SCRIPT_BOUND",
1049
+ fallbackReason: requestedBindingMode === "descriptor-bound" ? decision.fallbackReason : undefined,
1050
+ bindingInputs,
1051
+ };
1052
+ }
1053
+ if (!descriptor.nextOutputHash) {
1054
+ return {
1055
+ supportedForm: decision.supportedForm,
1056
+ reasonCode: decision.reasonCode,
1057
+ fallbackReason: decision.fallbackReason ?? "descriptor-bound artifact is missing nextOutputHash",
1058
+ bindingInputs,
1059
+ };
1060
+ }
1061
+ if (rawOutputAnalysis.valid && rawOutputAnalysis.normalized) {
1062
+ const derivedNextOutputHash = (0, outputBinding_1.computeRawOutputV1Hash)(rawOutputAnalysis.normalized);
1063
+ const autoDerived = derivedNextOutputHash === descriptor.nextOutputHash;
1064
+ return {
1065
+ supportedForm: "raw-output-v1",
1066
+ reasonCode: autoDerived ? "OK_RAW_OUTPUT" : "OK_MANUAL_HASH",
1067
+ autoDerived,
1068
+ bindingInputs,
1069
+ };
1070
+ }
1071
+ if (!outputFormSupported) {
1072
+ return {
1073
+ supportedForm: decision.supportedForm,
1074
+ reasonCode: "OK_MANUAL_HASH",
1075
+ bindingInputs,
1076
+ };
1077
+ }
1078
+ if (!explicitAssetHex || !descriptor.nextOutputScriptHash) {
1079
+ return {
1080
+ supportedForm: explicitAssetHex ? "explicit-v1" : "unsupported",
1081
+ reasonCode: !explicitAssetHex ? "OK_MANUAL_HASH" : "FALLBACK_UNSUPPORTED_OUTPUT_FORM",
1082
+ ...(explicitAssetHex
1083
+ ? {
1084
+ fallbackReason: "descriptor-bound output hash could not be auto-verified because the descriptor is missing the explicit-v1 output shape inputs",
1085
+ }
1086
+ : {}),
1087
+ bindingInputs,
1088
+ };
1089
+ }
1090
+ const derivedNextOutputHash = (0, outputBinding_1.computeExplicitV1OutputHash)({
1091
+ assetHex: explicitAssetHex,
1092
+ nextAmountSat: descriptor.nextAmountSat,
1093
+ nextOutputScriptHash: descriptor.nextOutputScriptHash,
1094
+ });
1095
+ const autoDerived = derivedNextOutputHash === descriptor.nextOutputHash;
1096
+ return {
1097
+ supportedForm: "explicit-v1",
1098
+ reasonCode: autoDerived ? "OK_EXPLICIT" : "OK_MANUAL_HASH",
1099
+ autoDerived,
1100
+ bindingInputs,
1101
+ };
1102
+ }
1103
+ async function prepareTransfer(sdk, input) {
1104
+ const verifiedCurrent = await verifyState(sdk, {
1105
+ artifactPath: input.currentArtifactPath,
1106
+ artifact: input.currentArtifact,
1107
+ template: input.template,
1108
+ statePath: input.currentStatePath,
1109
+ stateValue: input.currentStateValue,
1110
+ });
1111
+ if (!verifiedCurrent.ok) {
1112
+ throw new errors_1.ValidationError(verifiedCurrent.reason ?? "Current policy state verification failed", {
1113
+ code: "POLICY_CURRENT_STATE_INVALID",
1114
+ });
1115
+ }
1116
+ const currentState = verifiedCurrent.stateValue;
1117
+ const nextPropagationMode = validatePropagationMode(input.propagationMode ?? currentState.propagationMode);
1118
+ const nextReceiver = validateReceiver(input.nextReceiver);
1119
+ if (currentState.propagationMode === "required" && nextReceiver.mode === "plain") {
1120
+ throw new errors_1.ValidationError("required propagation mode does not allow a plain-address exit", {
1121
+ code: "POLICY_REQUIRED_PROPAGATION_PLAIN_EXIT_FORBIDDEN",
1122
+ });
1123
+ }
1124
+ if (nextPropagationMode === "none" && nextReceiver.mode === "policy") {
1125
+ throw new errors_1.ValidationError("propagationMode none does not create a next policy state", {
1126
+ code: "POLICY_NONE_MODE_POLICY_RECEIVER_FORBIDDEN",
1127
+ });
1128
+ }
1129
+ if (currentState.propagationMode === "required") {
1130
+ return prepareDirectTransfer(sdk, input);
1131
+ }
1132
+ if (nextReceiver.mode === "plain") {
1133
+ const descriptor = validatePolicyTransferDescriptor({
1134
+ policyTemplateId: currentState.policyTemplateId,
1135
+ previousPolicyHash: currentState.policyHash,
1136
+ previousStateHash: summarizePolicyState(currentState).hash,
1137
+ propagationMode: currentState.propagationMode,
1138
+ plainExitAddress: nextReceiver.address,
1139
+ });
1140
+ const summary = summarizePolicyTransferDescriptor(descriptor);
1141
+ return {
1142
+ current: verifiedCurrent,
1143
+ nextState: null,
1144
+ nextCompiled: null,
1145
+ machineArtifact: null,
1146
+ transferDescriptor: descriptor,
1147
+ transferSummary: summary,
1148
+ verificationReport: {
1149
+ ...verifiedCurrent.report,
1150
+ nextPolicyPresent: false,
1151
+ plainExitAllowed: true,
1152
+ enforcement: resolvePolicyEnforcementMode({
1153
+ currentPropagationMode: currentState.propagationMode,
1154
+ nextReceiverMode: "plain",
1155
+ }),
1156
+ },
1157
+ };
1158
+ }
1159
+ const template = await resolveTemplateDocument(sdk, input.template, nextPropagationMode);
1160
+ const nextParams = validatePolicyTemplateParams({
1161
+ templateId: template.manifest.templateId,
1162
+ params: { ...(nextReceiver.defaultParams ?? {}), ...(input.nextParams ?? currentState.params) },
1163
+ propagationMode: nextPropagationMode,
1164
+ });
1165
+ const nextState = createPolicyState({
1166
+ template: template.document,
1167
+ recipientXonly: nextReceiver.recipientXonly,
1168
+ amountSat: input.nextAmountSat,
1169
+ assetId: currentState.assetId,
1170
+ params: nextParams,
1171
+ propagationMode: nextPropagationMode,
1172
+ previousStateHash: summarizePolicyState(currentState).hash,
1173
+ hop: currentState.hop + 1,
1174
+ });
1175
+ const nextCompiled = await compilePolicyStateContractInternal(sdk, {
1176
+ template: input.template,
1177
+ stateValue: nextState,
1178
+ });
1179
+ const outputDescriptor = await buildPolicyOutputDescriptor(sdk, {
1180
+ nextCompiledContractAddress: nextCompiled.contractAddress,
1181
+ nextAmountSat: input.nextAmountSat,
1182
+ assetId: nextState.assetId,
1183
+ maxFeeSat: 100,
1184
+ nextOutputHash: input.nextOutputHash,
1185
+ outputForm: input.nextOutputForm,
1186
+ rawOutput: input.nextRawOutput,
1187
+ outputBindingMode: input.outputBindingMode ?? "script-bound",
1188
+ });
1189
+ const previousStateSummary = summarizePolicyState(currentState);
1190
+ const nextStateSummary = summarizePolicyState(nextState);
1191
+ const transferDescriptor = validatePolicyTransferDescriptor({
1192
+ policyTemplateId: currentState.policyTemplateId,
1193
+ previousPolicyHash: currentState.policyHash,
1194
+ nextPolicyHash: nextState.policyHash,
1195
+ previousStateHash: previousStateSummary.hash,
1196
+ nextStateHash: nextStateSummary.hash,
1197
+ propagationMode: currentState.propagationMode,
1198
+ outputDescriptor: outputDescriptor.descriptor,
1199
+ });
1200
+ const transferSummary = summarizePolicyTransferDescriptor(transferDescriptor);
1201
+ if (input.nextArtifactPath) {
1202
+ await nextCompiled.saveArtifact(input.nextArtifactPath);
1203
+ }
1204
+ return {
1205
+ current: verifiedCurrent,
1206
+ nextState,
1207
+ nextCompiled,
1208
+ machineArtifact: null,
1209
+ transferDescriptor,
1210
+ transferSummary,
1211
+ verificationReport: {
1212
+ ...verifiedCurrent.report,
1213
+ nextPolicyPresent: true,
1214
+ outputBinding: buildPolicyOutputBindingReport({
1215
+ descriptor: outputDescriptor.descriptor,
1216
+ sdkVerified: true,
1217
+ supportedForm: outputDescriptor.supportedForm,
1218
+ autoDerived: outputDescriptor.autoDerivedNextOutputHash,
1219
+ reasonCode: outputDescriptor.reasonCode,
1220
+ fallbackReason: outputDescriptor.fallbackReason,
1221
+ bindingInputs: outputDescriptor.bindingInputs,
1222
+ }),
1223
+ enforcement: resolvePolicyEnforcementMode({
1224
+ currentPropagationMode: currentState.propagationMode,
1225
+ nextReceiverMode: "policy",
1226
+ }),
1227
+ },
1228
+ };
1229
+ }
1230
+ async function prepareDirectTransfer(sdk, input) {
1231
+ const verifiedCurrent = await verifyState(sdk, {
1232
+ artifactPath: input.currentArtifactPath,
1233
+ artifact: input.currentArtifact,
1234
+ template: input.template,
1235
+ statePath: input.currentStatePath,
1236
+ stateValue: input.currentStateValue,
1237
+ });
1238
+ if (!verifiedCurrent.ok) {
1239
+ throw new errors_1.ValidationError(verifiedCurrent.reason ?? "Current policy state verification failed", {
1240
+ code: "POLICY_CURRENT_STATE_INVALID",
1241
+ });
1242
+ }
1243
+ const currentState = verifiedCurrent.stateValue;
1244
+ if (currentState.propagationMode !== "required") {
1245
+ throw new errors_1.ValidationError("Direct transfer is only available for required propagation mode", {
1246
+ code: "POLICY_DIRECT_HOP_REQUIRES_REQUIRED_MODE",
1247
+ });
1248
+ }
1249
+ const template = await resolveTemplateDocument(sdk, input.template, currentState.propagationMode);
1250
+ assertDirectArtifactCompatibility(verifiedCurrent.artifact, template);
1251
+ const nextReceiver = validateReceiver(input.nextReceiver);
1252
+ if (nextReceiver.mode !== "policy") {
1253
+ throw new errors_1.ValidationError("Direct transfer requires a policy receiver", {
1254
+ code: "POLICY_DIRECT_HOP_POLICY_RECEIVER_REQUIRED",
1255
+ });
1256
+ }
1257
+ const nextPropagationMode = validatePropagationMode(input.propagationMode ?? currentState.propagationMode);
1258
+ if (nextPropagationMode !== "required") {
1259
+ throw new errors_1.ValidationError("Direct transfer currently requires the next hop to remain in required mode", {
1260
+ code: "POLICY_DIRECT_HOP_NEXT_MODE_INVALID",
1261
+ });
1262
+ }
1263
+ const nextParams = validatePolicyTemplateParams({
1264
+ templateId: template.manifest.templateId,
1265
+ params: { ...(nextReceiver.defaultParams ?? {}), ...(input.nextParams ?? currentState.params) },
1266
+ propagationMode: nextPropagationMode,
1267
+ });
1268
+ const nextState = createPolicyState({
1269
+ template: template.document,
1270
+ recipientXonly: nextReceiver.recipientXonly,
1271
+ amountSat: input.nextAmountSat,
1272
+ assetId: currentState.assetId,
1273
+ params: nextParams,
1274
+ propagationMode: nextPropagationMode,
1275
+ previousStateHash: summarizePolicyState(currentState).hash,
1276
+ hop: currentState.hop + 1,
1277
+ });
1278
+ const nextCompiled = await compilePolicyStateContractInternal(sdk, {
1279
+ template: input.template,
1280
+ stateValue: nextState,
1281
+ });
1282
+ const outputDescriptor = await buildPolicyOutputDescriptor(sdk, {
1283
+ nextCompiledContractAddress: nextCompiled.contractAddress,
1284
+ nextAmountSat: input.nextAmountSat,
1285
+ assetId: nextState.assetId,
1286
+ maxFeeSat: 100,
1287
+ nextOutputHash: input.nextOutputHash,
1288
+ outputForm: input.nextOutputForm,
1289
+ rawOutput: input.nextRawOutput,
1290
+ outputBindingMode: input.outputBindingMode ?? "script-bound",
1291
+ });
1292
+ const previousStateSummary = summarizePolicyState(currentState);
1293
+ const nextStateSummary = summarizePolicyState(nextState);
1294
+ const transferDescriptor = validatePolicyTransferDescriptor({
1295
+ policyTemplateId: currentState.policyTemplateId,
1296
+ previousPolicyHash: currentState.policyHash,
1297
+ nextPolicyHash: nextState.policyHash,
1298
+ previousStateHash: previousStateSummary.hash,
1299
+ nextStateHash: nextStateSummary.hash,
1300
+ propagationMode: currentState.propagationMode,
1301
+ outputDescriptor: outputDescriptor.descriptor,
1302
+ });
1303
+ const transferSummary = summarizePolicyTransferDescriptor(transferDescriptor);
1304
+ if (input.nextArtifactPath) {
1305
+ await nextCompiled.saveArtifact(input.nextArtifactPath);
1306
+ }
1307
+ return {
1308
+ current: verifiedCurrent,
1309
+ nextState,
1310
+ nextCompiled,
1311
+ machineArtifact: null,
1312
+ transferDescriptor,
1313
+ transferSummary,
1314
+ verificationReport: {
1315
+ ...verifiedCurrent.report,
1316
+ nextPolicyPresent: true,
1317
+ plainExitAllowed: false,
1318
+ outputBinding: buildPolicyOutputBindingReport({
1319
+ descriptor: outputDescriptor.descriptor,
1320
+ sdkVerified: true,
1321
+ supportedForm: outputDescriptor.supportedForm,
1322
+ autoDerived: outputDescriptor.autoDerivedNextOutputHash,
1323
+ reasonCode: outputDescriptor.reasonCode,
1324
+ fallbackReason: outputDescriptor.fallbackReason,
1325
+ bindingInputs: outputDescriptor.bindingInputs,
1326
+ }),
1327
+ enforcement: resolvePolicyEnforcementMode({
1328
+ currentPropagationMode: currentState.propagationMode,
1329
+ nextReceiverMode: "policy",
1330
+ }),
1331
+ },
1332
+ };
1333
+ }
1334
+ async function verifyDirectTransfer(sdk, input) {
1335
+ const prepared = await verifyState(sdk, {
1336
+ artifactPath: input.currentArtifactPath,
1337
+ artifact: input.currentArtifact,
1338
+ template: input.template,
1339
+ statePath: input.currentStatePath,
1340
+ stateValue: input.currentStateValue,
1341
+ });
1342
+ const template = await resolveTemplateDocument(sdk, input.template);
1343
+ assertDirectArtifactCompatibility(prepared.artifact, template);
1344
+ const currentState = prepared.stateValue;
1345
+ const transfer = input.transferDescriptorValue ? validatePolicyTransferDescriptor(input.transferDescriptorValue) : undefined;
1346
+ const nextState = input.nextStateValue ? validatePolicyState(input.nextStateValue) : undefined;
1347
+ const outputBindingContext = await resolvePolicyOutputBindingVerificationContext(sdk, transfer?.outputDescriptor);
1348
+ const summary = transfer ? summarizePolicyTransferDescriptor(transfer) : undefined;
1349
+ const nextStateSummary = nextState ? summarizePolicyState(nextState) : undefined;
1350
+ const ok = currentState.propagationMode === "required" && (!transfer || (transfer.previousPolicyHash === currentState.policyHash
1351
+ && transfer.previousStateHash === summarizePolicyState(currentState).hash
1352
+ && Boolean(transfer.outputDescriptor?.nextOutputScriptHash)
1353
+ && (!nextState || transfer.nextPolicyHash === nextState.policyHash)
1354
+ && (!nextStateSummary || transfer.nextStateHash === nextStateSummary.hash)));
1355
+ return {
1356
+ ok,
1357
+ reason: ok ? undefined : "Direct transfer descriptor does not match current/next policy state",
1358
+ current: prepared,
1359
+ transfer,
1360
+ transferSummary: summary,
1361
+ nextState,
1362
+ verificationReport: {
1363
+ ...prepared.report,
1364
+ nextPolicyPresent: Boolean(nextState),
1365
+ plainExitAllowed: false,
1366
+ outputBinding: buildPolicyOutputBindingReport({
1367
+ descriptor: transfer?.outputDescriptor,
1368
+ sdkVerified: ok,
1369
+ supportedForm: outputBindingContext.supportedForm,
1370
+ reasonCode: outputBindingContext.reasonCode,
1371
+ autoDerived: outputBindingContext.autoDerived,
1372
+ fallbackReason: outputBindingContext.fallbackReason,
1373
+ bindingInputs: outputBindingContext.bindingInputs,
1374
+ }),
1375
+ enforcement: resolvePolicyEnforcementMode({
1376
+ currentPropagationMode: currentState.propagationMode,
1377
+ nextReceiverMode: "policy",
1378
+ }),
1379
+ },
1380
+ };
1381
+ }
1382
+ async function inspectDirectTransfer(sdk, input) {
1383
+ const prepared = await prepareDirectTransfer(sdk, input);
1384
+ const currentArtifact = input.currentArtifact
1385
+ ?? (input.currentArtifactPath ? (await sdk.loadArtifact(input.currentArtifactPath)).artifact : undefined);
1386
+ if (!currentArtifact) {
1387
+ throw new errors_1.ValidationError("currentArtifactPath or currentArtifact is required", {
1388
+ code: "POLICY_CURRENT_ARTIFACT_REQUIRED",
1389
+ });
1390
+ }
1391
+ const currentContract = sdk.fromArtifact(currentArtifact);
1392
+ const inspect = await currentContract.inspectCall({
1393
+ wallet: input.wallet,
1394
+ toAddress: prepared.nextCompiled.contractAddress,
1395
+ signer: input.signer,
1396
+ sendAmount: satToBtcAmount(input.nextAmountSat),
1397
+ feeSat: input.feeSat,
1398
+ utxoPolicy: input.utxoPolicy,
1399
+ sequence: resolvePolicySequence(prepared.current.stateValue),
1400
+ witness: buildPolicyDirectWitness(prepared),
1401
+ });
1402
+ return {
1403
+ mode: "direct-hop",
1404
+ prepared,
1405
+ inspect,
1406
+ };
1407
+ }
1408
+ async function executeDirectTransfer(sdk, input) {
1409
+ const prepared = await prepareDirectTransfer(sdk, input);
1410
+ const currentArtifact = input.currentArtifact
1411
+ ?? (input.currentArtifactPath ? (await sdk.loadArtifact(input.currentArtifactPath)).artifact : undefined);
1412
+ if (!currentArtifact) {
1413
+ throw new errors_1.ValidationError("currentArtifactPath or currentArtifact is required", {
1414
+ code: "POLICY_CURRENT_ARTIFACT_REQUIRED",
1415
+ });
1416
+ }
1417
+ const currentContract = sdk.fromArtifact(currentArtifact);
1418
+ const execution = await currentContract.execute({
1419
+ wallet: input.wallet,
1420
+ toAddress: prepared.nextCompiled.contractAddress,
1421
+ signer: input.signer,
1422
+ sendAmount: satToBtcAmount(input.nextAmountSat),
1423
+ feeSat: input.feeSat,
1424
+ broadcast: input.broadcast,
1425
+ utxoPolicy: input.utxoPolicy,
1426
+ sequence: resolvePolicySequence(prepared.current.stateValue),
1427
+ witness: buildPolicyDirectWitness(prepared),
1428
+ });
1429
+ return {
1430
+ mode: "direct-hop",
1431
+ prepared,
1432
+ execution,
1433
+ };
1434
+ }
1435
+ async function verifyTransfer(sdk, input) {
1436
+ const prepared = await verifyState(sdk, {
1437
+ artifactPath: input.currentArtifactPath,
1438
+ artifact: input.currentArtifact,
1439
+ template: input.template,
1440
+ statePath: input.currentStatePath,
1441
+ stateValue: input.currentStateValue,
1442
+ });
1443
+ const currentState = prepared.stateValue;
1444
+ const transfer = input.transferDescriptorValue ? validatePolicyTransferDescriptor(input.transferDescriptorValue) : undefined;
1445
+ const nextState = input.nextStateValue ? validatePolicyState(input.nextStateValue) : undefined;
1446
+ const outputBindingContext = await resolvePolicyOutputBindingVerificationContext(sdk, transfer?.outputDescriptor);
1447
+ const summary = transfer ? summarizePolicyTransferDescriptor(transfer) : undefined;
1448
+ const nextStateSummary = nextState ? summarizePolicyState(nextState) : undefined;
1449
+ const ok = !transfer || (transfer.previousPolicyHash === currentState.policyHash
1450
+ && transfer.previousStateHash === summarizePolicyState(currentState).hash
1451
+ && (!nextState || transfer.nextPolicyHash === nextState.policyHash)
1452
+ && (!nextStateSummary || transfer.nextStateHash === nextStateSummary.hash));
1453
+ return {
1454
+ ok,
1455
+ reason: ok ? undefined : "Policy transfer descriptor does not match current/next policy state",
1456
+ current: prepared,
1457
+ transfer,
1458
+ transferSummary: summary,
1459
+ nextState,
1460
+ verificationReport: {
1461
+ ...prepared.report,
1462
+ nextPolicyPresent: Boolean(nextState),
1463
+ plainExitAllowed: currentState.propagationMode !== "required",
1464
+ outputBinding: buildPolicyOutputBindingReport({
1465
+ descriptor: transfer?.outputDescriptor,
1466
+ sdkVerified: ok,
1467
+ supportedForm: outputBindingContext.supportedForm,
1468
+ reasonCode: outputBindingContext.reasonCode,
1469
+ autoDerived: outputBindingContext.autoDerived,
1470
+ fallbackReason: outputBindingContext.fallbackReason,
1471
+ bindingInputs: outputBindingContext.bindingInputs,
1472
+ }),
1473
+ enforcement: resolvePolicyEnforcementMode({
1474
+ currentPropagationMode: currentState.propagationMode,
1475
+ nextReceiverMode: nextState ? "policy" : "plain",
1476
+ }),
1477
+ },
1478
+ };
1479
+ }
1480
+ async function inspectTransfer(sdk, input) {
1481
+ const prepared = await prepareTransfer(sdk, input);
1482
+ const currentArtifact = input.currentArtifact
1483
+ ?? (input.currentArtifactPath ? (await sdk.loadArtifact(input.currentArtifactPath)).artifact : undefined);
1484
+ if (!currentArtifact) {
1485
+ throw new errors_1.ValidationError("currentArtifactPath or currentArtifact is required", {
1486
+ code: "POLICY_CURRENT_ARTIFACT_REQUIRED",
1487
+ });
1488
+ }
1489
+ const currentContract = sdk.fromArtifact(currentArtifact);
1490
+ if (!prepared.nextCompiled) {
1491
+ const inspect = await currentContract.inspectCall({
1492
+ wallet: input.wallet,
1493
+ toAddress: input.nextReceiver.address,
1494
+ signer: input.signer,
1495
+ sendAmount: satToBtcAmount(input.nextAmountSat),
1496
+ feeSat: input.feeSat,
1497
+ utxoPolicy: input.utxoPolicy,
1498
+ sequence: resolvePolicySequence(prepared.current.stateValue),
1499
+ witness: buildPolicyRecursiveWitness(prepared),
1500
+ });
1501
+ return {
1502
+ mode: "plain-exit",
1503
+ prepared,
1504
+ inspect,
1505
+ };
1506
+ }
1507
+ const inspect = await currentContract.inspectCall({
1508
+ wallet: input.wallet,
1509
+ toAddress: prepared.nextCompiled.contractAddress,
1510
+ signer: input.signer,
1511
+ sendAmount: satToBtcAmount(input.nextAmountSat),
1512
+ feeSat: input.feeSat,
1513
+ utxoPolicy: input.utxoPolicy,
1514
+ sequence: resolvePolicySequence(prepared.current.stateValue),
1515
+ witness: buildPolicyRecursiveWitness(prepared),
1516
+ });
1517
+ return {
1518
+ mode: prepared.verificationReport.enforcement,
1519
+ prepared,
1520
+ inspect,
1521
+ };
1522
+ }
1523
+ async function executeTransfer(sdk, input) {
1524
+ const prepared = await prepareTransfer(sdk, input);
1525
+ const currentArtifact = input.currentArtifact
1526
+ ?? (input.currentArtifactPath ? (await sdk.loadArtifact(input.currentArtifactPath)).artifact : undefined);
1527
+ if (!currentArtifact) {
1528
+ throw new errors_1.ValidationError("currentArtifactPath or currentArtifact is required", {
1529
+ code: "POLICY_CURRENT_ARTIFACT_REQUIRED",
1530
+ });
1531
+ }
1532
+ const currentContract = sdk.fromArtifact(currentArtifact);
1533
+ if (!prepared.nextCompiled) {
1534
+ const execution = await currentContract.execute({
1535
+ wallet: input.wallet,
1536
+ toAddress: input.nextReceiver.address,
1537
+ signer: input.signer,
1538
+ sendAmount: satToBtcAmount(input.nextAmountSat),
1539
+ feeSat: input.feeSat,
1540
+ broadcast: input.broadcast,
1541
+ utxoPolicy: input.utxoPolicy,
1542
+ sequence: resolvePolicySequence(prepared.current.stateValue),
1543
+ witness: buildPolicyRecursiveWitness(prepared),
1544
+ });
1545
+ return {
1546
+ mode: "plain-exit",
1547
+ prepared,
1548
+ execution,
1549
+ };
1550
+ }
1551
+ const execution = await currentContract.execute({
1552
+ wallet: input.wallet,
1553
+ toAddress: prepared.nextCompiled.contractAddress,
1554
+ signer: input.signer,
1555
+ sendAmount: satToBtcAmount(input.nextAmountSat),
1556
+ feeSat: input.feeSat,
1557
+ broadcast: input.broadcast,
1558
+ utxoPolicy: input.utxoPolicy,
1559
+ sequence: resolvePolicySequence(prepared.current.stateValue),
1560
+ witness: buildPolicyRecursiveWitness(prepared),
1561
+ });
1562
+ return {
1563
+ mode: prepared.verificationReport.enforcement,
1564
+ prepared,
1565
+ execution,
1566
+ };
1567
+ }
1568
+ async function exportEvidence(sdk, input) {
1569
+ const verified = await verifyState(sdk, {
1570
+ artifactPath: input.artifactPath,
1571
+ artifact: input.artifact,
1572
+ template: input.template,
1573
+ statePath: input.statePath,
1574
+ stateValue: input.stateValue,
1575
+ });
1576
+ const transferVerification = input.transferDescriptorValue
1577
+ ? await verifyTransfer(sdk, {
1578
+ template: input.template,
1579
+ currentArtifact: verified.artifact,
1580
+ currentStateValue: verified.stateValue,
1581
+ transferDescriptorValue: input.transferDescriptorValue,
1582
+ })
1583
+ : undefined;
1584
+ const template = await resolveTemplateDocument(sdk, input.template, verified.stateValue.propagationMode);
1585
+ const stateSummary = summarizePolicyState(verified.stateValue);
1586
+ const artifact = verified.artifact;
1587
+ const renderedSourceHash = artifact.source.simfPath && (0, node_fs_1.existsSync)(artifact.source.simfPath)
1588
+ ? (0, summary_1.sha256HexUtf8)(await (0, promises_1.readFile)(artifact.source.simfPath, "utf8"))
1589
+ : undefined;
1590
+ return {
1591
+ schemaVersion: exports.POLICY_EVIDENCE_BUNDLE_SCHEMA_VERSION,
1592
+ artifact,
1593
+ template: template.summary,
1594
+ state: stateSummary,
1595
+ ...(input.transferDescriptorValue ? { transfer: summarizePolicyTransferDescriptor(input.transferDescriptorValue) } : {}),
1596
+ report: transferVerification?.verificationReport ?? verified.report,
1597
+ renderedSourceHash,
1598
+ sourceVerificationMode: renderedSourceHash ? "source-reloaded" : "artifact-only",
1599
+ compiled: {
1600
+ program: artifact.compiled.program,
1601
+ cmr: artifact.compiled.cmr,
1602
+ contractAddress: artifact.compiled.contractAddress,
1603
+ },
1604
+ };
1605
+ }