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