@hazbase/simplicity 0.4.6 → 0.4.8
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 +151 -2
- package/dist/core/schnorr.js +2 -8
- package/dist/core/strictPolicyCrypto.d.ts +8 -0
- package/dist/core/strictPolicyCrypto.js +36 -0
- package/dist/docs/definitions/strict-cu-whitelist-golden-v1.json +63 -0
- package/dist/docs/definitions/strict-holder-input-v1.simf +62 -0
- package/dist/docs/definitions/strict-policy-snapshot-golden-v1.json +47 -0
- package/dist/docs/definitions/strict-verifier-ordinary-v1.simf +240 -0
- package/dist/domain/damp.d.ts +96 -0
- package/dist/domain/damp.js +373 -0
- package/dist/domain/dampPolicy.d.ts +91 -0
- package/dist/domain/dampPolicy.js +249 -0
- package/dist/domain/dampPset.d.ts +51 -0
- package/dist/domain/dampPset.js +237 -0
- package/dist/domain/dampTransaction.d.ts +96 -0
- package/dist/domain/dampTransaction.js +320 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +57 -1
- package/dist/x402/index.d.ts +18 -0
- package/dist/x402/index.js +57 -6
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -12,6 +12,21 @@ It is intentionally narrower than a full market stack. It does **not** try to be
|
|
|
12
12
|
|
|
13
13
|
This SDK is designed to help Node developers get productive quickly, but it is still opinionated, early-stage, and best suited today to permissioned settlement pilots on Liquid.
|
|
14
14
|
|
|
15
|
+
## Version 0.4.7
|
|
16
|
+
|
|
17
|
+
This release extends strict-policy positions from ordinary transfers to a
|
|
18
|
+
complete controlled lifecycle:
|
|
19
|
+
|
|
20
|
+
- ordinary transfers remain restricted to the approved holder whitelist;
|
|
21
|
+
- redemptions return the position to a bound vault under an authority signature;
|
|
22
|
+
- governance can rotate the verifier to a reviewed successor without moving the
|
|
23
|
+
protected asset; and
|
|
24
|
+
- browser wallets can provide their own audited crypto implementation before
|
|
25
|
+
any strict-policy helper loads, avoiding extension CSP violations.
|
|
26
|
+
|
|
27
|
+
For policy-locked redemptions, Liquid x402 now binds a wallet-created holder
|
|
28
|
+
signature for the locally recomputed transaction hash into the payment payload.
|
|
29
|
+
|
|
15
30
|
## What You Can Build
|
|
16
31
|
|
|
17
32
|
With the current public SDK you can build and test:
|
|
@@ -22,6 +37,7 @@ With the current public SDK you can build and test:
|
|
|
22
37
|
- receivable repayment-first funding / partial repayment / closing flows
|
|
23
38
|
- RWA delivery-versus-payment flows over Liquid PSETs and Liquid x402
|
|
24
39
|
- atomic Liquid DvP proposals where service delivery and buyer payment settle in one PSET
|
|
40
|
+
- canonical, signed policy snapshots for strict DAMP-compatible wallet validation
|
|
25
41
|
- evidence, trust summary, lineage, and finality exports
|
|
26
42
|
|
|
27
43
|
## Public Architecture
|
|
@@ -34,6 +50,8 @@ The public SDK is organized into domain clients and lower-level payment helpers:
|
|
|
34
50
|
- `sdk.receivables`: repayment-first receivable business layer
|
|
35
51
|
- `sdk.rwaDvp`: RWA purchase terms, payment requirements, claim descriptors, and evidence
|
|
36
52
|
- `sdk.payments.x402`: Liquid x402 assets, PSET payment helpers, verification, and settlement
|
|
53
|
+
- root exports such as `prepareStrictPolicySnapshot(...)` and
|
|
54
|
+
`verifyStrictPolicySnapshot(...)`: canonical policy snapshot and authority-proof validation
|
|
37
55
|
|
|
38
56
|
A useful mental model is:
|
|
39
57
|
- `sdk.outputBinding` + `sdk.policies` provide the shared settlement kernel
|
|
@@ -82,6 +100,134 @@ npx simplicity-cli --help
|
|
|
82
100
|
|
|
83
101
|
For richer walkthroughs and sample JSON / `.simf` files, use [`docs/definitions/README.md`](./docs/definitions/README.md).
|
|
84
102
|
|
|
103
|
+
## Strict Policy Snapshots And Covenants
|
|
104
|
+
|
|
105
|
+
The root-level strict policy helpers provide the strict-asset validation and
|
|
106
|
+
covenant layer. They canonicalize and verify an approved snapshot, derive the
|
|
107
|
+
canonical holder output `CU(X)`, build fixed-depth owner whitelist proofs, and
|
|
108
|
+
render or compile matching holder and verifier covenants.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import {
|
|
112
|
+
buildStrictPolicyWhitelist,
|
|
113
|
+
deriveStrictPolicyHolderPosition,
|
|
114
|
+
inspectStrictPolicyTransactionWithProofs,
|
|
115
|
+
normalizeStrictPolicyVerifierCovenantParams,
|
|
116
|
+
prepareStrictPolicySnapshot,
|
|
117
|
+
verifyStrictPolicySnapshot,
|
|
118
|
+
} from "@hazbase/simplicity";
|
|
119
|
+
|
|
120
|
+
const prepared = prepareStrictPolicySnapshot({ payload, authority });
|
|
121
|
+
console.log(prepared.snapshotHash, prepared.approvalMessageHash);
|
|
122
|
+
|
|
123
|
+
await verifyStrictPolicySnapshot({
|
|
124
|
+
...prepared,
|
|
125
|
+
approvalProof,
|
|
126
|
+
approvalProofHash,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const whitelist = buildStrictPolicyWhitelist([recipientXonly, changeXonly]);
|
|
130
|
+
if (whitelist.root !== prepared.payload.whitelistRoot) {
|
|
131
|
+
throw new Error("Whitelist does not match the active policy snapshot");
|
|
132
|
+
}
|
|
133
|
+
const recipientPosition = await deriveStrictPolicyHolderPosition({
|
|
134
|
+
holderProgramCmr,
|
|
135
|
+
numsInternalKey,
|
|
136
|
+
holderXonly: recipientXonly,
|
|
137
|
+
});
|
|
138
|
+
const proofFor = (ownerXonly: string) => {
|
|
139
|
+
const entry = whitelist.entries.find((candidate) => candidate.ownerXonly === ownerXonly);
|
|
140
|
+
if (!entry) throw new Error("Owner is not in the active whitelist");
|
|
141
|
+
return entry.proof;
|
|
142
|
+
};
|
|
143
|
+
const strictOutputAuthorizations = [
|
|
144
|
+
{ outputIndex: 1, ownerXonly: recipientXonly, proof: proofFor(recipientXonly) },
|
|
145
|
+
{ outputIndex: 2, ownerXonly: changeXonly, proof: proofFor(changeXonly) },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
const inspection = await inspectStrictPolicyTransactionWithProofs(decodedTransaction, {
|
|
149
|
+
operation: "transfer",
|
|
150
|
+
snapshot: prepared.payload,
|
|
151
|
+
feeAssetId,
|
|
152
|
+
maxFeeAmountAtomic: "500",
|
|
153
|
+
holderProgramCmr,
|
|
154
|
+
holderNumsInternalKey: numsInternalKey,
|
|
155
|
+
strictOutputAuthorizations,
|
|
156
|
+
});
|
|
157
|
+
console.log(recipientPosition.scriptPubKey, inspection.summaryHash);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The current public schema deliberately supports `issuer_single` only. Its
|
|
161
|
+
authority fields are typed so a separately specified quorum or multisig scheme
|
|
162
|
+
can be added later without treating unknown proof types as valid.
|
|
163
|
+
`strict-holder-input-v1.simf` proves the owner-bound `CU(X)` and holder
|
|
164
|
+
signature. `strict-verifier-ordinary-v1.simf` fixes the verifier at input and
|
|
165
|
+
output zero and exposes three mutually exclusive paths:
|
|
166
|
+
|
|
167
|
+
- ordinary transfer: every strict output must be a canonical `CU(X)` for a key
|
|
168
|
+
proven under the active whitelist root;
|
|
169
|
+
- redemption: every strict output must return to the configured vault and the
|
|
170
|
+
configured authority signs the complete transaction; and
|
|
171
|
+
- governance: the authority signs a verifier-only transition to a reviewed
|
|
172
|
+
successor contract, without moving any strict asset.
|
|
173
|
+
|
|
174
|
+
### Verifier resource profiles
|
|
175
|
+
|
|
176
|
+
`strict_v2` is the default profile for the general strict-policy lifecycle. It
|
|
177
|
+
permits up to 16 transaction inputs, 8 outputs, and a 256-owner whitelist;
|
|
178
|
+
larger transactions fail closed.
|
|
179
|
+
|
|
180
|
+
`strict_v2_compact` is an explicit, tightly bounded profile for a controlled
|
|
181
|
+
two-owner lifecycle, such as issuer inventory to an approved holder. It permits
|
|
182
|
+
at most 3 inputs, 4 outputs, and 2 whitelist owners. It retains the complete
|
|
183
|
+
holder signature and whitelist checks while making the verifier witness small
|
|
184
|
+
enough for the bounded transaction shape. Do not use it for a secondary market,
|
|
185
|
+
additional intermediaries, or transactions that need more inputs, outputs, or
|
|
186
|
+
approved holders.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
const verifier = normalizeStrictPolicyVerifierCovenantParams({
|
|
190
|
+
assetId,
|
|
191
|
+
feeAssetId,
|
|
192
|
+
redemptionScriptHash,
|
|
193
|
+
authorityXonly,
|
|
194
|
+
resourceProfile: "strict_v2_compact",
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The verifier parameters require both `redemptionScriptHash` and
|
|
199
|
+
`authorityXonly`, so callers must bind the vault and the authority explicitly
|
|
200
|
+
when compiling a new verifier.
|
|
201
|
+
|
|
202
|
+
`inspectStrictPolicyTransactionWithProofs(...)` is the recommended wallet API.
|
|
203
|
+
It validates every strict output by index, verifies its owner proof, derives the
|
|
204
|
+
destination script locally, and then checks verifier continuity, asset
|
|
205
|
+
conservation, issuance absence, and the fee ceiling. The lower-level
|
|
206
|
+
`inspectStrictPolicyTransaction(...)` remains available for adapters that have
|
|
207
|
+
already completed that proof-to-script derivation.
|
|
208
|
+
|
|
209
|
+
`inspectStrictPolicyPsetForSigning(...)` is the signing boundary for a complete
|
|
210
|
+
PSET. Supply a local lossless decoder through `decodePset`; the SDK then binds
|
|
211
|
+
the canonical PSET bytes to the decoded transaction, validates the snapshot,
|
|
212
|
+
CMR, verifier, every strict output and fee limit, and returns only the locally
|
|
213
|
+
recomputed Simplicity `sig_all_hash`. A service-provided hash is accepted only
|
|
214
|
+
as a comparison hint and causes rejection when it differs.
|
|
215
|
+
|
|
216
|
+
The settlement repository includes
|
|
217
|
+
`hazbase-simplicity-inspect-pset`, a native reference decoder built on the
|
|
218
|
+
official `hal-simplicity` sighash implementation. A browser wallet still needs
|
|
219
|
+
to connect an equivalent lossless WASM decoder to the SDK callback.
|
|
220
|
+
The SDK supplies the canonical schema, compiler inputs and x402 holder-signature
|
|
221
|
+
payload binding. A wallet must still decode the complete PSET locally, derive
|
|
222
|
+
the exact `sig_all_hash`, construct the branch witness, and verify the active
|
|
223
|
+
snapshot before signing. Transaction-level regtest/testnet vectors remain
|
|
224
|
+
release gates; these templates and checks alone are not a production activation.
|
|
225
|
+
|
|
226
|
+
The pinned public vector is
|
|
227
|
+
[`strict-policy-snapshot-golden-v1.json`](./docs/definitions/strict-policy-snapshot-golden-v1.json).
|
|
228
|
+
The holder-output and whitelist vector is
|
|
229
|
+
[`strict-cu-whitelist-golden-v1.json`](./docs/definitions/strict-cu-whitelist-golden-v1.json).
|
|
230
|
+
|
|
85
231
|
## Domain Overview
|
|
86
232
|
|
|
87
233
|
### Policy
|
|
@@ -181,8 +327,11 @@ Liquid x402 request with `extra.redemptionSource.type:
|
|
|
181
327
|
`buildLiquidPolicyLockedRedemptionPaymentFromProposal(...)` when a service has
|
|
182
328
|
already prepared the Simplicity policy-spend PSET proposal. The helper binds the
|
|
183
329
|
custom RWA asset, policy position, vault output, expiry, and summary hash into
|
|
184
|
-
the `X-PAYMENT` payload
|
|
185
|
-
|
|
330
|
+
the `X-PAYMENT` payload. When the proposal requires a holder signature, supply
|
|
331
|
+
the locally derived `holderSignature` for the exact `sig_all_hash`; the helper
|
|
332
|
+
then carries the request and signature together without accepting a
|
|
333
|
+
service-provided signing hash as authoritative. It still rejects proposals that
|
|
334
|
+
require the wallet to construct an unspecified Simplicity witness.
|
|
186
335
|
|
|
187
336
|
Services can prepare that policy-spend proposal with
|
|
188
337
|
`sdk.policies.inspectTransfer(...)` or `sdk.policies.executeTransfer(...)`.
|
package/dist/core/schnorr.js
CHANGED
|
@@ -6,15 +6,9 @@ exports.schnorrPublicKeyFromPrivkeyHex = schnorrPublicKeyFromPrivkeyHex;
|
|
|
6
6
|
exports.schnorrSignHex = schnorrSignHex;
|
|
7
7
|
exports.schnorrVerifyHex = schnorrVerifyHex;
|
|
8
8
|
const errors_1 = require("./errors");
|
|
9
|
-
|
|
10
|
-
const nativeImport = new Function("specifier", "return import(specifier);");
|
|
11
|
-
let schnorrPromise;
|
|
9
|
+
const strictPolicyCrypto_1 = require("./strictPolicyCrypto");
|
|
12
10
|
async function loadSchnorr() {
|
|
13
|
-
|
|
14
|
-
const typed = module;
|
|
15
|
-
return typed.schnorr;
|
|
16
|
-
});
|
|
17
|
-
return schnorrPromise;
|
|
11
|
+
return (await (0, strictPolicyCrypto_1.loadStrictPolicyCrypto)()).schnorr;
|
|
18
12
|
}
|
|
19
13
|
function assertHex(value, byteLength, code, message) {
|
|
20
14
|
if (!/^[0-9a-f]+$/i.test(value) || value.length % 2 !== 0) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Secp256k1Module = typeof import("@noble/curves/secp256k1.js");
|
|
2
|
+
export interface StrictPolicyCryptoProvider {
|
|
3
|
+
secp256k1: Secp256k1Module["secp256k1"];
|
|
4
|
+
schnorr: Secp256k1Module["schnorr"];
|
|
5
|
+
}
|
|
6
|
+
export declare function configureStrictPolicyCrypto(provider: StrictPolicyCryptoProvider): void;
|
|
7
|
+
export declare function loadStrictPolicyCrypto(): Promise<StrictPolicyCryptoProvider>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configureStrictPolicyCrypto = configureStrictPolicyCrypto;
|
|
4
|
+
exports.loadStrictPolicyCrypto = loadStrictPolicyCrypto;
|
|
5
|
+
// Keep the native import intact under CommonJS output so Node can load noble's
|
|
6
|
+
// ESM package. Construct it only for the Node fallback: Chrome extension CSP
|
|
7
|
+
// rejects `new Function` when a bundled module evaluates, while browser bundles
|
|
8
|
+
// inject the audited implementation through configureStrictPolicyCrypto first.
|
|
9
|
+
function nativeImport(specifier) {
|
|
10
|
+
const importModule = new Function("value", "return import(value);");
|
|
11
|
+
return importModule(specifier);
|
|
12
|
+
}
|
|
13
|
+
let configuredProvider;
|
|
14
|
+
let providerPromise;
|
|
15
|
+
function configureStrictPolicyCrypto(provider) {
|
|
16
|
+
if (!provider?.secp256k1 || !provider?.schnorr) {
|
|
17
|
+
throw new TypeError("Strict policy crypto provider must include secp256k1 and schnorr");
|
|
18
|
+
}
|
|
19
|
+
if (configuredProvider
|
|
20
|
+
&& (configuredProvider.secp256k1 !== provider.secp256k1
|
|
21
|
+
|| configuredProvider.schnorr !== provider.schnorr)) {
|
|
22
|
+
throw new Error("Strict policy crypto provider is already configured");
|
|
23
|
+
}
|
|
24
|
+
configuredProvider = provider;
|
|
25
|
+
providerPromise = Promise.resolve(provider);
|
|
26
|
+
}
|
|
27
|
+
async function loadStrictPolicyCrypto() {
|
|
28
|
+
providerPromise ??= nativeImport("@noble/curves/secp256k1.js").then((module) => {
|
|
29
|
+
const typed = module;
|
|
30
|
+
return {
|
|
31
|
+
secp256k1: typed.secp256k1,
|
|
32
|
+
schnorr: typed.schnorr,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
return providerPromise;
|
|
36
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "hazbase_strict_policy_cu_whitelist_vector_v1",
|
|
3
|
+
"holderProgramCmr": "72f400043533c5fc9d5e6865b6edb2d22732db3379e54d2643b3c02652c0c87a",
|
|
4
|
+
"numsInternalKey": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
|
|
5
|
+
"whitelist": {
|
|
6
|
+
"depth": 8,
|
|
7
|
+
"root": "e3262065c02e0b42c1c63b12ef118202356430ca5b5d272b54e3715f48a7bf23",
|
|
8
|
+
"entries": [
|
|
9
|
+
{
|
|
10
|
+
"ownerXonly": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
|
11
|
+
"leafHash": "79943062a19aed81bf32e0b826bb048a94187c52804d3343971090ed4dae8618",
|
|
12
|
+
"proof": [
|
|
13
|
+
{ "siblingPosition": "right", "siblingHash": "cf74eec84a4fac9690f00fdef98466da22c68c4931be60986fea89ee99b920cd" },
|
|
14
|
+
{ "siblingPosition": "right", "siblingHash": "1c8e82d1dfbc4e86bae912bdf36574b23608a1d099aba732d7ad1f8b98cf3ada" },
|
|
15
|
+
{ "siblingPosition": "right", "siblingHash": "44f3a9c851a83b8b22fba37a44813b506e065776053565f486c2cd877ec4a207" },
|
|
16
|
+
{ "siblingPosition": "right", "siblingHash": "b32210b7558e6c968279875cd07abccafd6dd29ca62589dd19c686fb32d997bc" },
|
|
17
|
+
{ "siblingPosition": "right", "siblingHash": "4a006214dc3467b20dce046d8c0b6614e537b1c8874d0a07ef44d0e411e97a81" },
|
|
18
|
+
{ "siblingPosition": "right", "siblingHash": "2b5109b939fd3ba1e538b3f0477960673b24fa9caae2f4c121ca608d44a33f9c" },
|
|
19
|
+
{ "siblingPosition": "right", "siblingHash": "15869b4b05b6ac579551352dc91f4c1e06c225a233b7bbda967534b8563a461f" },
|
|
20
|
+
{ "siblingPosition": "right", "siblingHash": "696cdfe69a3b0d700ad5c8ce6b14e13e1dbec6ef3283e231d37f6f648e2fbec4" }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"ownerXonly": "c6047f9441ed7d6d3045406e95c07cd85a6e326c5a5fe5bdfb8f6935d3a3c96c",
|
|
25
|
+
"leafHash": "cf74eec84a4fac9690f00fdef98466da22c68c4931be60986fea89ee99b920cd",
|
|
26
|
+
"proof": [
|
|
27
|
+
{ "siblingPosition": "left", "siblingHash": "79943062a19aed81bf32e0b826bb048a94187c52804d3343971090ed4dae8618" },
|
|
28
|
+
{ "siblingPosition": "right", "siblingHash": "1c8e82d1dfbc4e86bae912bdf36574b23608a1d099aba732d7ad1f8b98cf3ada" },
|
|
29
|
+
{ "siblingPosition": "right", "siblingHash": "44f3a9c851a83b8b22fba37a44813b506e065776053565f486c2cd877ec4a207" },
|
|
30
|
+
{ "siblingPosition": "right", "siblingHash": "b32210b7558e6c968279875cd07abccafd6dd29ca62589dd19c686fb32d997bc" },
|
|
31
|
+
{ "siblingPosition": "right", "siblingHash": "4a006214dc3467b20dce046d8c0b6614e537b1c8874d0a07ef44d0e411e97a81" },
|
|
32
|
+
{ "siblingPosition": "right", "siblingHash": "2b5109b939fd3ba1e538b3f0477960673b24fa9caae2f4c121ca608d44a33f9c" },
|
|
33
|
+
{ "siblingPosition": "right", "siblingHash": "15869b4b05b6ac579551352dc91f4c1e06c225a233b7bbda967534b8563a461f" },
|
|
34
|
+
{ "siblingPosition": "right", "siblingHash": "696cdfe69a3b0d700ad5c8ce6b14e13e1dbec6ef3283e231d37f6f648e2fbec4" }
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"positions": [
|
|
40
|
+
{
|
|
41
|
+
"holderProgramCmr": "72f400043533c5fc9d5e6865b6edb2d22732db3379e54d2643b3c02652c0c87a",
|
|
42
|
+
"holderTapleafHash": "892f43437d4f9992ee854f4852a4dc533bd70df011f9e8331a1b8e5edb450225",
|
|
43
|
+
"holderXonly": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
|
44
|
+
"ownerTapDataHash": "2726c3e2df291658a30abbf145b775c0403192b7bf16a6ee3c6243b00f445bc2",
|
|
45
|
+
"taprootRoot": "35b17c8a4e03bd30b21fc9a27bbbefec267843e6be6c2dc5becc8bfac2a8627c",
|
|
46
|
+
"numsInternalKey": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
|
|
47
|
+
"outputKey": "741df228c751ac305ebf6fbd300a7c1e5eddb21415b93782709f6839fafc5931",
|
|
48
|
+
"scriptPubKey": "5120741df228c751ac305ebf6fbd300a7c1e5eddb21415b93782709f6839fafc5931",
|
|
49
|
+
"scriptHash": "14506b881f1c9ef212b7dfb93100b1067f40b876059773c846ffd6a9d9c74080"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"holderProgramCmr": "72f400043533c5fc9d5e6865b6edb2d22732db3379e54d2643b3c02652c0c87a",
|
|
53
|
+
"holderTapleafHash": "892f43437d4f9992ee854f4852a4dc533bd70df011f9e8331a1b8e5edb450225",
|
|
54
|
+
"holderXonly": "c6047f9441ed7d6d3045406e95c07cd85a6e326c5a5fe5bdfb8f6935d3a3c96c",
|
|
55
|
+
"ownerTapDataHash": "825cae77c977520bf404779e1a66a5f084e1c18bfecf77a9c0ded3d977368e80",
|
|
56
|
+
"taprootRoot": "012c978075295e3f5f32ded5c127050a287b465cdf076efcdb607fd2dfca4c92",
|
|
57
|
+
"numsInternalKey": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
|
|
58
|
+
"outputKey": "1444e10128e2281b23fc8f1b6ef6d804cd1be4c43dbe381b4a0c89bd05111d88",
|
|
59
|
+
"scriptPubKey": "51201444e10128e2281b23fc8f1b6ef6d804cd1be4c43dbe381b4a0c89bd05111d88",
|
|
60
|
+
"scriptHash": "1e943bea5b0c861d787829dda1c2bd981c9c546cfeb5ff7567c94261acfa3548"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Strict v2 holder input covenant, Phase 1 prototype.
|
|
3
|
+
*
|
|
4
|
+
* This leaf proves that the current strict-asset input is the canonical CU(X)
|
|
5
|
+
* for witness OWNER_XONLY and that the same transaction spends the unique
|
|
6
|
+
* verifier asset at input 0.
|
|
7
|
+
* The verifier input must separately execute the matching CV policy covenant;
|
|
8
|
+
* this holder leaf is not a complete strict-asset policy by itself.
|
|
9
|
+
*
|
|
10
|
+
* Asset constants use the byte order returned by the Simplicity Elements jets.
|
|
11
|
+
*/
|
|
12
|
+
fn not(bit: bool) -> bool {
|
|
13
|
+
<u1>::into(jet::complement_1(<bool>::into(bit)))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
fn canonical_holder_script_hash(owner: Pubkey) -> u256 {
|
|
17
|
+
let expected_internal_key: Pubkey = 0x{{NUMS_INTERNAL_KEY}};
|
|
18
|
+
assert!(jet::eq_256(jet::internal_key(), expected_internal_key));
|
|
19
|
+
|
|
20
|
+
let user_leaf: u256 = jet::tapleaf_hash();
|
|
21
|
+
let tapdata_ctx: Ctx8 = jet::tapdata_init();
|
|
22
|
+
let tapdata_ctx: Ctx8 = jet::sha_256_ctx_8_add_32(tapdata_ctx, owner);
|
|
23
|
+
let owner_tapdata: u256 = jet::sha_256_ctx_8_finalize(tapdata_ctx);
|
|
24
|
+
let holder_root: u256 = jet::build_tapbranch(user_leaf, owner_tapdata);
|
|
25
|
+
let output_key: u256 = jet::build_taptweak(expected_internal_key, holder_root);
|
|
26
|
+
|
|
27
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_init();
|
|
28
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(script_ctx, 0x51);
|
|
29
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(script_ctx, 0x20);
|
|
30
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_32(script_ctx, output_key);
|
|
31
|
+
jet::sha_256_ctx_8_finalize(script_ctx)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn explicit_input_asset_amount(index: u32) -> (u256, u64) {
|
|
35
|
+
let pair: (Asset1, Amount1) = unwrap(jet::input_amount(index));
|
|
36
|
+
let (asset, amount): (Asset1, Amount1) = pair;
|
|
37
|
+
let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset);
|
|
38
|
+
let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
|
|
39
|
+
(asset_bits, amount_bits)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn main() {
|
|
43
|
+
let owner: Pubkey = witness::OWNER_XONLY;
|
|
44
|
+
assert!(jet::eq_256(jet::current_script_hash(), canonical_holder_script_hash(owner)));
|
|
45
|
+
|
|
46
|
+
let current_index: u32 = jet::current_index();
|
|
47
|
+
assert!(not(jet::eq_32(current_index, 0)));
|
|
48
|
+
|
|
49
|
+
let (current_asset, current_amount): (u256, u64) =
|
|
50
|
+
explicit_input_asset_amount(current_index);
|
|
51
|
+
let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
|
|
52
|
+
assert!(jet::eq_256(current_asset, strict_asset));
|
|
53
|
+
assert!(not(jet::eq_64(current_amount, 0)));
|
|
54
|
+
|
|
55
|
+
let (verifier_asset, verifier_amount): (u256, u64) =
|
|
56
|
+
explicit_input_asset_amount(0);
|
|
57
|
+
let expected_verifier_asset: u256 = 0x{{VERIFIER_ASSET_ID_JET_HEX}};
|
|
58
|
+
assert!(jet::eq_256(verifier_asset, expected_verifier_asset));
|
|
59
|
+
assert!(jet::eq_64(verifier_amount, {{VERIFIER_AMOUNT_ATOMIC}}));
|
|
60
|
+
|
|
61
|
+
jet::bip_0340_verify((owner, jet::sig_all_hash()), witness::HOLDER_SIGNATURE)
|
|
62
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "hazbase_strict_policy_snapshot_golden_vector_v1",
|
|
3
|
+
"name": "issuer_single_v1_regtest_epoch_0",
|
|
4
|
+
"notes": "Public test vector only. The private key value 1 must never be used outside tests.",
|
|
5
|
+
"testPrivateKeyHex": "0000000000000000000000000000000000000000000000000000000000000001",
|
|
6
|
+
"payload": {
|
|
7
|
+
"schema": "hazbase_strict_policy_snapshot_v1",
|
|
8
|
+
"network": "liquid-regtest",
|
|
9
|
+
"liquidGenesisHash": "1111111111111111111111111111111111111111111111111111111111111111",
|
|
10
|
+
"assetBindingId": "strict-card-pilot-1",
|
|
11
|
+
"liquidAssetId": "2222222222222222222222222222222222222222222222222222222222222222",
|
|
12
|
+
"epoch": "0",
|
|
13
|
+
"previousSnapshotHash": null,
|
|
14
|
+
"policyRoot": "3333333333333333333333333333333333333333333333333333333333333333",
|
|
15
|
+
"whitelistRoot": "4444444444444444444444444444444444444444444444444444444444444444",
|
|
16
|
+
"verifier": {
|
|
17
|
+
"outpoint": {
|
|
18
|
+
"txid": "5555555555555555555555555555555555555555555555555555555555555555",
|
|
19
|
+
"vout": 0
|
|
20
|
+
},
|
|
21
|
+
"assetId": "6666666666666666666666666666666666666666666666666666666666666666",
|
|
22
|
+
"amountAtomic": "1",
|
|
23
|
+
"cmr": "7777777777777777777777777777777777777777777777777777777777777777",
|
|
24
|
+
"scriptHash": "8888888888888888888888888888888888888888888888888888888888888888"
|
|
25
|
+
},
|
|
26
|
+
"effectiveAt": "2026-08-19T00:00:00.000Z",
|
|
27
|
+
"validUntil": null
|
|
28
|
+
},
|
|
29
|
+
"authority": {
|
|
30
|
+
"authorityType": "issuer_single",
|
|
31
|
+
"authorityId": "strict-card-pilot-authority",
|
|
32
|
+
"authorityVersion": "1",
|
|
33
|
+
"authoritySetHash": "d2e6e5ed40e4ed2eebd74e30f333f33290721291de77b4b7bbf1856afe41d995",
|
|
34
|
+
"approvalScheme": "bip340-sha256",
|
|
35
|
+
"approvalThreshold": 1,
|
|
36
|
+
"signerXonly": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
|
37
|
+
"approvedAt": "2026-08-18T23:59:00.000Z"
|
|
38
|
+
},
|
|
39
|
+
"expected": {
|
|
40
|
+
"canonicalPayload": "{\"assetBindingId\":\"strict-card-pilot-1\",\"effectiveAt\":\"2026-08-19T00:00:00.000Z\",\"epoch\":\"0\",\"liquidAssetId\":\"2222222222222222222222222222222222222222222222222222222222222222\",\"liquidGenesisHash\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"network\":\"liquid-regtest\",\"policyRoot\":\"3333333333333333333333333333333333333333333333333333333333333333\",\"previousSnapshotHash\":null,\"schema\":\"hazbase_strict_policy_snapshot_v1\",\"validUntil\":null,\"verifier\":{\"amountAtomic\":\"1\",\"assetId\":\"6666666666666666666666666666666666666666666666666666666666666666\",\"cmr\":\"7777777777777777777777777777777777777777777777777777777777777777\",\"outpoint\":{\"txid\":\"5555555555555555555555555555555555555555555555555555555555555555\",\"vout\":0},\"scriptHash\":\"8888888888888888888888888888888888888888888888888888888888888888\"},\"whitelistRoot\":\"4444444444444444444444444444444444444444444444444444444444444444\"}",
|
|
41
|
+
"snapshotHash": "1c3994d3bd78e9d8cb48b33be8fc2db4e2da2c9bde11d0bfd26ab493a46942dd",
|
|
42
|
+
"canonicalApprovalMessage": "{\"approvalScheme\":\"bip340-sha256\",\"approvalThreshold\":1,\"approvedAt\":\"2026-08-18T23:59:00.000Z\",\"assetBindingId\":\"strict-card-pilot-1\",\"authorityId\":\"strict-card-pilot-authority\",\"authoritySetHash\":\"d2e6e5ed40e4ed2eebd74e30f333f33290721291de77b4b7bbf1856afe41d995\",\"authorityType\":\"issuer_single\",\"authorityVersion\":\"1\",\"epoch\":\"0\",\"liquidAssetId\":\"2222222222222222222222222222222222222222222222222222222222222222\",\"liquidGenesisHash\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"network\":\"liquid-regtest\",\"previousSnapshotHash\":null,\"schema\":\"hazbase_strict_policy_snapshot_approval_v1\",\"signerXonly\":\"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"snapshotHash\":\"1c3994d3bd78e9d8cb48b33be8fc2db4e2da2c9bde11d0bfd26ab493a46942dd\",\"verifierCmr\":\"7777777777777777777777777777777777777777777777777777777777777777\",\"verifierScriptHash\":\"8888888888888888888888888888888888888888888888888888888888888888\"}",
|
|
43
|
+
"approvalMessageHash": "6535e9cbdf029702754847a1fd7ab5e41a0606793632ab87a0c6a9f24ac59fc4",
|
|
44
|
+
"signatureHex": "14e7c165d70a0cf33cba29cc62bed4c1c55e1adbe537b5f55691e382dbf4d376eb98e48670b7238fddca222cb01b987fa2f217422dc684f8d6c7941ace6912a7",
|
|
45
|
+
"approvalProofHash": "1a657fb2cd581027b2603aad1e19f644de50d4a3ca25e1e10cd036ce3076e91a"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Strict v2 verifier covenant.
|
|
3
|
+
*
|
|
4
|
+
* One CMR exposes three explicit paths:
|
|
5
|
+
* - ordinary transfer: every strict output is a canonical whitelisted CU(X)
|
|
6
|
+
* - redemption: every strict output is returned to the configured vault
|
|
7
|
+
* - governance: the authority rotates the verifier to a reviewed successor
|
|
8
|
+
*
|
|
9
|
+
* Pilot limits are consensus-visible: at most 16 inputs, at most 8 outputs,
|
|
10
|
+
* and an 8-level owner whitelist proof. Transactions outside these bounds
|
|
11
|
+
* fail closed and require a later reviewed profile.
|
|
12
|
+
*/
|
|
13
|
+
fn not(bit: bool) -> bool {
|
|
14
|
+
<u1>::into(jet::complement_1(<bool>::into(bit)))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn explicit_input_asset_amount(index: u32) -> (u256, u64) {
|
|
18
|
+
let pair: (Asset1, Amount1) = unwrap(jet::input_amount(index));
|
|
19
|
+
let (asset, amount): (Asset1, Amount1) = pair;
|
|
20
|
+
let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset);
|
|
21
|
+
let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
|
|
22
|
+
(asset_bits, amount_bits)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn explicit_output_asset_amount(index: u32) -> (u256, u64) {
|
|
26
|
+
let pair: (Asset1, Amount1) = unwrap(jet::output_amount(index));
|
|
27
|
+
let (asset, amount): (Asset1, Amount1) = pair;
|
|
28
|
+
let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset);
|
|
29
|
+
let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
|
|
30
|
+
(asset_bits, amount_bits)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn whitelist_owner_leaf(owner: Pubkey) -> u256 {
|
|
34
|
+
let tag_hash: u256 = 0x{{WHITELIST_OWNER_TAG_HASH}};
|
|
35
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_init();
|
|
36
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
|
|
37
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
|
|
38
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, owner);
|
|
39
|
+
jet::sha_256_ctx_8_finalize(ctx)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn whitelist_step(step: (bool, u256), node: u256) -> u256 {
|
|
43
|
+
let (sibling_on_left, sibling): (bool, u256) = step;
|
|
44
|
+
let tag_hash: u256 = 0x{{WHITELIST_NODE_TAG_HASH}};
|
|
45
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_init();
|
|
46
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
|
|
47
|
+
let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
|
|
48
|
+
let ctx: Ctx8 = match sibling_on_left {
|
|
49
|
+
true => jet::sha_256_ctx_8_add_32(ctx, sibling),
|
|
50
|
+
false => jet::sha_256_ctx_8_add_32(ctx, node),
|
|
51
|
+
};
|
|
52
|
+
let ctx: Ctx8 = match sibling_on_left {
|
|
53
|
+
true => jet::sha_256_ctx_8_add_32(ctx, node),
|
|
54
|
+
false => jet::sha_256_ctx_8_add_32(ctx, sibling),
|
|
55
|
+
};
|
|
56
|
+
jet::sha_256_ctx_8_finalize(ctx)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn require_whitelisted(owner: Pubkey, proof: [(bool, u256); {{WHITELIST_DEPTH}}]) {
|
|
60
|
+
let leaf: u256 = whitelist_owner_leaf(owner);
|
|
61
|
+
let root: u256 = array_fold::<whitelist_step, {{WHITELIST_DEPTH}}>(proof, leaf);
|
|
62
|
+
let expected_root: u256 = 0x{{WHITELIST_ROOT}};
|
|
63
|
+
assert!(jet::eq_256(root, expected_root));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn canonical_holder_script_hash(owner: Pubkey) -> u256 {
|
|
67
|
+
let holder_cmr: u256 = 0x{{HOLDER_PROGRAM_CMR}};
|
|
68
|
+
let holder_leaf: u256 = jet::build_tapleaf_simplicity(holder_cmr);
|
|
69
|
+
let tapdata_ctx: Ctx8 = jet::tapdata_init();
|
|
70
|
+
let tapdata_ctx: Ctx8 = jet::sha_256_ctx_8_add_32(tapdata_ctx, owner);
|
|
71
|
+
let owner_tapdata: u256 = jet::sha_256_ctx_8_finalize(tapdata_ctx);
|
|
72
|
+
let holder_root: u256 = jet::build_tapbranch(holder_leaf, owner_tapdata);
|
|
73
|
+
let holder_internal_key: Pubkey = 0x{{HOLDER_NUMS_INTERNAL_KEY}};
|
|
74
|
+
let output_key: u256 = jet::build_taptweak(holder_internal_key, holder_root);
|
|
75
|
+
|
|
76
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_init();
|
|
77
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(script_ctx, 0x51);
|
|
78
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(script_ctx, 0x20);
|
|
79
|
+
let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_32(script_ctx, output_key);
|
|
80
|
+
jet::sha_256_ctx_8_finalize(script_ctx)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fn next_index(index: u32) -> u32 {
|
|
84
|
+
let (carry, next): (bool, u32) = jet::add_32(index, 1);
|
|
85
|
+
assert!(not(carry));
|
|
86
|
+
next
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fn scan_input(unused: (), state: (u32, bool)) -> (u32, bool) {
|
|
90
|
+
let (index, found_strict): (u32, bool) = state;
|
|
91
|
+
let past_end: bool = jet::le_32(jet::num_inputs(), index);
|
|
92
|
+
let found_strict: bool = match past_end {
|
|
93
|
+
true => found_strict,
|
|
94
|
+
false => {
|
|
95
|
+
let (asset, amount): (u256, u64) = explicit_input_asset_amount(index);
|
|
96
|
+
let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
|
|
97
|
+
let is_positive: bool = not(jet::eq_64(amount, 0));
|
|
98
|
+
let is_strict: bool = jet::eq_256(asset, strict_asset);
|
|
99
|
+
match is_strict {
|
|
100
|
+
true => is_positive,
|
|
101
|
+
false => found_strict,
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
(next_index(index), found_strict)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fn scan_output_ordinary(
|
|
109
|
+
owner_proof: (Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]),
|
|
110
|
+
state: (u32, bool)
|
|
111
|
+
) -> (u32, bool) {
|
|
112
|
+
let (index, found_strict): (u32, bool) = state;
|
|
113
|
+
let past_end: bool = jet::le_32(jet::num_outputs(), index);
|
|
114
|
+
let found_strict: bool = match past_end {
|
|
115
|
+
true => found_strict,
|
|
116
|
+
false => {
|
|
117
|
+
let pair: (Asset1, Amount1) = unwrap(jet::output_amount(index));
|
|
118
|
+
let (asset, amount): (Asset1, Amount1) = pair;
|
|
119
|
+
let asset: u256 = unwrap_right::<(u1, u256)>(asset);
|
|
120
|
+
let unused_amount: u64 = unwrap_right::<(u1, u256)>(amount);
|
|
121
|
+
let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
|
|
122
|
+
match jet::eq_256(asset, strict_asset) {
|
|
123
|
+
true => {
|
|
124
|
+
let (owner, proof): (Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]) = owner_proof;
|
|
125
|
+
require_whitelisted(owner, proof);
|
|
126
|
+
let actual_script_hash: u256 = unwrap(jet::output_script_hash(index));
|
|
127
|
+
let expected_script_hash: u256 = canonical_holder_script_hash(owner);
|
|
128
|
+
assert!(jet::eq_256(actual_script_hash, expected_script_hash));
|
|
129
|
+
true
|
|
130
|
+
},
|
|
131
|
+
false => found_strict,
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
(next_index(index), found_strict)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fn require_verifier_successor(expected_script_hash: u256) {
|
|
139
|
+
let (verifier_asset, verifier_amount): (u256, u64) = explicit_input_asset_amount(0);
|
|
140
|
+
let expected_verifier_asset: u256 = 0x{{VERIFIER_ASSET_ID_JET_HEX}};
|
|
141
|
+
assert!(jet::eq_256(verifier_asset, expected_verifier_asset));
|
|
142
|
+
assert!(jet::eq_64(verifier_amount, {{VERIFIER_AMOUNT_ATOMIC}}));
|
|
143
|
+
|
|
144
|
+
let (successor_asset, successor_amount): (u256, u64) = explicit_output_asset_amount(0);
|
|
145
|
+
assert!(jet::eq_256(successor_asset, expected_verifier_asset));
|
|
146
|
+
assert!(jet::eq_64(successor_amount, {{VERIFIER_AMOUNT_ATOMIC}}));
|
|
147
|
+
let successor_script_hash: u256 = unwrap(jet::output_script_hash(0));
|
|
148
|
+
assert!(jet::eq_256(successor_script_hash, expected_script_hash))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn scan_output_redemption(unused: (), state: (u32, bool)) -> (u32, bool) {
|
|
152
|
+
let (index, found_strict): (u32, bool) = state;
|
|
153
|
+
let past_end: bool = jet::le_32(jet::num_outputs(), index);
|
|
154
|
+
let found_strict: bool = match past_end {
|
|
155
|
+
true => found_strict,
|
|
156
|
+
false => {
|
|
157
|
+
let (asset, unused_amount): (u256, u64) = explicit_output_asset_amount(index);
|
|
158
|
+
let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
|
|
159
|
+
match jet::eq_256(asset, strict_asset) {
|
|
160
|
+
true => {
|
|
161
|
+
let actual_script_hash: u256 = unwrap(jet::output_script_hash(index));
|
|
162
|
+
let redemption_script_hash: u256 = 0x{{REDEMPTION_SCRIPT_HASH}};
|
|
163
|
+
assert!(jet::eq_256(actual_script_hash, redemption_script_hash));
|
|
164
|
+
true
|
|
165
|
+
},
|
|
166
|
+
false => found_strict,
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
(next_index(index), found_strict)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fn strict_input_present() -> bool {
|
|
174
|
+
let input_slots: [(); {{MAX_INPUTS}}] = [{{INPUT_UNIT_SLOTS}}];
|
|
175
|
+
let input_state: (u32, bool) =
|
|
176
|
+
array_fold::<scan_input, {{MAX_INPUTS}}>(input_slots, (0, false));
|
|
177
|
+
let (unused_input_index, found_strict_input): (u32, bool) = input_state;
|
|
178
|
+
found_strict_input
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
fn ordinary_transfer(
|
|
182
|
+
output_owner_proofs: [(Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]); {{MAX_OUTPUTS}}]
|
|
183
|
+
) {
|
|
184
|
+
require_verifier_successor(jet::current_script_hash());
|
|
185
|
+
assert!(strict_input_present());
|
|
186
|
+
|
|
187
|
+
let output_state: (u32, bool) =
|
|
188
|
+
array_fold::<scan_output_ordinary, {{MAX_OUTPUTS}}>(output_owner_proofs, (0, false));
|
|
189
|
+
let (unused_output_index, found_strict_output): (u32, bool) = output_state;
|
|
190
|
+
assert!(found_strict_output)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
fn redemption(authority_signature: Signature) {
|
|
194
|
+
require_verifier_successor(jet::current_script_hash());
|
|
195
|
+
assert!(strict_input_present());
|
|
196
|
+
|
|
197
|
+
let output_slots: [(); {{MAX_OUTPUTS}}] = [{{OUTPUT_UNIT_SLOTS}}];
|
|
198
|
+
let output_state: (u32, bool) =
|
|
199
|
+
array_fold::<scan_output_redemption, {{MAX_OUTPUTS}}>(output_slots, (0, false));
|
|
200
|
+
let (unused_output_index, found_strict_output): (u32, bool) = output_state;
|
|
201
|
+
assert!(found_strict_output);
|
|
202
|
+
|
|
203
|
+
let authority: Pubkey = 0x{{AUTHORITY_XONLY}};
|
|
204
|
+
jet::bip_0340_verify((authority, jet::sig_all_hash()), authority_signature)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn governance(next: (u256, Signature)) {
|
|
208
|
+
let (next_verifier_script_hash, authority_signature): (u256, Signature) = next;
|
|
209
|
+
require_verifier_successor(next_verifier_script_hash);
|
|
210
|
+
assert!(not(strict_input_present()));
|
|
211
|
+
|
|
212
|
+
let output_slots: [(); {{MAX_OUTPUTS}}] = [{{OUTPUT_UNIT_SLOTS}}];
|
|
213
|
+
let output_state: (u32, bool) =
|
|
214
|
+
array_fold::<scan_output_redemption, {{MAX_OUTPUTS}}>(output_slots, (0, false));
|
|
215
|
+
let (unused_output_index, found_strict_output): (u32, bool) = output_state;
|
|
216
|
+
assert!(not(found_strict_output));
|
|
217
|
+
|
|
218
|
+
let authority: Pubkey = 0x{{AUTHORITY_XONLY}};
|
|
219
|
+
jet::bip_0340_verify((authority, jet::sig_all_hash()), authority_signature)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn main() {
|
|
223
|
+
assert!(jet::eq_32(jet::current_index(), 0));
|
|
224
|
+
assert!(jet::le_32(jet::num_inputs(), {{MAX_INPUTS}}));
|
|
225
|
+
assert!(jet::le_32(jet::num_outputs(), {{MAX_OUTPUTS}}));
|
|
226
|
+
|
|
227
|
+
let action:
|
|
228
|
+
Either<
|
|
229
|
+
[(Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]); {{MAX_OUTPUTS}}],
|
|
230
|
+
Either<Signature, (u256, Signature)>
|
|
231
|
+
> = witness::ACTION;
|
|
232
|
+
match action {
|
|
233
|
+
Left(output_owner_proofs : [(Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]); {{MAX_OUTPUTS}}]) =>
|
|
234
|
+
ordinary_transfer(output_owner_proofs),
|
|
235
|
+
Right(protected_action : Either<Signature, (u256, Signature)>) => match protected_action {
|
|
236
|
+
Left(authority_signature : Signature) => redemption(authority_signature),
|
|
237
|
+
Right(next : (u256, Signature)) => governance(next),
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
}
|