@xfuel/verify 0.3.5 → 0.4.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/LICENSE +201 -201
- package/README.md +82 -269
- package/cli.js +40 -0
- package/index.cjs +8 -0
- package/index.d.ts +5 -0
- package/index.js +7 -0
- package/package.json +61 -67
- package/dist/anchor-trust.d.ts +0 -129
- package/dist/anchor-trust.d.ts.map +0 -1
- package/dist/anchor-trust.js +0 -395
- package/dist/anchor-trust.js.map +0 -1
- package/dist/anchor-witness.d.ts +0 -265
- package/dist/anchor-witness.d.ts.map +0 -1
- package/dist/anchor-witness.js +0 -818
- package/dist/anchor-witness.js.map +0 -1
- package/dist/base-payer.d.ts +0 -65
- package/dist/base-payer.d.ts.map +0 -1
- package/dist/base-payer.js +0 -222
- package/dist/base-payer.js.map +0 -1
- package/dist/binding.d.ts +0 -52
- package/dist/binding.d.ts.map +0 -1
- package/dist/binding.js +0 -80
- package/dist/binding.js.map +0 -1
- package/dist/canonical-preimage.d.ts +0 -9
- package/dist/canonical-preimage.d.ts.map +0 -1
- package/dist/canonical-preimage.js +0 -25
- package/dist/canonical-preimage.js.map +0 -1
- package/dist/carry-forward.d.ts +0 -79
- package/dist/carry-forward.d.ts.map +0 -1
- package/dist/carry-forward.js +0 -184
- package/dist/carry-forward.js.map +0 -1
- package/dist/cli.d.ts +0 -19
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -1534
- package/dist/cli.js.map +0 -1
- package/dist/epoch.d.ts +0 -143
- package/dist/epoch.d.ts.map +0 -1
- package/dist/epoch.js +0 -352
- package/dist/epoch.js.map +0 -1
- package/dist/head-binding.d.ts +0 -24
- package/dist/head-binding.d.ts.map +0 -1
- package/dist/head-binding.js +0 -61
- package/dist/head-binding.js.map +0 -1
- package/dist/index.d.ts +0 -574
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -1627
- package/dist/index.js.map +0 -1
- package/dist/issuer-history.d.ts +0 -110
- package/dist/issuer-history.d.ts.map +0 -1
- package/dist/issuer-history.js +0 -348
- package/dist/issuer-history.js.map +0 -1
- package/dist/issuer-pin.d.ts +0 -126
- package/dist/issuer-pin.d.ts.map +0 -1
- package/dist/issuer-pin.js +0 -567
- package/dist/issuer-pin.js.map +0 -1
- package/dist/jcs.d.ts +0 -8
- package/dist/jcs.d.ts.map +0 -1
- package/dist/jcs.js +0 -64
- package/dist/jcs.js.map +0 -1
- package/dist/jws.d.ts +0 -46
- package/dist/jws.d.ts.map +0 -1
- package/dist/jws.js +0 -129
- package/dist/jws.js.map +0 -1
- package/dist/payer.d.ts +0 -65
- package/dist/payer.d.ts.map +0 -1
- package/dist/payer.js +0 -148
- package/dist/payer.js.map +0 -1
- package/dist/preimage.d.ts +0 -78
- package/dist/preimage.d.ts.map +0 -1
- package/dist/preimage.js +0 -363
- package/dist/preimage.js.map +0 -1
- package/dist/receipt-lane.d.ts +0 -235
- package/dist/receipt-lane.d.ts.map +0 -1
- package/dist/receipt-lane.js +0 -446
- package/dist/receipt-lane.js.map +0 -1
- package/dist/receipt-version.d.ts +0 -92
- package/dist/receipt-version.d.ts.map +0 -1
- package/dist/receipt-version.js +0 -323
- package/dist/receipt-version.js.map +0 -1
- package/dist/refusal.d.ts +0 -93
- package/dist/refusal.d.ts.map +0 -1
- package/dist/refusal.js +0 -249
- package/dist/refusal.js.map +0 -1
- package/dist/request-binding.d.ts +0 -7
- package/dist/request-binding.d.ts.map +0 -1
- package/dist/request-binding.js +0 -26
- package/dist/request-binding.js.map +0 -1
- package/dist/row-hash.d.ts +0 -22
- package/dist/row-hash.d.ts.map +0 -1
- package/dist/row-hash.js +0 -26
- package/dist/row-hash.js.map +0 -1
- package/dist/shell.d.ts +0 -65
- package/dist/shell.d.ts.map +0 -1
- package/dist/shell.js +0 -268
- package/dist/shell.js.map +0 -1
- package/dist/solana-payer.d.ts +0 -99
- package/dist/solana-payer.d.ts.map +0 -1
- package/dist/solana-payer.js +0 -258
- package/dist/solana-payer.js.map +0 -1
- package/dist/v11-receipt.d.ts +0 -23
- package/dist/v11-receipt.d.ts.map +0 -1
- package/dist/v11-receipt.js +0 -368
- package/dist/v11-receipt.js.map +0 -1
package/dist/cli.js
DELETED
|
@@ -1,1534 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* xfuel-verify CLI — Offline receipt verification.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* xfuel-verify receipt.json
|
|
8
|
-
* xfuel-verify receipt.json --check-nullifier
|
|
9
|
-
* xfuel-verify receipt.json --rpc https://mainnet.base.org
|
|
10
|
-
* xfuel-verify receipt.json inclusion.json head.json --rpc
|
|
11
|
-
* cat receipt.json | xfuel-verify -
|
|
12
|
-
*
|
|
13
|
-
* Exit codes:
|
|
14
|
-
* 0 = verified
|
|
15
|
-
* 1 = verification failed
|
|
16
|
-
* 2 = partial (binding ok, nullifier not checked or pending)
|
|
17
|
-
* 3 = input error
|
|
18
|
-
*/
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
const node_module_1 = require("node:module");
|
|
21
|
-
const node_fs_1 = require("node:fs");
|
|
22
|
-
const index_js_1 = require("./index.js");
|
|
23
|
-
const anchor_witness_js_1 = require("./anchor-witness.js");
|
|
24
|
-
const jws_js_1 = require("./jws.js");
|
|
25
|
-
const shell_js_1 = require("./shell.js");
|
|
26
|
-
const preimage_js_1 = require("./preimage.js");
|
|
27
|
-
const issuer_history_js_1 = require("./issuer-history.js");
|
|
28
|
-
const canonical_preimage_js_1 = require("./canonical-preimage.js");
|
|
29
|
-
const v11_receipt_js_1 = require("./v11-receipt.js");
|
|
30
|
-
const receipt_version_js_1 = require("./receipt-version.js");
|
|
31
|
-
const issuer_pin_js_1 = require("./issuer-pin.js");
|
|
32
|
-
const anchor_trust_js_1 = require("./anchor-trust.js");
|
|
33
|
-
const HELP = `
|
|
34
|
-
xfuel-verify — Offline verification for Chit402 receipts
|
|
35
|
-
|
|
36
|
-
Usage:
|
|
37
|
-
xfuel-verify <receipt.json> Verify binding locally
|
|
38
|
-
xfuel-verify <refusal.json> Verify a chit402.refusal.v1 document
|
|
39
|
-
(issuer refused; not a payment)
|
|
40
|
-
xfuel-verify <receipt.json> --jwks-file <jwks.json>
|
|
41
|
-
Verify issuer signature with a JWKS file
|
|
42
|
-
xfuel-verify <receipt.json> --fetch-jwks
|
|
43
|
-
Fetch JWKS from an allowlisted issuer jwks_uri
|
|
44
|
-
xfuel-verify <receipt.json> --check-payer
|
|
45
|
-
Confirm payer, payee, asset, and amount on-chain
|
|
46
|
-
xfuel-verify - < receipt.json Read from stdin
|
|
47
|
-
xfuel-verify --help Show this help
|
|
48
|
-
|
|
49
|
-
Options:
|
|
50
|
-
--jwks-file <path> JWKS file. Keys are trusted and matched by kid (no network)
|
|
51
|
-
--jwks-url <url> Fetch this https JWKS and trust it (explicit; any host)
|
|
52
|
-
--fetch-jwks Fetch receipt verification.jwks_uri when the host is allowlisted
|
|
53
|
-
(default allowlist: api.chit402.com)
|
|
54
|
-
--trusted-kid <kid> Replace the default offline pin with this RFC 7638 kid
|
|
55
|
-
(repeatable). Default pin: ${index_js_1.DEFAULT_TRUSTED_ISSUER_KIDS[0]}
|
|
56
|
-
--no-trusted-kid Do not use the default offline pin (JWKS only)
|
|
57
|
-
--check-nullifier Query Base RPC for nullifier anchor (requires network)
|
|
58
|
-
--check-payer Query Base or Solana RPC. Base confirms payer, payee, asset, amount
|
|
59
|
-
--solana-rpc <url> Solana RPC URL (default: https://api.mainnet-beta.solana.com or SOLANA_RPC_URL)
|
|
60
|
-
--rpc <url> Base RPC URL (default: https://mainnet.base.org)
|
|
61
|
-
--version Print @xfuel/verify version and exit
|
|
62
|
-
--rpc With a receipt, an inclusion proof, and a tree head: check the
|
|
63
|
-
leaf, then the Solana memo and the Base calldata for that root.
|
|
64
|
-
The tree head must carry a valid ES256 issuer signature, and
|
|
65
|
-
the anchor wallets must match the signed head.
|
|
66
|
-
A v2 head also needs the signed epoch record.
|
|
67
|
-
A tree-head file with --rpc enters this mode.
|
|
68
|
-
The same files without --rpc check the head signature and the
|
|
69
|
-
index/size-bound inclusion offline. They do not call chain RPC.
|
|
70
|
-
--epoch-record <f> Signed epoch record JSON. Skips the network fetch.
|
|
71
|
-
--epoch-url <url> GET this epoch record. Default: the receipt verify_url
|
|
72
|
-
origin plus /v1/receipts/tree/epoch
|
|
73
|
-
--inclusion <file> Inclusion proof JSON (chit402.inclusion.v1)
|
|
74
|
-
--head <file> Signed tree head JSON (chit402.tree_head.v1 or v2)
|
|
75
|
-
--anchor-wallets-file <path>
|
|
76
|
-
Signed chit402.anchor_wallets.v1 document. Wallets in a
|
|
77
|
-
verified document are added to the package pin. A head
|
|
78
|
-
that names any other wallet fails.
|
|
79
|
-
--carry-old-head <file>
|
|
80
|
-
--carry-old-inclusion <file>
|
|
81
|
-
--carry-head <file>
|
|
82
|
-
--carry-inclusion <file>
|
|
83
|
-
Earlier signed head, its inclusion proof, the current
|
|
84
|
-
signed head, and the current inclusion proof. Repeat
|
|
85
|
-
--carry-inclusion for each current index of the leaf.
|
|
86
|
-
The issued time and epoch are the earlier head's signed
|
|
87
|
-
claims. The logged time, epoch, and leaf index are the
|
|
88
|
-
current head's signed claims. A receipt in the current
|
|
89
|
-
epoch does not use these flags.
|
|
90
|
-
--json Output JSON instead of human-readable
|
|
91
|
-
--quiet Only output errors
|
|
92
|
-
--strict-issuer-history
|
|
93
|
-
Fail if the issuer key history cannot be fetched
|
|
94
|
-
--issuer-history-file <path>
|
|
95
|
-
Read issuer history JSON instead of fetching it
|
|
96
|
-
--canonical-preimage <path>
|
|
97
|
-
SHA-256 this file and match the signed payload_hash
|
|
98
|
-
--no-issuer-history Do not check the kid's not_before / not_after window
|
|
99
|
-
--no-preimage Do not require published hash preimages
|
|
100
|
-
|
|
101
|
-
Exit codes:
|
|
102
|
-
0 = verified
|
|
103
|
-
1 = verification failed
|
|
104
|
-
2 = partial verification
|
|
105
|
-
3 = input error
|
|
106
|
-
|
|
107
|
-
Trust:
|
|
108
|
-
A signature counts only when the verifying key is trusted. Trust is a JWKS
|
|
109
|
-
entry matched by kid (file, --jwks-url, or --fetch-jwks), or an embedded
|
|
110
|
-
issuer_jwk whose RFC 7638 thumbprint equals a pinned trusted kid. The
|
|
111
|
-
embedded key alone is not a trust root. Untrusted keys report "key untrusted".
|
|
112
|
-
|
|
113
|
-
Amount, payer, payee, and tx are read from verified signed claims. Every
|
|
114
|
-
signed field is compared with the outer receipt at the same path. A mismatch
|
|
115
|
-
fails. Outer fields the signature does not cover are printed as UNVERIFIED.
|
|
116
|
-
The outer document schema is not a copy of the JWS schema. Log mode (a
|
|
117
|
-
receipt, an inclusion proof, and a tree head) runs these receipt checks too.
|
|
118
|
-
|
|
119
|
-
Network behavior:
|
|
120
|
-
JWKS, nullifiers, and anchors are not fetched unless you pass a flag.
|
|
121
|
-
Issuer history is fetched by default when the receipt names a history URL
|
|
122
|
-
and neither --issuer-history-file nor --no-issuer-history is set. A payload
|
|
123
|
-
v10 receipt fails (exit 1) when that fetch cannot run. Offline mode is
|
|
124
|
-
--issuer-history-file <snapshot>, or --no-issuer-history to skip the window.
|
|
125
|
-
Other network uses:
|
|
126
|
-
- --fetch-jwks or --jwks-url
|
|
127
|
-
- --check-nullifier (Base RPC)
|
|
128
|
-
- --check-payer (Base or Solana RPC)
|
|
129
|
-
- --rpc with a receipt, an inclusion proof, and a tree head
|
|
130
|
-
- a v2 head, which fetches GET /v1/receipts/tree/epoch unless --epoch-record
|
|
131
|
-
|
|
132
|
-
Solana payer verify uses SOLANA_RPC_URL when set, else the public mainnet RPC.
|
|
133
|
-
|
|
134
|
-
Anchored root:
|
|
135
|
-
xfuel-verify receipt.json inclusion.json head.json --rpc
|
|
136
|
-
xfuel-verify receipt.json --inclusion inclusion.json --head head.json --rpc https://mainnet.base.org
|
|
137
|
-
|
|
138
|
-
Also runs the single-receipt checks (issuer signature and signed-versus-outer
|
|
139
|
-
comparison). Those must pass before Overall can be VERIFIED. Then it checks
|
|
140
|
-
the tree head's ES256 issuer signature (production pin, issuer
|
|
141
|
-
history, or JWKS, same rules as a receipt). The signed root, size, epoch,
|
|
142
|
-
and anchors must match the head. Then it checks Merkle inclusion, requires
|
|
143
|
-
the Solana memo and fee payer, and requires the Base calldata and sender.
|
|
144
|
-
The sender and fee payer must be on the issuer anchor-wallet list (the
|
|
145
|
-
package pin, plus a verified /.well-known/anchor-wallets.json or
|
|
146
|
-
issuer-history anchor_wallets list). The on-chain issuer-root registry and
|
|
147
|
-
the DNS anchor are not read. Prints what this proves and what it does not
|
|
148
|
-
prove. Exit 0 when the receipt checks pass and both chains match, 2 when the
|
|
149
|
-
leaf is included but an anchor is still pending, or when the leaf is past
|
|
150
|
-
the anchored tree (PENDING, not a tamper failure), and the receipt checks
|
|
151
|
-
did not fail, 1 when a check fails. A pending leaf must sit at an index
|
|
152
|
-
at or past the anchored size, and PENDING applies only when the receipt
|
|
153
|
-
itself verifies. A failed signature, field, or preimage is a failure.
|
|
154
|
-
With --rpc, a claimed anchor must name a Solana fee payer that is on the
|
|
155
|
-
anchor-wallet list, and the head root must equal the newest memo that
|
|
156
|
-
payer actually paid. A memo from any other payer is ignored.
|
|
157
|
-
|
|
158
|
-
A receipt or head with issuer_key_pin is a pinned era. Pass the pin file
|
|
159
|
-
and its commit SHA plus SHA-256. The verifier does not fetch a branch.
|
|
160
|
-
--issuer-pin <file> --issuer-pin-commit <40-hex> --issuer-pin-sha256 <64-hex>
|
|
161
|
-
Optional with that pin: --issuer-pin-sig, --issuer-witnesses,
|
|
162
|
-
--issuer-prior-pin, --issuer-prior-commit, --issuer-prior-sha256, --issuer-control.
|
|
163
|
-
Those flags do not start the check on their own. A prior pin does not
|
|
164
|
-
replace the published specimen key. Sepolia only (eip155:84532).
|
|
165
|
-
See docs/product/issuer-key-pin.md.
|
|
166
|
-
|
|
167
|
-
Receipt lane (unsigned, beside book_seq):
|
|
168
|
-
settled_by is observed_transfer when the USDC transfer was checked on Base
|
|
169
|
-
or Solana, and receipt when only the issuer asserts settlement. Unknown is
|
|
170
|
-
null. Ordering is seq + settled_by + (anchor_changed AND not settled).
|
|
171
|
-
Boundary: complete over registry marks, blind to payments the registry
|
|
172
|
-
never joined. freeze is true only when book_seq is set, settled_by is
|
|
173
|
-
receipt, the anchor changed after binding, and the row is not settled. An
|
|
174
|
-
anchor change alone does not freeze, and freeze does not change the
|
|
175
|
-
signature exit code. classification unverifiable_from_registry means the
|
|
176
|
-
binding is past expiry and settled_by, receipt_id, observed_tx_hash, and
|
|
177
|
-
observed_transfer_id are all null. That is not unpaid. local_check, when
|
|
178
|
-
set, is a Base USDC payee and amount a stranger can check. It does not
|
|
179
|
-
claim the row was paid. Design by Turbo on 1F916 (post 6579, comments
|
|
180
|
-
88201, 88403, and 88596).
|
|
181
|
-
|
|
182
|
-
Supported receipt versions:
|
|
183
|
-
${(0, receipt_version_js_1.formatSupportedReceiptVersions)().split('\n').join('\n ')}
|
|
184
|
-
|
|
185
|
-
Examples:
|
|
186
|
-
# Skip the issuer-history fetch. A v10 receipt otherwise needs that network call.
|
|
187
|
-
xfuel-verify my-receipt.json --no-issuer-history
|
|
188
|
-
|
|
189
|
-
# Verify issuer signature with JWKS file
|
|
190
|
-
xfuel-verify my-receipt.json --jwks-file issuer-jwks.json
|
|
191
|
-
|
|
192
|
-
# Full verification including on-chain nullifier
|
|
193
|
-
xfuel-verify my-receipt.json --jwks-file issuer-jwks.json --check-nullifier
|
|
194
|
-
|
|
195
|
-
# Pipe from curl
|
|
196
|
-
curl -s https://api.chit402.com/receipt/task-123?format=json | xfuel-verify -
|
|
197
|
-
|
|
198
|
-
# Public shell. INCLUDED_SHELL is not VERIFIED.
|
|
199
|
-
xfuel-verify shell.json
|
|
200
|
-
|
|
201
|
-
# Owner view. VERIFIED needs a trusted issuer key, an inclusion proof,
|
|
202
|
-
# and a signed payment. The key inside the holder is not enough.
|
|
203
|
-
xfuel-verify shell.json --jws owner-view.json --inclusion inclusion.json --head head.json
|
|
204
|
-
`;
|
|
205
|
-
function parseArgs(args) {
|
|
206
|
-
const result = {
|
|
207
|
-
file: null,
|
|
208
|
-
jwksFile: null,
|
|
209
|
-
jwksUrl: null,
|
|
210
|
-
fetchJwks: false,
|
|
211
|
-
trustedKids: null,
|
|
212
|
-
noTrustedKid: false,
|
|
213
|
-
checkNullifier: false,
|
|
214
|
-
checkPayer: false,
|
|
215
|
-
rpcUrl: null,
|
|
216
|
-
solanaRpcUrl: null,
|
|
217
|
-
sawRpc: false,
|
|
218
|
-
anchorFlag: false,
|
|
219
|
-
inclusionFile: null,
|
|
220
|
-
headFile: null,
|
|
221
|
-
epochRecordFile: null,
|
|
222
|
-
epochUrl: null,
|
|
223
|
-
carryOldHeadFile: null,
|
|
224
|
-
carryOldInclusionFile: null,
|
|
225
|
-
carryHeadFile: null,
|
|
226
|
-
carryInclusionFiles: [],
|
|
227
|
-
positionals: [],
|
|
228
|
-
json: false,
|
|
229
|
-
quiet: false,
|
|
230
|
-
help: false,
|
|
231
|
-
strictIssuerHistory: false,
|
|
232
|
-
issuerHistoryFile: null,
|
|
233
|
-
canonicalPreimageFile: null,
|
|
234
|
-
noIssuerHistory: false,
|
|
235
|
-
noPreimage: false,
|
|
236
|
-
issuerPinFile: null,
|
|
237
|
-
issuerPinCommit: null,
|
|
238
|
-
issuerPinSha256: null,
|
|
239
|
-
issuerPinSigFile: null,
|
|
240
|
-
issuerWitnessesFile: null,
|
|
241
|
-
issuerPriorPinFile: null,
|
|
242
|
-
issuerPriorCommit: null,
|
|
243
|
-
issuerPriorSha256: null,
|
|
244
|
-
issuerControlFile: null,
|
|
245
|
-
version: false,
|
|
246
|
-
anchorWalletsFile: null,
|
|
247
|
-
acceptShell: false,
|
|
248
|
-
jwsFile: null,
|
|
249
|
-
salt: null,
|
|
250
|
-
open: [],
|
|
251
|
-
url: null,
|
|
252
|
-
};
|
|
253
|
-
for (let i = 0; i < args.length; i++) {
|
|
254
|
-
const arg = args[i];
|
|
255
|
-
if (arg === '--version' || arg === '-V') {
|
|
256
|
-
result.version = true;
|
|
257
|
-
}
|
|
258
|
-
else if (arg === '--help' || arg === '-h') {
|
|
259
|
-
result.help = true;
|
|
260
|
-
}
|
|
261
|
-
else if (arg === '--check-nullifier') {
|
|
262
|
-
result.checkNullifier = true;
|
|
263
|
-
}
|
|
264
|
-
else if (arg === '--check-payer') {
|
|
265
|
-
result.checkPayer = true;
|
|
266
|
-
}
|
|
267
|
-
else if (arg === '--jwks-file' && args[i + 1]) {
|
|
268
|
-
result.jwksFile = args[++i];
|
|
269
|
-
}
|
|
270
|
-
else if (arg === '--jwks-url' && args[i + 1]) {
|
|
271
|
-
result.jwksUrl = args[++i];
|
|
272
|
-
}
|
|
273
|
-
else if (arg === '--fetch-jwks') {
|
|
274
|
-
result.fetchJwks = true;
|
|
275
|
-
}
|
|
276
|
-
else if (arg === '--trusted-kid' && args[i + 1]) {
|
|
277
|
-
result.trustedKids = result.trustedKids || [];
|
|
278
|
-
result.trustedKids.push(args[++i]);
|
|
279
|
-
}
|
|
280
|
-
else if (arg === '--no-trusted-kid') {
|
|
281
|
-
result.noTrustedKid = true;
|
|
282
|
-
}
|
|
283
|
-
else if (arg === '--rpc') {
|
|
284
|
-
result.sawRpc = true;
|
|
285
|
-
const next = args[i + 1];
|
|
286
|
-
if (next && /^https?:\/\//i.test(next))
|
|
287
|
-
result.rpcUrl = args[++i];
|
|
288
|
-
else
|
|
289
|
-
result.anchorFlag = true;
|
|
290
|
-
}
|
|
291
|
-
else if (arg === '--inclusion' && args[i + 1]) {
|
|
292
|
-
result.inclusionFile = args[++i];
|
|
293
|
-
}
|
|
294
|
-
else if (arg === '--head' && args[i + 1]) {
|
|
295
|
-
result.headFile = args[++i];
|
|
296
|
-
}
|
|
297
|
-
else if (arg === '--epoch-record' && args[i + 1]) {
|
|
298
|
-
result.epochRecordFile = args[++i];
|
|
299
|
-
}
|
|
300
|
-
else if (arg === '--epoch-url' && args[i + 1]) {
|
|
301
|
-
result.epochUrl = args[++i];
|
|
302
|
-
}
|
|
303
|
-
else if (arg === '--carry-old-head' && args[i + 1]) {
|
|
304
|
-
result.carryOldHeadFile = args[++i];
|
|
305
|
-
}
|
|
306
|
-
else if (arg === '--carry-old-inclusion' && args[i + 1]) {
|
|
307
|
-
result.carryOldInclusionFile = args[++i];
|
|
308
|
-
}
|
|
309
|
-
else if (arg === '--carry-head' && args[i + 1]) {
|
|
310
|
-
result.carryHeadFile = args[++i];
|
|
311
|
-
}
|
|
312
|
-
else if (arg === '--carry-inclusion' && args[i + 1]) {
|
|
313
|
-
result.carryInclusionFiles.push(args[++i]);
|
|
314
|
-
}
|
|
315
|
-
else if (arg === '--solana-rpc' && args[i + 1]) {
|
|
316
|
-
result.solanaRpcUrl = args[++i];
|
|
317
|
-
}
|
|
318
|
-
else if (arg === '--strict-issuer-history') {
|
|
319
|
-
result.strictIssuerHistory = true;
|
|
320
|
-
}
|
|
321
|
-
else if (arg === '--issuer-history-file' && args[i + 1]) {
|
|
322
|
-
result.issuerHistoryFile = args[++i];
|
|
323
|
-
}
|
|
324
|
-
else if (arg === '--salt' && args[i + 1]) {
|
|
325
|
-
result.salt = args[++i];
|
|
326
|
-
}
|
|
327
|
-
else if (arg === '--open' && args[i + 1]) {
|
|
328
|
-
result.open.push(args[++i]);
|
|
329
|
-
}
|
|
330
|
-
else if (arg === '--canonical-preimage' && args[i + 1]) {
|
|
331
|
-
result.canonicalPreimageFile = args[++i];
|
|
332
|
-
}
|
|
333
|
-
else if (arg === '--no-issuer-history') {
|
|
334
|
-
result.noIssuerHistory = true;
|
|
335
|
-
}
|
|
336
|
-
else if (arg === '--anchor-wallets-file' && args[i + 1]) {
|
|
337
|
-
result.anchorWalletsFile = args[++i];
|
|
338
|
-
}
|
|
339
|
-
else if (arg === '--no-preimage') {
|
|
340
|
-
result.noPreimage = true;
|
|
341
|
-
}
|
|
342
|
-
else if (arg === '--accept-shell') {
|
|
343
|
-
result.acceptShell = true;
|
|
344
|
-
}
|
|
345
|
-
else if (arg === '--jws' && args[i + 1]) {
|
|
346
|
-
result.jwsFile = args[++i];
|
|
347
|
-
}
|
|
348
|
-
else if (arg === '--url' && args[i + 1]) {
|
|
349
|
-
result.url = args[++i];
|
|
350
|
-
}
|
|
351
|
-
else if (arg === '--issuer-pin' && args[i + 1]) {
|
|
352
|
-
result.issuerPinFile = args[++i];
|
|
353
|
-
}
|
|
354
|
-
else if (arg === '--issuer-pin-commit' && args[i + 1]) {
|
|
355
|
-
result.issuerPinCommit = args[++i];
|
|
356
|
-
}
|
|
357
|
-
else if (arg === '--issuer-pin-sha256' && args[i + 1]) {
|
|
358
|
-
result.issuerPinSha256 = args[++i];
|
|
359
|
-
}
|
|
360
|
-
else if (arg === '--issuer-pin-sig' && args[i + 1]) {
|
|
361
|
-
result.issuerPinSigFile = args[++i];
|
|
362
|
-
}
|
|
363
|
-
else if (arg === '--issuer-witnesses' && args[i + 1]) {
|
|
364
|
-
result.issuerWitnessesFile = args[++i];
|
|
365
|
-
}
|
|
366
|
-
else if (arg === '--issuer-prior-pin' && args[i + 1]) {
|
|
367
|
-
result.issuerPriorPinFile = args[++i];
|
|
368
|
-
}
|
|
369
|
-
else if (arg === '--issuer-prior-commit' && args[i + 1]) {
|
|
370
|
-
result.issuerPriorCommit = args[++i];
|
|
371
|
-
}
|
|
372
|
-
else if (arg === '--issuer-prior-sha256' && args[i + 1]) {
|
|
373
|
-
result.issuerPriorSha256 = args[++i];
|
|
374
|
-
}
|
|
375
|
-
else if (arg === '--issuer-control' && args[i + 1]) {
|
|
376
|
-
result.issuerControlFile = args[++i];
|
|
377
|
-
}
|
|
378
|
-
else if (arg === '--json') {
|
|
379
|
-
result.json = true;
|
|
380
|
-
}
|
|
381
|
-
else if (arg === '--quiet' || arg === '-q') {
|
|
382
|
-
result.quiet = true;
|
|
383
|
-
}
|
|
384
|
-
else if (!arg.startsWith('-')) {
|
|
385
|
-
result.positionals.push(arg);
|
|
386
|
-
if (!result.file)
|
|
387
|
-
result.file = arg;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return result;
|
|
391
|
-
}
|
|
392
|
-
function readReceipt(file) {
|
|
393
|
-
let content;
|
|
394
|
-
if (file === '-') {
|
|
395
|
-
content = (0, node_fs_1.readFileSync)(0, 'utf8');
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
content = (0, node_fs_1.readFileSync)(file, 'utf8');
|
|
399
|
-
}
|
|
400
|
-
return JSON.parse(content);
|
|
401
|
-
}
|
|
402
|
-
function formatAmount(units) {
|
|
403
|
-
if (!units)
|
|
404
|
-
return '—';
|
|
405
|
-
const n = Number(units);
|
|
406
|
-
if (!Number.isFinite(n))
|
|
407
|
-
return units;
|
|
408
|
-
const usd = n / 1e6;
|
|
409
|
-
return `$${usd.toFixed(usd >= 0.01 ? 2 : 6)} (${units} units)`;
|
|
410
|
-
}
|
|
411
|
-
function readJson(file) {
|
|
412
|
-
const content = file === '-' ? (0, node_fs_1.readFileSync)(0, 'utf8') : (0, node_fs_1.readFileSync)(file, 'utf8');
|
|
413
|
-
return JSON.parse(content);
|
|
414
|
-
}
|
|
415
|
-
function headStatus(file) {
|
|
416
|
-
try {
|
|
417
|
-
const doc = readJson(file);
|
|
418
|
-
return typeof doc?.status === 'string' ? doc.status : null;
|
|
419
|
-
}
|
|
420
|
-
catch {
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
function printLane(lane) {
|
|
425
|
-
const bit = (value) => (value == null ? 'unknown' : (value ? 'true' : 'false'));
|
|
426
|
-
console.log(` Receipt lane (unsigned)`);
|
|
427
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
428
|
-
console.log(` book_seq: ${lane.book_seq ?? '—'}`);
|
|
429
|
-
console.log(` settled_by: ${lane.settled_by ?? 'null'}`);
|
|
430
|
-
console.log(` settled: ${bit(lane.settled)}`);
|
|
431
|
-
console.log(` anchor_changed_since_binding: ${bit(lane.anchor_changed_since_binding)}`);
|
|
432
|
-
console.log(` freeze: ${lane.freeze ? 'YES' : 'no'}`);
|
|
433
|
-
if (lane.reason)
|
|
434
|
-
console.log(` reason: ${lane.reason}`);
|
|
435
|
-
console.log(` classification:${lane.classification}`);
|
|
436
|
-
console.log(` ordering: ${lane.ordering}`);
|
|
437
|
-
console.log(` boundary: ${lane.boundary}`);
|
|
438
|
-
if (lane.local_check) {
|
|
439
|
-
console.log(` local_check: payee ${lane.local_check.payee} amount ${lane.local_check.amount_atomic} (not a payment)`);
|
|
440
|
-
}
|
|
441
|
-
console.log(` ${lane.rule}`);
|
|
442
|
-
console.log('');
|
|
443
|
-
}
|
|
444
|
-
function printAnchor(result, json, quiet) {
|
|
445
|
-
if (json) {
|
|
446
|
-
console.log(JSON.stringify(result, null, 2));
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
if (quiet && result.overall !== 'failed')
|
|
450
|
-
return;
|
|
451
|
-
const mark = (ok) => (ok ? '✓ YES' : '✗ NO');
|
|
452
|
-
console.log('');
|
|
453
|
-
console.log(' Anchored receipt root');
|
|
454
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
455
|
-
console.log(` Root: ${result.root || '—'}`);
|
|
456
|
-
console.log(` Head signature:${result.head_signature.valid ? ' ✓ YES' : ' ✗ NO'}${result.head_signature.trust ? ` (${result.head_signature.trust})` : ''}`);
|
|
457
|
-
if (result.head_signature.kid)
|
|
458
|
-
console.log(` Head kid: ${result.head_signature.kid}`);
|
|
459
|
-
if (result.head_signature.message)
|
|
460
|
-
console.log(` Head reason: ${result.head_signature.message}`);
|
|
461
|
-
const unlogged = result.inclusion.unlogged_reason ? ` unlogged:${result.inclusion.unlogged_reason}` : '';
|
|
462
|
-
if (result.overall === 'pending') {
|
|
463
|
-
console.log(` Inclusion: PENDING${result.inclusion.reason ? ` (${result.inclusion.reason})` : ''}`);
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
console.log(` Inclusion: ${mark(result.inclusion.valid)}${result.inclusion.reason ? ` (${result.inclusion.reason})` : ''}${unlogged}`);
|
|
467
|
-
}
|
|
468
|
-
console.log(` Leaf source: ${result.inclusion.leaf_source}`);
|
|
469
|
-
console.log(` Solana: ${result.solana.checked ? mark(result.solana.valid) : (result.solana.reason || 'not checked')}`);
|
|
470
|
-
if (result.solana.signature)
|
|
471
|
-
console.log(` Signature: ${result.solana.signature}`);
|
|
472
|
-
if (result.solana.slot != null)
|
|
473
|
-
console.log(` Slot: ${result.solana.slot}`);
|
|
474
|
-
if (result.solana.cluster)
|
|
475
|
-
console.log(` Cluster: ${result.solana.cluster}`);
|
|
476
|
-
if (result.solana.fee_payer)
|
|
477
|
-
console.log(` Fee payer: ${result.solana.fee_payer}`);
|
|
478
|
-
if (result.solana.memo)
|
|
479
|
-
console.log(` Memo: ${result.solana.memo}`);
|
|
480
|
-
if (result.solana.reason && !result.solana.valid)
|
|
481
|
-
console.log(` Solana reason: ${result.solana.reason}`);
|
|
482
|
-
if (result.issuer_pin?.checked) {
|
|
483
|
-
console.log(` Issuer pin: ${result.issuer_pin.ok ? '✓ YES' : '✗ NO'}${result.issuer_pin.code ? ` (${result.issuer_pin.code})` : ''}`);
|
|
484
|
-
}
|
|
485
|
-
console.log(` Base: ${result.base.checked ? mark(result.base.valid) : (result.base.reason || 'not checked')}`);
|
|
486
|
-
if (result.base.from)
|
|
487
|
-
console.log(` Base from: ${result.base.from}`);
|
|
488
|
-
if (result.base.tx)
|
|
489
|
-
console.log(` Base tx: ${result.base.tx}`);
|
|
490
|
-
if (result.base.chain_id != null)
|
|
491
|
-
console.log(` Chain id: ${result.base.chain_id}`);
|
|
492
|
-
if (result.base.reason && !result.base.valid)
|
|
493
|
-
console.log(` Base reason: ${result.base.reason}`);
|
|
494
|
-
console.log('');
|
|
495
|
-
console.log(' What this proves');
|
|
496
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
497
|
-
for (const line of result.proves)
|
|
498
|
-
console.log(` ${line}`);
|
|
499
|
-
console.log('');
|
|
500
|
-
console.log(' What this does not prove');
|
|
501
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
502
|
-
for (const line of result.does_not_prove)
|
|
503
|
-
console.log(` ${line}`);
|
|
504
|
-
console.log('');
|
|
505
|
-
if (result.overall === 'pending') {
|
|
506
|
-
console.log(` Anchored size: ${result.inclusion.anchored_tree_size ?? '—'}`);
|
|
507
|
-
console.log(` Live size: ${result.inclusion.live_tree_size ?? '—'}`);
|
|
508
|
-
}
|
|
509
|
-
console.log(` Overall: ${result.overall.toUpperCase()}`);
|
|
510
|
-
if (result.errors.length > 0)
|
|
511
|
-
console.log(` Errors: ${result.errors.join(', ')}`);
|
|
512
|
-
if (result.errors.includes(anchor_trust_js_1.LEGACY_HEAD_UNPINNED_SIGNER)) {
|
|
513
|
-
console.log(` ${anchor_trust_js_1.HEAD_TRUST_MESSAGES[anchor_trust_js_1.LEGACY_HEAD_UNPINNED_SIGNER]}`);
|
|
514
|
-
}
|
|
515
|
-
console.log('');
|
|
516
|
-
}
|
|
517
|
-
function headNeedsEpochRecord(head) {
|
|
518
|
-
const schema = head.schema;
|
|
519
|
-
const version = head.payload_version;
|
|
520
|
-
return head.epoch != null || schema === 'chit402.tree_head.v2' || version === 2;
|
|
521
|
-
}
|
|
522
|
-
function epochUrlFromReceipt(receipt) {
|
|
523
|
-
const raw = receipt?.verify_url;
|
|
524
|
-
if (!raw)
|
|
525
|
-
return null;
|
|
526
|
-
try {
|
|
527
|
-
return `${new URL(raw).origin}/v1/receipts/tree/epoch`;
|
|
528
|
-
}
|
|
529
|
-
catch {
|
|
530
|
-
return null;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
async function loadEpochRecord(args, receipt) {
|
|
534
|
-
if (args.epochRecordFile) {
|
|
535
|
-
return JSON.parse((0, node_fs_1.readFileSync)(args.epochRecordFile, 'utf8'));
|
|
536
|
-
}
|
|
537
|
-
const url = args.epochUrl || epochUrlFromReceipt(receipt);
|
|
538
|
-
if (!url)
|
|
539
|
-
return null;
|
|
540
|
-
const res = await fetch(url);
|
|
541
|
-
if (!res.ok)
|
|
542
|
-
return null;
|
|
543
|
-
const body = await res.json();
|
|
544
|
-
if (!body?.issuer_signature?.jws)
|
|
545
|
-
return null;
|
|
546
|
-
return body;
|
|
547
|
-
}
|
|
548
|
-
function epochSignatureOk(record, jws, jwks, trustedKids) {
|
|
549
|
-
const sig = record.issuer_signature;
|
|
550
|
-
const embedded = sig && 'issuer_jwk' in sig
|
|
551
|
-
? sig.issuer_jwk
|
|
552
|
-
: undefined;
|
|
553
|
-
const kid = (0, jws_js_1.readJwsHeader)(jws)?.kid;
|
|
554
|
-
const keys = [];
|
|
555
|
-
for (const key of jwks?.keys || []) {
|
|
556
|
-
if (!key || key.kty !== 'EC')
|
|
557
|
-
continue;
|
|
558
|
-
const jwk = key;
|
|
559
|
-
if (kid && jwk.kid !== kid)
|
|
560
|
-
continue;
|
|
561
|
-
keys.push(jwk);
|
|
562
|
-
}
|
|
563
|
-
if (embedded && trustedKids?.includes((0, jws_js_1.jwkThumbprint)(embedded)))
|
|
564
|
-
keys.push(embedded);
|
|
565
|
-
for (const key of keys) {
|
|
566
|
-
const verified = (0, jws_js_1.verifyIssuerJws)(jws, key);
|
|
567
|
-
if (!verified.valid || !verified.payload)
|
|
568
|
-
continue;
|
|
569
|
-
const payload = verified.payload;
|
|
570
|
-
if (JSON.stringify(payload.epochs ?? null) !== JSON.stringify(record.epochs ?? null))
|
|
571
|
-
return false;
|
|
572
|
-
if (JSON.stringify(payload.orphans ?? []) !== JSON.stringify(record.orphans ?? []))
|
|
573
|
-
return false;
|
|
574
|
-
const payloadUnlogged = payload.unlogged === undefined ? null : payload.unlogged;
|
|
575
|
-
const recordUnlogged = record.unlogged === undefined ? null : record.unlogged;
|
|
576
|
-
if (JSON.stringify(payloadUnlogged) !== JSON.stringify(recordUnlogged))
|
|
577
|
-
return false;
|
|
578
|
-
return true;
|
|
579
|
-
}
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
|
-
function packageVersion() {
|
|
583
|
-
const require = (0, node_module_1.createRequire)(__filename);
|
|
584
|
-
const pkg = require('../package.json');
|
|
585
|
-
return pkg.version || '0.0.0';
|
|
586
|
-
}
|
|
587
|
-
function looksLikeTreeHead(value) {
|
|
588
|
-
if (!value || typeof value !== 'object')
|
|
589
|
-
return false;
|
|
590
|
-
const doc = value;
|
|
591
|
-
if (doc.status === 'not_yet_published')
|
|
592
|
-
return true;
|
|
593
|
-
if (typeof doc.schema === 'string' && doc.schema.startsWith('chit402.tree_head.'))
|
|
594
|
-
return true;
|
|
595
|
-
return Boolean(doc.anchors && doc.root && !doc.task_id);
|
|
596
|
-
}
|
|
597
|
-
function readOfflineWitness(args) {
|
|
598
|
-
let head = null;
|
|
599
|
-
let inclusion = null;
|
|
600
|
-
for (const file of args.positionals.slice(1)) {
|
|
601
|
-
let doc;
|
|
602
|
-
try {
|
|
603
|
-
doc = readJson(file);
|
|
604
|
-
}
|
|
605
|
-
catch (err) {
|
|
606
|
-
throw new Error(`Error reading ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
607
|
-
}
|
|
608
|
-
const row = doc;
|
|
609
|
-
if (looksLikeTreeHead(doc))
|
|
610
|
-
head = doc;
|
|
611
|
-
else if (doc && typeof doc === 'object' && (Array.isArray(row.proof) || row.leaf_index != null)) {
|
|
612
|
-
inclusion = doc;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return { head, inclusion };
|
|
616
|
-
}
|
|
617
|
-
function fileLooksLikeHead(file) {
|
|
618
|
-
if (!file)
|
|
619
|
-
return false;
|
|
620
|
-
try {
|
|
621
|
-
return looksLikeTreeHead(readJson(file));
|
|
622
|
-
}
|
|
623
|
-
catch {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function historyHostAllowed(url) {
|
|
628
|
-
try {
|
|
629
|
-
const parsed = new URL(url);
|
|
630
|
-
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')
|
|
631
|
-
return false;
|
|
632
|
-
if (parsed.protocol === 'http:' && parsed.hostname !== '127.0.0.1' && parsed.hostname !== 'localhost')
|
|
633
|
-
return false;
|
|
634
|
-
if (parsed.protocol === 'https:') {
|
|
635
|
-
return index_js_1.DEFAULT_TRUSTED_JWKS_HOSTS.some((host) => host.toLowerCase() === parsed.hostname.toLowerCase());
|
|
636
|
-
}
|
|
637
|
-
return true;
|
|
638
|
-
}
|
|
639
|
-
catch {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
async function loadAnchorIssuerHistory(args, receipt) {
|
|
644
|
-
if (args.issuerHistoryFile) {
|
|
645
|
-
return JSON.parse((0, node_fs_1.readFileSync)(args.issuerHistoryFile, 'utf8'));
|
|
646
|
-
}
|
|
647
|
-
if (args.noIssuerHistory)
|
|
648
|
-
return null;
|
|
649
|
-
const url = (0, issuer_history_js_1.historyUrlFromReceipt)(receipt);
|
|
650
|
-
if (!url || !historyHostAllowed(url))
|
|
651
|
-
return null;
|
|
652
|
-
try {
|
|
653
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(4000) });
|
|
654
|
-
if (!res.ok)
|
|
655
|
-
return null;
|
|
656
|
-
return await res.json();
|
|
657
|
-
}
|
|
658
|
-
catch {
|
|
659
|
-
return null;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
async function loadAnchorWalletDocument(args, receipt) {
|
|
663
|
-
if (args.anchorWalletsFile) {
|
|
664
|
-
return JSON.parse((0, node_fs_1.readFileSync)(args.anchorWalletsFile, 'utf8'));
|
|
665
|
-
}
|
|
666
|
-
if (args.noIssuerHistory)
|
|
667
|
-
return null;
|
|
668
|
-
const historyUrl = (0, issuer_history_js_1.historyUrlFromReceipt)(receipt);
|
|
669
|
-
const epochOrigin = (() => {
|
|
670
|
-
if (!args.epochUrl)
|
|
671
|
-
return null;
|
|
672
|
-
try {
|
|
673
|
-
return new URL(args.epochUrl).origin;
|
|
674
|
-
}
|
|
675
|
-
catch {
|
|
676
|
-
return null;
|
|
677
|
-
}
|
|
678
|
-
})();
|
|
679
|
-
const origin = epochOrigin || (historyUrl ? new URL(historyUrl).origin : null);
|
|
680
|
-
if (!origin)
|
|
681
|
-
return null;
|
|
682
|
-
const url = `${origin}/.well-known/anchor-wallets.json`;
|
|
683
|
-
if (!historyHostAllowed(url))
|
|
684
|
-
return null;
|
|
685
|
-
try {
|
|
686
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(4000) });
|
|
687
|
-
if (res.status === 404)
|
|
688
|
-
return null;
|
|
689
|
-
if (!res.ok)
|
|
690
|
-
return null;
|
|
691
|
-
return await res.json();
|
|
692
|
-
}
|
|
693
|
-
catch {
|
|
694
|
-
return null;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
function emitVersionFailure(args, errors) {
|
|
698
|
-
const reason = errors[0] || 'unsupported_version';
|
|
699
|
-
if (args.json) {
|
|
700
|
-
console.log(JSON.stringify({
|
|
701
|
-
overall: 'failed',
|
|
702
|
-
errors,
|
|
703
|
-
issuer_signature: { valid: false, checked: true, key_trusted: false, reason },
|
|
704
|
-
amount_usdc: null,
|
|
705
|
-
tx: null,
|
|
706
|
-
payer: { checked: false, valid: false },
|
|
707
|
-
}, null, 2));
|
|
708
|
-
}
|
|
709
|
-
else {
|
|
710
|
-
console.log(reason);
|
|
711
|
-
console.log(' Overall: FAILED');
|
|
712
|
-
}
|
|
713
|
-
return 1;
|
|
714
|
-
}
|
|
715
|
-
async function runAnchor(args) {
|
|
716
|
-
const receiptPath = args.file;
|
|
717
|
-
const inclusionPath = args.inclusionFile || args.positionals[1] || null;
|
|
718
|
-
const headPath = args.headFile || (args.inclusionFile ? null : args.positionals[2]) || null;
|
|
719
|
-
const headCandidates = [args.headFile, headPath, ...args.positionals];
|
|
720
|
-
if (headCandidates.some((file) => file && headStatus(file) === 'not_yet_published')) {
|
|
721
|
-
console.error('Tree head is not_yet_published.');
|
|
722
|
-
return 3;
|
|
723
|
-
}
|
|
724
|
-
if (!receiptPath || !inclusionPath || !headPath) {
|
|
725
|
-
console.error('Anchor check needs a receipt, an inclusion proof, and a tree head.');
|
|
726
|
-
console.error(' xfuel-verify receipt.json inclusion.json head.json --rpc');
|
|
727
|
-
return 3;
|
|
728
|
-
}
|
|
729
|
-
let receipt;
|
|
730
|
-
let inclusion;
|
|
731
|
-
let head;
|
|
732
|
-
try {
|
|
733
|
-
receipt = readJson(receiptPath);
|
|
734
|
-
inclusion = readJson(inclusionPath);
|
|
735
|
-
head = readJson(headPath);
|
|
736
|
-
}
|
|
737
|
-
catch (err) {
|
|
738
|
-
console.error(`Error reading anchor inputs: ${err instanceof Error ? err.message : String(err)}`);
|
|
739
|
-
return 3;
|
|
740
|
-
}
|
|
741
|
-
const anchorGate = (0, receipt_version_js_1.classifyReceiptDocument)(receipt);
|
|
742
|
-
if (!anchorGate.ok) {
|
|
743
|
-
if (args.json) {
|
|
744
|
-
console.log(JSON.stringify({
|
|
745
|
-
overall: 'failed',
|
|
746
|
-
errors: anchorGate.errors,
|
|
747
|
-
receipt_check: { overall: 'failed', errors: anchorGate.errors },
|
|
748
|
-
}, null, 2));
|
|
749
|
-
}
|
|
750
|
-
else {
|
|
751
|
-
console.log(anchorGate.errors[0]);
|
|
752
|
-
console.log(' Overall: FAILED');
|
|
753
|
-
}
|
|
754
|
-
return 1;
|
|
755
|
-
}
|
|
756
|
-
let fileJwks;
|
|
757
|
-
if (args.jwksFile) {
|
|
758
|
-
try {
|
|
759
|
-
fileJwks = JSON.parse((0, node_fs_1.readFileSync)(args.jwksFile, 'utf8'));
|
|
760
|
-
}
|
|
761
|
-
catch (err) {
|
|
762
|
-
console.error(`Error reading JWKS file: ${err instanceof Error ? err.message : String(err)}`);
|
|
763
|
-
return 3;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
const loaded = await (0, index_js_1.loadIssuerJwks)(receipt, {
|
|
767
|
-
jwks: fileJwks,
|
|
768
|
-
jwksUri: args.jwksUrl || undefined,
|
|
769
|
-
fetchJwks: args.fetchJwks,
|
|
770
|
-
});
|
|
771
|
-
const trustedKids = args.noTrustedKid
|
|
772
|
-
? []
|
|
773
|
-
: (args.trustedKids ?? [...index_js_1.DEFAULT_TRUSTED_ISSUER_KIDS]);
|
|
774
|
-
let issuerHistory = null;
|
|
775
|
-
let anchorWalletDocument = null;
|
|
776
|
-
try {
|
|
777
|
-
issuerHistory = await loadAnchorIssuerHistory(args, receipt);
|
|
778
|
-
anchorWalletDocument = await loadAnchorWalletDocument(args, receipt);
|
|
779
|
-
}
|
|
780
|
-
catch (err) {
|
|
781
|
-
console.error(`Error reading issuer trust files: ${err instanceof Error ? err.message : String(err)}`);
|
|
782
|
-
return 3;
|
|
783
|
-
}
|
|
784
|
-
let epochRecord = null;
|
|
785
|
-
let verifyEpochSignature;
|
|
786
|
-
if (headNeedsEpochRecord(head)) {
|
|
787
|
-
try {
|
|
788
|
-
epochRecord = await loadEpochRecord(args, receipt);
|
|
789
|
-
}
|
|
790
|
-
catch {
|
|
791
|
-
epochRecord = null;
|
|
792
|
-
}
|
|
793
|
-
if (epochRecord?.issuer_signature?.jws) {
|
|
794
|
-
verifyEpochSignature = (jws) => epochSignatureOk(epochRecord, jws, loaded.jwks, trustedKids);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
let issuerPin;
|
|
798
|
-
const pinClaim = (0, issuer_pin_js_1.readIssuerPinClaim)(receipt).claimed || (0, issuer_pin_js_1.readIssuerPinClaim)(head).claimed;
|
|
799
|
-
const pinRequested = Boolean(args.issuerPinFile) || pinClaim;
|
|
800
|
-
if (pinRequested) {
|
|
801
|
-
try {
|
|
802
|
-
const claimRef = (0, issuer_pin_js_1.readIssuerPinClaim)(receipt).ref || (0, issuer_pin_js_1.readIssuerPinClaim)(head).ref;
|
|
803
|
-
const ref = (args.issuerPinCommit || args.issuerPinSha256 || claimRef)
|
|
804
|
-
? {
|
|
805
|
-
commit: args.issuerPinCommit || claimRef?.commit || '',
|
|
806
|
-
path: claimRef?.path || 'docs/well-known/issuer-key.json',
|
|
807
|
-
sha256: (args.issuerPinSha256 || claimRef?.sha256 || '').toLowerCase(),
|
|
808
|
-
}
|
|
809
|
-
: null;
|
|
810
|
-
let witnesses;
|
|
811
|
-
if (args.issuerWitnessesFile)
|
|
812
|
-
witnesses = JSON.parse((0, node_fs_1.readFileSync)(args.issuerWitnessesFile, 'utf8'));
|
|
813
|
-
const priorRef = args.issuerPriorPinFile
|
|
814
|
-
? {
|
|
815
|
-
commit: args.issuerPriorCommit || '',
|
|
816
|
-
path: 'docs/well-known/issuer-key.json',
|
|
817
|
-
sha256: (args.issuerPriorSha256 || '').toLowerCase(),
|
|
818
|
-
}
|
|
819
|
-
: null;
|
|
820
|
-
issuerPin = {
|
|
821
|
-
pinBytes: args.issuerPinFile ? (0, node_fs_1.readFileSync)(args.issuerPinFile, 'utf8') : null,
|
|
822
|
-
sigBytes: args.issuerPinSigFile ? (0, node_fs_1.readFileSync)(args.issuerPinSigFile, 'utf8') : null,
|
|
823
|
-
ref,
|
|
824
|
-
witnesses,
|
|
825
|
-
priorPinBytes: args.issuerPriorPinFile ? (0, node_fs_1.readFileSync)(args.issuerPriorPinFile, 'utf8') : null,
|
|
826
|
-
priorRef,
|
|
827
|
-
controlJws: args.issuerControlFile ? (0, node_fs_1.readFileSync)(args.issuerControlFile, 'utf8').trim() : null,
|
|
828
|
-
required: true,
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
|
-
catch (err) {
|
|
832
|
-
console.error(`Error reading issuer pin: ${err instanceof Error ? err.message : String(err)}`);
|
|
833
|
-
return 3;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
const result = await (0, anchor_witness_js_1.verifyAnchoredRoot)({
|
|
837
|
-
receipt,
|
|
838
|
-
inclusion,
|
|
839
|
-
head,
|
|
840
|
-
baseRpcUrl: args.rpcUrl || undefined,
|
|
841
|
-
solanaRpcUrl: args.solanaRpcUrl || undefined,
|
|
842
|
-
checkNewestAnchor: args.sawRpc,
|
|
843
|
-
epochRecord,
|
|
844
|
-
verifyEpochSignature,
|
|
845
|
-
issuerPin,
|
|
846
|
-
jwks: loaded.jwks,
|
|
847
|
-
trustedKids,
|
|
848
|
-
issuerHistory,
|
|
849
|
-
strictIssuerHistory: args.strictIssuerHistory,
|
|
850
|
-
anchorWalletDocument: anchorWalletDocument,
|
|
851
|
-
});
|
|
852
|
-
let canonicalPreimage = null;
|
|
853
|
-
if (args.canonicalPreimageFile) {
|
|
854
|
-
canonicalPreimage = (0, node_fs_1.readFileSync)(args.canonicalPreimageFile, 'utf8');
|
|
855
|
-
}
|
|
856
|
-
const historyBytes = args.issuerHistoryFile ? (0, node_fs_1.readFileSync)(args.issuerHistoryFile, 'utf8') : null;
|
|
857
|
-
const verified = await (0, index_js_1.verifyReceipt)(receipt, {
|
|
858
|
-
jwks: loaded.jwks,
|
|
859
|
-
jwksUri: args.jwksUrl || undefined,
|
|
860
|
-
fetchJwks: args.fetchJwks,
|
|
861
|
-
trustedKids,
|
|
862
|
-
checkNullifier: args.checkNullifier,
|
|
863
|
-
checkPayer: args.checkPayer,
|
|
864
|
-
rpcUrl: args.rpcUrl || undefined,
|
|
865
|
-
solanaRpcUrl: args.solanaRpcUrl || undefined,
|
|
866
|
-
requirePreimages: !args.noPreimage,
|
|
867
|
-
issuerHistory: args.issuerHistoryFile ? issuerHistory : null,
|
|
868
|
-
issuerHistoryBytes: historyBytes,
|
|
869
|
-
fetchIssuerHistory: !args.noIssuerHistory && !args.issuerHistoryFile,
|
|
870
|
-
strictIssuerHistory: args.strictIssuerHistory,
|
|
871
|
-
skipIssuerHistory: args.noIssuerHistory,
|
|
872
|
-
canonicalPreimage,
|
|
873
|
-
head,
|
|
874
|
-
});
|
|
875
|
-
// Inclusion was already checked above. Passing it again re-derives the leaf
|
|
876
|
-
// from book_chain.row_hash only, which rejects a receipt that stores row_hash
|
|
877
|
-
// at the top level. Log mode still requires the receipt itself. A missing or
|
|
878
|
-
// stripped issuer signature, a partial check, or a signed-versus-outer
|
|
879
|
-
// mismatch fails the combined result. An inclusion match is not VERIFIED.
|
|
880
|
-
const issuerSig = receipt.issuer_signature;
|
|
881
|
-
const signaturePresent = Boolean(issuerSig?.jws || issuerSig?.value);
|
|
882
|
-
const receiptVerified = signaturePresent
|
|
883
|
-
&& verified.issuer_signature.valid === true
|
|
884
|
-
&& (verified.overall === 'verified' || verified.overall === 'verified_carried_forward')
|
|
885
|
-
&& verified.claim_mismatches.length === 0;
|
|
886
|
-
const receiptFailed = !receiptVerified;
|
|
887
|
-
const receiptErrors = [...verified.errors];
|
|
888
|
-
if (!signaturePresent) {
|
|
889
|
-
receiptErrors.push(verified.issuer_signature.reason || 'receipt_signature_missing');
|
|
890
|
-
}
|
|
891
|
-
else if (verified.overall === 'partial') {
|
|
892
|
-
receiptErrors.push(verified.issuer_signature.reason || 'receipt_check_partial');
|
|
893
|
-
}
|
|
894
|
-
const errors = receiptFailed
|
|
895
|
-
? [...result.errors, ...receiptErrors.filter((err) => !result.errors.includes(err))]
|
|
896
|
-
: result.errors;
|
|
897
|
-
const combined = {
|
|
898
|
-
...result,
|
|
899
|
-
errors,
|
|
900
|
-
overall: receiptFailed ? 'failed' : result.overall,
|
|
901
|
-
receipt_lane: verified.receipt_lane,
|
|
902
|
-
receipt_check: {
|
|
903
|
-
overall: result.overall === 'pending' && !receiptFailed ? 'pending' : verified.overall,
|
|
904
|
-
verified_scope: verified.verified_scope,
|
|
905
|
-
claim_mismatches: verified.claim_mismatches,
|
|
906
|
-
unsigned_fields: verified.unsigned_fields,
|
|
907
|
-
errors: verified.errors,
|
|
908
|
-
},
|
|
909
|
-
};
|
|
910
|
-
if (args.json) {
|
|
911
|
-
console.log(JSON.stringify(combined, null, 2));
|
|
912
|
-
}
|
|
913
|
-
else {
|
|
914
|
-
printAnchor(combined, false, args.quiet);
|
|
915
|
-
if (!args.quiet || receiptFailed) {
|
|
916
|
-
const anchorPending = result.overall === 'pending';
|
|
917
|
-
const receiptLabel = anchorPending && !receiptFailed
|
|
918
|
-
? 'PENDING'
|
|
919
|
-
: verified.overall.toUpperCase();
|
|
920
|
-
console.log(` Receipt checks: ${receiptLabel} (signed claims only)`);
|
|
921
|
-
if (verified.claim_mismatches.length > 0) {
|
|
922
|
-
for (const mismatch of verified.claim_mismatches) {
|
|
923
|
-
console.log(` ${mismatch.field}: outer ${mismatch.outer} ≠ signed ${mismatch.signed}`);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
if (!(anchorPending && !receiptFailed))
|
|
927
|
-
printUnsigned(verified.unsigned_fields);
|
|
928
|
-
console.log('');
|
|
929
|
-
}
|
|
930
|
-
if (!args.quiet)
|
|
931
|
-
printLane(verified.receipt_lane);
|
|
932
|
-
}
|
|
933
|
-
if (combined.overall === 'verified')
|
|
934
|
-
return 0;
|
|
935
|
-
if (combined.overall === 'partial' || combined.overall === 'pending')
|
|
936
|
-
return 2;
|
|
937
|
-
return 1;
|
|
938
|
-
}
|
|
939
|
-
function printUnsigned(fields) {
|
|
940
|
-
console.log(' Scope: signed claims only. Unsigned fields are UNVERIFIED.');
|
|
941
|
-
if (fields.length === 0)
|
|
942
|
-
return;
|
|
943
|
-
console.log(` Unsigned fields (${fields.length})`);
|
|
944
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
945
|
-
for (const field of fields)
|
|
946
|
-
console.log(` UNVERIFIED ${field}`);
|
|
947
|
-
}
|
|
948
|
-
async function runRefusal(doc, args) {
|
|
949
|
-
const trustedKids = args.trustedKids ?? index_js_1.DEFAULT_TRUSTED_ISSUER_KIDS;
|
|
950
|
-
const refusalGate = (0, receipt_version_js_1.classifyReceiptDocument)(doc);
|
|
951
|
-
if (!refusalGate.ok)
|
|
952
|
-
return emitVersionFailure(args, refusalGate.errors);
|
|
953
|
-
const result = (0, index_js_1.verifyRefusal)(doc, {
|
|
954
|
-
jwks: args.jwks,
|
|
955
|
-
trustedKids,
|
|
956
|
-
});
|
|
957
|
-
if (!result.valid && (result.errors[0] === 'unsupported_version' || result.reason === 'unsupported_version' || result.reason === 'payload_version_mismatch')) {
|
|
958
|
-
return emitVersionFailure(args, result.errors.length ? result.errors : [result.reason || 'unsupported_version']);
|
|
959
|
-
}
|
|
960
|
-
const preimages = await (0, preimage_js_1.verifyPublishedPreimages)(doc, {
|
|
961
|
-
requirePreimages: args.requirePreimages,
|
|
962
|
-
});
|
|
963
|
-
let canonicalFailed = false;
|
|
964
|
-
if (args.canonicalPreimage != null) {
|
|
965
|
-
const canonical = (0, canonical_preimage_js_1.verifyCanonicalPreimageBytes)(args.canonicalPreimage, result.payload_hash);
|
|
966
|
-
canonicalFailed = !canonical.ok;
|
|
967
|
-
if (canonicalFailed)
|
|
968
|
-
preimages.errors.push(`canonical preimage: ${canonical.reason}`);
|
|
969
|
-
if (canonicalFailed)
|
|
970
|
-
preimages.ok = false;
|
|
971
|
-
}
|
|
972
|
-
const pin = result.valid ? (0, issuer_history_js_1.readIssuerHistoryPin)({
|
|
973
|
-
issuer_history: result.issuer_history,
|
|
974
|
-
}) : null;
|
|
975
|
-
const history = args.skipIssuerHistory
|
|
976
|
-
? { checked: false, ok: true, unreachable: false, warning: null, reason: null, kid: result.kid ?? null, document: null, loaded: false }
|
|
977
|
-
: await (0, issuer_history_js_1.checkReceiptIssuerHistory)(doc, {
|
|
978
|
-
document: args.issuerHistory,
|
|
979
|
-
documentBytes: args.issuerHistoryBytes,
|
|
980
|
-
fetchHistory: args.fetchIssuerHistory,
|
|
981
|
-
strict: args.strictIssuerHistory,
|
|
982
|
-
jwks: args.jwks,
|
|
983
|
-
trustedKids,
|
|
984
|
-
issuedAt: doc.issued_at ?? null,
|
|
985
|
-
kid: result.kid ?? doc.issuer_signature?.kid ?? null,
|
|
986
|
-
pin,
|
|
987
|
-
requirePin: result.valid && Number(result.payload_version) >= 2,
|
|
988
|
-
});
|
|
989
|
-
const historyFailed = history.checked && !history.ok;
|
|
990
|
-
const failed = !result.valid || !preimages.ok || historyFailed || canonicalFailed;
|
|
991
|
-
if (args.json) {
|
|
992
|
-
console.log(JSON.stringify({ ...result, preimages, issuer_history: history }, null, 2));
|
|
993
|
-
}
|
|
994
|
-
else if (!args.quiet || failed) {
|
|
995
|
-
console.log('');
|
|
996
|
-
console.log(' Chit402 Refusal Verification');
|
|
997
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
998
|
-
console.log(` Schema: ${result.schema}`);
|
|
999
|
-
console.log(` Refusal ID: ${result.refusal_id || doc.refusal_id || '—'}`);
|
|
1000
|
-
console.log(` Code: ${result.refusal_code || doc.refusal_code || '—'}`);
|
|
1001
|
-
console.log(` Nonce: ${result.nonce || '—'}`);
|
|
1002
|
-
console.log(` Chain: ${result.chain_id ?? 'null'}`);
|
|
1003
|
-
console.log(` Charged: false`);
|
|
1004
|
-
console.log('');
|
|
1005
|
-
console.log(' Issuer Signature');
|
|
1006
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
1007
|
-
console.log(` Valid: ${result.valid ? '✓ YES' : '✗ NO'}`);
|
|
1008
|
-
if (result.kid)
|
|
1009
|
-
console.log(` Kid: ${result.kid}`);
|
|
1010
|
-
if (!result.valid && result.reason)
|
|
1011
|
-
console.log(` Reason: ${result.reason}`);
|
|
1012
|
-
console.log('');
|
|
1013
|
-
console.log(' What this proves');
|
|
1014
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
1015
|
-
for (const line of result.proves)
|
|
1016
|
-
console.log(` ${line}`);
|
|
1017
|
-
console.log('');
|
|
1018
|
-
console.log(' What this does not prove');
|
|
1019
|
-
console.log(' ─────────────────────────────────────────────────');
|
|
1020
|
-
for (const line of result.does_not_prove)
|
|
1021
|
-
console.log(` ${line}`);
|
|
1022
|
-
console.log('');
|
|
1023
|
-
console.log(` Overall: ${failed ? 'FAILED' : 'VERIFIED'}`);
|
|
1024
|
-
if (!preimages.ok)
|
|
1025
|
-
console.log(` Preimages: ${preimages.errors.join('; ')}`);
|
|
1026
|
-
if (history.warning)
|
|
1027
|
-
console.log(` Issuer history: ${history.warning}`);
|
|
1028
|
-
if (historyFailed && history.reason)
|
|
1029
|
-
console.log(` Issuer history: ${history.reason}`);
|
|
1030
|
-
console.log('');
|
|
1031
|
-
}
|
|
1032
|
-
return failed ? 1 : 0;
|
|
1033
|
-
}
|
|
1034
|
-
async function main() {
|
|
1035
|
-
const args = parseArgs(process.argv.slice(2));
|
|
1036
|
-
if (args.version) {
|
|
1037
|
-
console.log(`@xfuel/verify ${packageVersion()}`);
|
|
1038
|
-
console.log((0, receipt_version_js_1.formatSupportedReceiptVersions)());
|
|
1039
|
-
return 0;
|
|
1040
|
-
}
|
|
1041
|
-
let anchorMode = args.anchorFlag
|
|
1042
|
-
|| Boolean(args.inclusionFile || args.headFile)
|
|
1043
|
-
|| (args.positionals.length >= 3 && args.sawRpc);
|
|
1044
|
-
if (!anchorMode && args.sawRpc && args.positionals.some((file) => fileLooksLikeHead(file))) {
|
|
1045
|
-
anchorMode = true;
|
|
1046
|
-
}
|
|
1047
|
-
if (args.help) {
|
|
1048
|
-
console.log(HELP);
|
|
1049
|
-
return 0;
|
|
1050
|
-
}
|
|
1051
|
-
if (args.url && !args.jwsFile) {
|
|
1052
|
-
try {
|
|
1053
|
-
const response = await fetch(args.url);
|
|
1054
|
-
await response.arrayBuffer();
|
|
1055
|
-
}
|
|
1056
|
-
catch {
|
|
1057
|
-
// A failed fetch is still not a pass.
|
|
1058
|
-
}
|
|
1059
|
-
console.log('owner_proof_required');
|
|
1060
|
-
console.log(' Overall: OWNER_PROOF_REQUIRED');
|
|
1061
|
-
return 1;
|
|
1062
|
-
}
|
|
1063
|
-
let offlineWitness = null;
|
|
1064
|
-
if (!anchorMode) {
|
|
1065
|
-
try {
|
|
1066
|
-
offlineWitness = readOfflineWitness(args);
|
|
1067
|
-
}
|
|
1068
|
-
catch (err) {
|
|
1069
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
1070
|
-
return 3;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
// A shell plus a holder is not anchor mode. Inclusion is checked with the
|
|
1074
|
-
// holder. Anchor mode would verify the unsigned shell and skip the key.
|
|
1075
|
-
if (anchorMode && !args.jwsFile && !args.acceptShell)
|
|
1076
|
-
return runAnchor(args);
|
|
1077
|
-
if (!args.file) {
|
|
1078
|
-
console.log(HELP);
|
|
1079
|
-
return 3;
|
|
1080
|
-
}
|
|
1081
|
-
let receipt;
|
|
1082
|
-
try {
|
|
1083
|
-
receipt = readReceipt(args.file);
|
|
1084
|
-
}
|
|
1085
|
-
catch (err) {
|
|
1086
|
-
console.error(`Error reading receipt: ${err instanceof Error ? err.message : String(err)}`);
|
|
1087
|
-
return 3;
|
|
1088
|
-
}
|
|
1089
|
-
if ((0, shell_js_1.isReceiptShell)(receipt) || args.acceptShell || args.jwsFile) {
|
|
1090
|
-
if (!(0, shell_js_1.isReceiptShell)(receipt)) {
|
|
1091
|
-
console.error('Invalid shell: schema is not chit402.receipt_shell.v1');
|
|
1092
|
-
return 3;
|
|
1093
|
-
}
|
|
1094
|
-
if (!args.jwsFile) {
|
|
1095
|
-
const shellGate = (0, receipt_version_js_1.classifyShellDocument)(receipt);
|
|
1096
|
-
if (!shellGate.ok)
|
|
1097
|
-
return emitVersionFailure(args, shellGate.errors);
|
|
1098
|
-
console.log(shell_js_1.INCLUDED_SHELL_LINE);
|
|
1099
|
-
console.log(' Overall: INCLUDED_SHELL');
|
|
1100
|
-
return args.acceptShell ? 0 : 1;
|
|
1101
|
-
}
|
|
1102
|
-
let holder;
|
|
1103
|
-
try {
|
|
1104
|
-
const text = (0, node_fs_1.readFileSync)(args.jwsFile, 'utf8');
|
|
1105
|
-
let parsed = text;
|
|
1106
|
-
try {
|
|
1107
|
-
parsed = JSON.parse(text);
|
|
1108
|
-
}
|
|
1109
|
-
catch {
|
|
1110
|
-
parsed = text;
|
|
1111
|
-
}
|
|
1112
|
-
holder = (0, shell_js_1.holderDocumentFromOwnerView)(parsed);
|
|
1113
|
-
}
|
|
1114
|
-
catch (err) {
|
|
1115
|
-
console.error(`Error reading JWS: ${err instanceof Error ? err.message : String(err)}`);
|
|
1116
|
-
return 3;
|
|
1117
|
-
}
|
|
1118
|
-
const shellDoc = receipt;
|
|
1119
|
-
const holderGate = (0, receipt_version_js_1.classifyReceiptDocument)(holder);
|
|
1120
|
-
if (!holderGate.ok)
|
|
1121
|
-
return emitVersionFailure(args, holderGate.errors);
|
|
1122
|
-
const compared = (0, shell_js_1.compareShellToJws)(shellDoc, holder);
|
|
1123
|
-
if (!compared.ok) {
|
|
1124
|
-
console.log('shell_jws_mismatch');
|
|
1125
|
-
if (compared.field)
|
|
1126
|
-
console.log(` Field: ${compared.field}`);
|
|
1127
|
-
console.log(' Overall: FAILED');
|
|
1128
|
-
return 1;
|
|
1129
|
-
}
|
|
1130
|
-
let jwks;
|
|
1131
|
-
if (args.jwksFile) {
|
|
1132
|
-
try {
|
|
1133
|
-
jwks = JSON.parse((0, node_fs_1.readFileSync)(args.jwksFile, 'utf8'));
|
|
1134
|
-
}
|
|
1135
|
-
catch (err) {
|
|
1136
|
-
console.error(`Error reading JWKS file: ${err instanceof Error ? err.message : String(err)}`);
|
|
1137
|
-
return 3;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
let inclusion = offlineWitness?.inclusion ?? null;
|
|
1141
|
-
let head = offlineWitness?.head ?? null;
|
|
1142
|
-
try {
|
|
1143
|
-
if (args.inclusionFile)
|
|
1144
|
-
inclusion = readJson(args.inclusionFile);
|
|
1145
|
-
if (args.headFile)
|
|
1146
|
-
head = readJson(args.headFile);
|
|
1147
|
-
}
|
|
1148
|
-
catch (err) {
|
|
1149
|
-
console.error(`Error reading inclusion: ${err instanceof Error ? err.message : String(err)}`);
|
|
1150
|
-
return 3;
|
|
1151
|
-
}
|
|
1152
|
-
const trustedKids = args.noTrustedKid
|
|
1153
|
-
? []
|
|
1154
|
-
: (args.trustedKids ?? [...index_js_1.DEFAULT_TRUSTED_ISSUER_KIDS]);
|
|
1155
|
-
const result = await (0, index_js_1.verifyReceipt)(holder, {
|
|
1156
|
-
jwks,
|
|
1157
|
-
jwksUri: args.jwksUrl || undefined,
|
|
1158
|
-
fetchJwks: args.fetchJwks,
|
|
1159
|
-
trustedKids,
|
|
1160
|
-
checkPayer: args.checkPayer,
|
|
1161
|
-
rpcUrl: args.rpcUrl || undefined,
|
|
1162
|
-
solanaRpcUrl: args.solanaRpcUrl || undefined,
|
|
1163
|
-
requirePreimages: false,
|
|
1164
|
-
skipIssuerHistory: args.noIssuerHistory || !args.issuerHistoryFile,
|
|
1165
|
-
issuerHistory: null,
|
|
1166
|
-
fetchIssuerHistory: false,
|
|
1167
|
-
salt: args.salt,
|
|
1168
|
-
head: head ?? undefined,
|
|
1169
|
-
inclusion: inclusion ?? undefined,
|
|
1170
|
-
});
|
|
1171
|
-
const inclusionErrors = result.errors.filter((err) => /inclusion|leaf|row_hash|bad_root|tree_size|tree_head|no_leaf/.test(err));
|
|
1172
|
-
const shellLeaf = (shellDoc.inclusion && typeof shellDoc.inclusion === 'object')
|
|
1173
|
-
? shellDoc.inclusion.leaf_hash
|
|
1174
|
-
: null;
|
|
1175
|
-
const provedLeaf = inclusion && typeof inclusion.leaf === 'string'
|
|
1176
|
-
? inclusion.leaf
|
|
1177
|
-
: null;
|
|
1178
|
-
// A head the user wrote by hand ({root, tree_size}) is not a log. The
|
|
1179
|
-
// inclusion only counts against a head that carries an issuer JWS;
|
|
1180
|
-
// verifyReceipt() checks that JWS against the same trust set.
|
|
1181
|
-
const headSig = head && typeof head === 'object'
|
|
1182
|
-
? head.issuer_signature
|
|
1183
|
-
: null;
|
|
1184
|
-
const headSigned = !!headSig && typeof headSig.jws === 'string' && headSig.jws.split('.').length === 3;
|
|
1185
|
-
const verdict = (0, shell_js_1.shellHolderVerdict)({
|
|
1186
|
-
signatureValid: result.issuer_signature.valid === true,
|
|
1187
|
-
keyTrusted: result.issuer_signature.key_trusted === true,
|
|
1188
|
-
signatureReason: result.issuer_signature.reason || (result.issuer_signature.key_trusted ? null : 'key untrusted'),
|
|
1189
|
-
inclusionSupplied: inclusion != null,
|
|
1190
|
-
inclusionOk: inclusion != null && inclusionErrors.length === 0,
|
|
1191
|
-
inclusionReason: inclusion == null ? 'inclusion_missing' : (inclusionErrors[0] || null),
|
|
1192
|
-
headSigned,
|
|
1193
|
-
paymentRef: result.tx,
|
|
1194
|
-
bindingExpected: !!result.binding.expected,
|
|
1195
|
-
bindingMatches: result.binding.matches === true,
|
|
1196
|
-
shellLeaf: shellLeaf == null ? null : String(shellLeaf),
|
|
1197
|
-
provedLeaf,
|
|
1198
|
-
});
|
|
1199
|
-
if (!verdict.verified || (result.overall !== 'verified' && result.overall !== 'verified_carried_forward')) {
|
|
1200
|
-
const reason = verdict.reason || result.errors[0] || 'failed';
|
|
1201
|
-
console.log(reason);
|
|
1202
|
-
console.log(' Overall: FAILED');
|
|
1203
|
-
return 1;
|
|
1204
|
-
}
|
|
1205
|
-
if (args.checkPayer && !result.payer.valid) {
|
|
1206
|
-
console.log(result.payer.reason || 'payment_unchecked');
|
|
1207
|
-
console.log(' Overall: FAILED');
|
|
1208
|
-
return 1;
|
|
1209
|
-
}
|
|
1210
|
-
const issuer = holder.issuer_signature;
|
|
1211
|
-
if (args.salt) {
|
|
1212
|
-
const opened = (0, shell_js_1.openV11Commitment)({
|
|
1213
|
-
payload: (0, shell_js_1.decodeJwsPayload)(issuer?.jws),
|
|
1214
|
-
saltHex: args.salt || '',
|
|
1215
|
-
body: Buffer.alloc(0),
|
|
1216
|
-
});
|
|
1217
|
-
if (!opened.ok) {
|
|
1218
|
-
console.log(opened.reason || 'commitment_mismatch');
|
|
1219
|
-
console.log(' Overall: FAILED');
|
|
1220
|
-
return 1;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
console.log(' Overall: VERIFIED');
|
|
1224
|
-
return 0;
|
|
1225
|
-
}
|
|
1226
|
-
if (typeof receipt.issuer_signature?.jws === 'string') {
|
|
1227
|
-
const receiptGate = (0, receipt_version_js_1.classifyReceiptDocument)(receipt);
|
|
1228
|
-
if (!receiptGate.ok)
|
|
1229
|
-
return emitVersionFailure(args, receiptGate.errors);
|
|
1230
|
-
}
|
|
1231
|
-
const v11 = receipt.v === 11 || (receipt.issuer_signature?.jws
|
|
1232
|
-
? (() => {
|
|
1233
|
-
try {
|
|
1234
|
-
const part = String(receipt.issuer_signature.jws).split('.')[1];
|
|
1235
|
-
const payload = JSON.parse(Buffer.from(part, 'base64url').toString('utf8'));
|
|
1236
|
-
return payload?.v === 11;
|
|
1237
|
-
}
|
|
1238
|
-
catch {
|
|
1239
|
-
return false;
|
|
1240
|
-
}
|
|
1241
|
-
})()
|
|
1242
|
-
: false);
|
|
1243
|
-
if (!receipt.task_id && !v11) {
|
|
1244
|
-
console.error('Invalid receipt: missing task_id');
|
|
1245
|
-
return 3;
|
|
1246
|
-
}
|
|
1247
|
-
// Load JWKS if provided
|
|
1248
|
-
let jwks;
|
|
1249
|
-
if (args.jwksFile) {
|
|
1250
|
-
try {
|
|
1251
|
-
const jwksContent = (0, node_fs_1.readFileSync)(args.jwksFile, 'utf8');
|
|
1252
|
-
jwks = JSON.parse(jwksContent);
|
|
1253
|
-
}
|
|
1254
|
-
catch (err) {
|
|
1255
|
-
console.error(`Error reading JWKS file: ${err instanceof Error ? err.message : String(err)}`);
|
|
1256
|
-
return 3;
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
const trustedKids = args.noTrustedKid
|
|
1260
|
-
? []
|
|
1261
|
-
: (args.trustedKids ?? index_js_1.DEFAULT_TRUSTED_ISSUER_KIDS);
|
|
1262
|
-
if ((0, index_js_1.isRefusalDocument)(receipt)) {
|
|
1263
|
-
let issuerHistory = null;
|
|
1264
|
-
let issuerHistoryBytes = null;
|
|
1265
|
-
if (args.issuerHistoryFile) {
|
|
1266
|
-
issuerHistoryBytes = (0, node_fs_1.readFileSync)(args.issuerHistoryFile, 'utf8');
|
|
1267
|
-
issuerHistory = JSON.parse(issuerHistoryBytes);
|
|
1268
|
-
}
|
|
1269
|
-
let canonicalPreimage = null;
|
|
1270
|
-
if (args.canonicalPreimageFile) {
|
|
1271
|
-
canonicalPreimage = (0, node_fs_1.readFileSync)(args.canonicalPreimageFile, 'utf8');
|
|
1272
|
-
}
|
|
1273
|
-
return runRefusal(receipt, {
|
|
1274
|
-
jwks,
|
|
1275
|
-
trustedKids,
|
|
1276
|
-
json: args.json,
|
|
1277
|
-
quiet: args.quiet,
|
|
1278
|
-
requirePreimages: !args.noPreimage,
|
|
1279
|
-
issuerHistory,
|
|
1280
|
-
issuerHistoryBytes,
|
|
1281
|
-
fetchIssuerHistory: !args.noIssuerHistory && !issuerHistory,
|
|
1282
|
-
strictIssuerHistory: args.strictIssuerHistory,
|
|
1283
|
-
skipIssuerHistory: args.noIssuerHistory,
|
|
1284
|
-
canonicalPreimage,
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
let issuerHistory = null;
|
|
1288
|
-
let issuerHistoryBytes = null;
|
|
1289
|
-
if (args.issuerHistoryFile) {
|
|
1290
|
-
try {
|
|
1291
|
-
issuerHistoryBytes = (0, node_fs_1.readFileSync)(args.issuerHistoryFile, 'utf8');
|
|
1292
|
-
issuerHistory = JSON.parse(issuerHistoryBytes);
|
|
1293
|
-
}
|
|
1294
|
-
catch (err) {
|
|
1295
|
-
console.error(`Error reading issuer history: ${err instanceof Error ? err.message : String(err)}`);
|
|
1296
|
-
return 3;
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
let canonicalPreimage = null;
|
|
1300
|
-
if (args.canonicalPreimageFile) {
|
|
1301
|
-
try {
|
|
1302
|
-
canonicalPreimage = (0, node_fs_1.readFileSync)(args.canonicalPreimageFile, 'utf8');
|
|
1303
|
-
}
|
|
1304
|
-
catch (err) {
|
|
1305
|
-
console.error(`Error reading canonical preimage: ${err instanceof Error ? err.message : String(err)}`);
|
|
1306
|
-
return 3;
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
const carryAsked = args.carryOldHeadFile || args.carryOldInclusionFile
|
|
1310
|
-
|| args.carryHeadFile || args.carryInclusionFiles.length > 0;
|
|
1311
|
-
let carry = null;
|
|
1312
|
-
if (carryAsked) {
|
|
1313
|
-
try {
|
|
1314
|
-
carry = {
|
|
1315
|
-
oldHead: args.carryOldHeadFile ? readJson(args.carryOldHeadFile) : {},
|
|
1316
|
-
oldInclusion: args.carryOldInclusionFile ? readJson(args.carryOldInclusionFile) : { error: 'not_in_tree' },
|
|
1317
|
-
currentHead: args.carryHeadFile ? readJson(args.carryHeadFile) : {},
|
|
1318
|
-
currentInclusions: args.carryInclusionFiles.length > 0
|
|
1319
|
-
? args.carryInclusionFiles.map((file) => readJson(file))
|
|
1320
|
-
: [{ error: 'not_in_tree' }],
|
|
1321
|
-
};
|
|
1322
|
-
}
|
|
1323
|
-
catch (err) {
|
|
1324
|
-
console.error(`Error reading carry-forward files: ${err instanceof Error ? err.message : String(err)}`);
|
|
1325
|
-
return 3;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
let open;
|
|
1329
|
-
if (args.open.length > 0) {
|
|
1330
|
-
open = {};
|
|
1331
|
-
try {
|
|
1332
|
-
for (const spec of args.open) {
|
|
1333
|
-
const opened = (0, v11_receipt_js_1.readOpenFile)(spec);
|
|
1334
|
-
if (opened.kind === 'output')
|
|
1335
|
-
open.output = opened.bytes;
|
|
1336
|
-
else
|
|
1337
|
-
open[opened.kind] = opened.text;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
catch (err) {
|
|
1341
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
1342
|
-
return 3;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
const result = await (0, index_js_1.verifyReceipt)(receipt, {
|
|
1346
|
-
jwks,
|
|
1347
|
-
jwksUri: args.jwksUrl || undefined,
|
|
1348
|
-
fetchJwks: args.fetchJwks,
|
|
1349
|
-
trustedKids,
|
|
1350
|
-
checkNullifier: args.checkNullifier,
|
|
1351
|
-
checkPayer: args.checkPayer,
|
|
1352
|
-
rpcUrl: args.rpcUrl || undefined,
|
|
1353
|
-
solanaRpcUrl: args.solanaRpcUrl || undefined,
|
|
1354
|
-
requirePreimages: !args.noPreimage,
|
|
1355
|
-
issuerHistory,
|
|
1356
|
-
issuerHistoryBytes,
|
|
1357
|
-
fetchIssuerHistory: !args.noIssuerHistory && !issuerHistory,
|
|
1358
|
-
strictIssuerHistory: args.strictIssuerHistory,
|
|
1359
|
-
skipIssuerHistory: args.noIssuerHistory,
|
|
1360
|
-
canonicalPreimage,
|
|
1361
|
-
salt: args.salt,
|
|
1362
|
-
open,
|
|
1363
|
-
head: offlineWitness?.head ?? undefined,
|
|
1364
|
-
inclusion: offlineWitness?.inclusion ?? undefined,
|
|
1365
|
-
carry,
|
|
1366
|
-
});
|
|
1367
|
-
if (args.json) {
|
|
1368
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1369
|
-
}
|
|
1370
|
-
else if (!args.quiet || result.overall === 'failed') {
|
|
1371
|
-
console.log('');
|
|
1372
|
-
console.log(` Chit402 Receipt Verification`);
|
|
1373
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1374
|
-
console.log(` Receipt ID: ${result.receipt_id}`);
|
|
1375
|
-
console.log(` Hub: ${result.hub || '—'}`);
|
|
1376
|
-
console.log(` Model: ${result.model || '—'}`);
|
|
1377
|
-
console.log(` Amount: ${result.issuer_signature.valid ? formatAmount(result.amount_usdc) : '— (not verified)'}`);
|
|
1378
|
-
console.log(` TX: ${result.tx || '—'}`);
|
|
1379
|
-
console.log(` Output Hash: ${result.output_hash ? result.output_hash.slice(0, 18) + '…' : '—'}`);
|
|
1380
|
-
console.log('');
|
|
1381
|
-
console.log(` Binding`);
|
|
1382
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1383
|
-
if (result.binding.expected) {
|
|
1384
|
-
console.log(` Expected: ${result.binding.expected.slice(0, 18)}…`);
|
|
1385
|
-
console.log(` Recomputed: ${result.binding.recomputed?.slice(0, 18)}…`);
|
|
1386
|
-
console.log(` Match: ${result.binding.matches ? '✓ YES' : '✗ NO'}`);
|
|
1387
|
-
console.log(` Covers: ${result.binding.covers.join(', ')}`);
|
|
1388
|
-
}
|
|
1389
|
-
else {
|
|
1390
|
-
console.log(` Status: ${result.binding.reason || 'No payment-binding commitment'}`);
|
|
1391
|
-
}
|
|
1392
|
-
console.log('');
|
|
1393
|
-
console.log(` Issuer Signature`);
|
|
1394
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1395
|
-
if (result.issuer_signature.checked) {
|
|
1396
|
-
console.log(` Kid: ${result.issuer_signature.kid || '—'}`);
|
|
1397
|
-
console.log(` Valid: ${result.issuer_signature.valid ? '✓ YES' : '✗ NO'}`);
|
|
1398
|
-
console.log(` Key trusted: ${result.issuer_signature.key_trusted ? '✓ YES' : '✗ NO'}${result.issuer_signature.trust ? ` (${result.issuer_signature.trust})` : ''}`);
|
|
1399
|
-
if (!result.issuer_signature.valid && result.issuer_signature.reason) {
|
|
1400
|
-
console.log(` Reason: ${result.issuer_signature.reason}`);
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
else {
|
|
1404
|
-
console.log(` Status: ${result.issuer_signature.reason || 'Not checked'}`);
|
|
1405
|
-
if (receipt.issuer_signature && !args.jwksFile && !args.fetchJwks && !args.jwksUrl) {
|
|
1406
|
-
console.log(` (pass --jwks-file, --fetch-jwks, or rely on the default trusted kid)`);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
console.log('');
|
|
1410
|
-
console.log(` Payer (on-chain)`);
|
|
1411
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1412
|
-
if (result.payer.checked) {
|
|
1413
|
-
console.log(` Rail: ${result.payer.rail || '—'}`);
|
|
1414
|
-
console.log(` Payer: ${result.payer.payer_wallet || '—'}`);
|
|
1415
|
-
console.log(` Payee: ${result.payer.payee || '—'}`);
|
|
1416
|
-
console.log(` Asset: ${result.payer.asset || '—'}`);
|
|
1417
|
-
console.log(` Amount: ${formatAmount(result.payer.amount || null)}`);
|
|
1418
|
-
console.log(` Valid: ${result.payer.valid ? '✓ YES' : '✗ NO'}`);
|
|
1419
|
-
if (!result.payer.valid && result.payer.reason) {
|
|
1420
|
-
console.log(` Reason: ${result.payer.reason}`);
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
else {
|
|
1424
|
-
console.log(` Status: ${result.payer.reason || 'Not checked'}`);
|
|
1425
|
-
if (result.issuer_signature.valid && result.payer.payer_wallet && result.tx && !args.checkPayer) {
|
|
1426
|
-
console.log(` (pass --check-payer to verify payer, payee, asset, and amount on-chain)`);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
if (result.claim_mismatches.length > 0) {
|
|
1430
|
-
console.log('');
|
|
1431
|
-
console.log(` Outer / signed mismatches`);
|
|
1432
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1433
|
-
for (const mismatch of result.claim_mismatches) {
|
|
1434
|
-
console.log(` ${mismatch.field}: outer ${mismatch.outer} ≠ signed ${mismatch.signed}`);
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
console.log('');
|
|
1438
|
-
printUnsigned(result.unsigned_fields);
|
|
1439
|
-
console.log('');
|
|
1440
|
-
console.log(` Nullifier`);
|
|
1441
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1442
|
-
if (result.nullifier.nullifier) {
|
|
1443
|
-
console.log(` Value: ${result.nullifier.nullifier.slice(0, 18)}…`);
|
|
1444
|
-
if (args.checkNullifier) {
|
|
1445
|
-
console.log(` On-chain: ${result.nullifier.anchored ? '✓ ANCHORED' : '✗ NOT FOUND'}`);
|
|
1446
|
-
}
|
|
1447
|
-
else {
|
|
1448
|
-
console.log(` On-chain: (not checked — use --check-nullifier)`);
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
else {
|
|
1452
|
-
console.log(` Status: No nullifier (Tier-1 receipt)`);
|
|
1453
|
-
}
|
|
1454
|
-
console.log('');
|
|
1455
|
-
printLane(result.receipt_lane);
|
|
1456
|
-
if (result.preimages.checked) {
|
|
1457
|
-
console.log(` Preimages: ${result.preimages.ok ? '✓ MATCH' : '✗ ' + result.preimages.errors.join('; ')}`);
|
|
1458
|
-
}
|
|
1459
|
-
if (result.issuer_history.checked) {
|
|
1460
|
-
console.log(` Issuer history: ${result.issuer_history.ok ? '✓ kid in window' : '✗ ' + (result.issuer_history.reason || 'failed')}`);
|
|
1461
|
-
}
|
|
1462
|
-
else if (result.issuer_history.warning) {
|
|
1463
|
-
console.log(` Issuer history: ${result.issuer_history.warning}`);
|
|
1464
|
-
}
|
|
1465
|
-
if (result.carry_forward?.status === 'VERIFIED_CARRIED_FORWARD') {
|
|
1466
|
-
const carried = result.carry_forward;
|
|
1467
|
-
console.log(` Log`);
|
|
1468
|
-
console.log(` ─────────────────────────────────────────────────`);
|
|
1469
|
-
console.log(` Status: ${carried.status}`);
|
|
1470
|
-
console.log(` Issued at: ${carried.issued_at}`);
|
|
1471
|
-
console.log(` Issued epoch: ${carried.issued_epoch}`);
|
|
1472
|
-
console.log(` Logged at: ${carried.logged_at}`);
|
|
1473
|
-
console.log(` Logged epoch: ${carried.logged_epoch}`);
|
|
1474
|
-
console.log(` Leaf index: ${carried.leaf_index}`);
|
|
1475
|
-
console.log('');
|
|
1476
|
-
}
|
|
1477
|
-
console.log(` Overall: ${result.overall.toUpperCase()}`);
|
|
1478
|
-
if (result.errors.length > 0) {
|
|
1479
|
-
console.log(` Errors: ${result.errors.join(', ')}`);
|
|
1480
|
-
}
|
|
1481
|
-
console.log('');
|
|
1482
|
-
}
|
|
1483
|
-
switch (result.overall) {
|
|
1484
|
-
case 'verified':
|
|
1485
|
-
case 'verified_carried_forward':
|
|
1486
|
-
return 0;
|
|
1487
|
-
case 'failed':
|
|
1488
|
-
return 1;
|
|
1489
|
-
case 'partial':
|
|
1490
|
-
return 2;
|
|
1491
|
-
default:
|
|
1492
|
-
return 1;
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
/**
|
|
1496
|
-
* Node's fetch keeps its undici agent on this symbol after the first request.
|
|
1497
|
-
* There is no public handle for that pool. Closing it lets the loop drain.
|
|
1498
|
-
*/
|
|
1499
|
-
const UNDICI_GLOBAL_DISPATCHER = Symbol.for('undici.globalDispatcher.1');
|
|
1500
|
-
function fetchDispatcher() {
|
|
1501
|
-
const bag = globalThis;
|
|
1502
|
-
return bag[UNDICI_GLOBAL_DISPATCHER];
|
|
1503
|
-
}
|
|
1504
|
-
/**
|
|
1505
|
-
* process.exit() while a fetch keep-alive socket is already CLOSING aborts
|
|
1506
|
-
* on Windows: Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)
|
|
1507
|
-
* (src/win/async.c). Set the code and close the pool so the loop can drain.
|
|
1508
|
-
*/
|
|
1509
|
-
async function finish(code) {
|
|
1510
|
-
process.exitCode = code;
|
|
1511
|
-
const dispatcher = fetchDispatcher();
|
|
1512
|
-
if (!dispatcher)
|
|
1513
|
-
return;
|
|
1514
|
-
try {
|
|
1515
|
-
const closing = dispatcher.close?.();
|
|
1516
|
-
if (closing)
|
|
1517
|
-
await closing;
|
|
1518
|
-
}
|
|
1519
|
-
catch {
|
|
1520
|
-
try {
|
|
1521
|
-
await dispatcher.destroy?.();
|
|
1522
|
-
}
|
|
1523
|
-
catch {
|
|
1524
|
-
// The verification result is already the exit code.
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
main()
|
|
1529
|
-
.then((code) => finish(code))
|
|
1530
|
-
.catch((err) => {
|
|
1531
|
-
console.error('Unexpected error:', err);
|
|
1532
|
-
return finish(3);
|
|
1533
|
-
});
|
|
1534
|
-
//# sourceMappingURL=cli.js.map
|