chit402-verify 0.3.4 → 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 +270 -82
- package/dist/anchor-trust.d.ts +144 -0
- package/dist/anchor-trust.d.ts.map +1 -0
- package/dist/anchor-trust.js +441 -0
- package/dist/anchor-trust.js.map +1 -0
- package/dist/anchor-witness.d.ts +265 -0
- package/dist/anchor-witness.d.ts.map +1 -0
- package/dist/anchor-witness.js +833 -0
- package/dist/anchor-witness.js.map +1 -0
- package/dist/base-payer.d.ts +65 -0
- package/dist/base-payer.d.ts.map +1 -0
- package/dist/base-payer.js +222 -0
- package/dist/base-payer.js.map +1 -0
- package/dist/binding.d.ts +52 -0
- package/dist/binding.d.ts.map +1 -0
- package/dist/binding.js +80 -0
- package/dist/binding.js.map +1 -0
- package/dist/canonical-preimage.d.ts +9 -0
- package/dist/canonical-preimage.d.ts.map +1 -0
- package/dist/canonical-preimage.js +25 -0
- package/dist/canonical-preimage.js.map +1 -0
- package/dist/carry-forward.d.ts +79 -0
- package/dist/carry-forward.d.ts.map +1 -0
- package/dist/carry-forward.js +184 -0
- package/dist/carry-forward.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2090 -0
- package/dist/cli.js.map +1 -0
- package/dist/epoch.d.ts +143 -0
- package/dist/epoch.d.ts.map +1 -0
- package/dist/epoch.js +352 -0
- package/dist/epoch.js.map +1 -0
- package/dist/head-binding.d.ts +24 -0
- package/dist/head-binding.d.ts.map +1 -0
- package/dist/head-binding.js +61 -0
- package/dist/head-binding.js.map +1 -0
- package/dist/index.d.ts +585 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1648 -0
- package/dist/index.js.map +1 -0
- package/dist/issuer-history.d.ts +151 -0
- package/dist/issuer-history.d.ts.map +1 -0
- package/dist/issuer-history.js +595 -0
- package/dist/issuer-history.js.map +1 -0
- package/dist/issuer-pin.d.ts +126 -0
- package/dist/issuer-pin.d.ts.map +1 -0
- package/dist/issuer-pin.js +567 -0
- package/dist/issuer-pin.js.map +1 -0
- package/dist/jcs.d.ts +8 -0
- package/dist/jcs.d.ts.map +1 -0
- package/dist/jcs.js +64 -0
- package/dist/jcs.js.map +1 -0
- package/dist/jws.d.ts +46 -0
- package/dist/jws.d.ts.map +1 -0
- package/dist/jws.js +129 -0
- package/dist/jws.js.map +1 -0
- package/dist/payer.d.ts +65 -0
- package/dist/payer.d.ts.map +1 -0
- package/dist/payer.js +148 -0
- package/dist/payer.js.map +1 -0
- package/dist/preimage.d.ts +78 -0
- package/dist/preimage.d.ts.map +1 -0
- package/dist/preimage.js +363 -0
- package/dist/preimage.js.map +1 -0
- package/dist/receipt-lane.d.ts +235 -0
- package/dist/receipt-lane.d.ts.map +1 -0
- package/dist/receipt-lane.js +446 -0
- package/dist/receipt-lane.js.map +1 -0
- package/dist/receipt-version.d.ts +98 -0
- package/dist/receipt-version.d.ts.map +1 -0
- package/dist/receipt-version.js +364 -0
- package/dist/receipt-version.js.map +1 -0
- package/dist/refusal.d.ts +93 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +249 -0
- package/dist/refusal.js.map +1 -0
- package/dist/request-binding.d.ts +7 -0
- package/dist/request-binding.d.ts.map +1 -0
- package/dist/request-binding.js +26 -0
- package/dist/request-binding.js.map +1 -0
- package/dist/row-hash.d.ts +22 -0
- package/dist/row-hash.d.ts.map +1 -0
- package/dist/row-hash.js +26 -0
- package/dist/row-hash.js.map +1 -0
- package/dist/shell.d.ts +65 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +268 -0
- package/dist/shell.js.map +1 -0
- package/dist/solana-payer.d.ts +99 -0
- package/dist/solana-payer.d.ts.map +1 -0
- package/dist/solana-payer.js +258 -0
- package/dist/solana-payer.js.map +1 -0
- package/dist/v11-receipt.d.ts +23 -0
- package/dist/v11-receipt.d.ts.map +1 -0
- package/dist/v11-receipt.js +368 -0
- package/dist/v11-receipt.js.map +1 -0
- package/dist/verdict.d.ts +94 -0
- package/dist/verdict.d.ts.map +1 -0
- package/dist/verdict.js +381 -0
- package/dist/verdict.js.map +1 -0
- package/issuer-history.snapshot.json +1 -0
- package/package.json +70 -59
- package/cli.js +0 -27
- package/index.cjs +0 -8
- package/index.d.ts +0 -5
- package/index.js +0 -7
package/README.md
CHANGED
|
@@ -1,82 +1,270 @@
|
|
|
1
|
-
# chit402-verify
|
|
2
|
-
|
|
3
|
-
Offline verification for Chit402 receipts — verify payment binding and output commitment without calling the API.
|
|
4
|
-
|
|
5
|
-
npm: `chit402-verify` · License: Apache-2.0 · Docs: https://chit402.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
npx
|
|
39
|
-
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
|
68
|
-
|
|
69
|
-
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
# chit402-verify
|
|
2
|
+
|
|
3
|
+
Offline verification for Chit402 receipts — verify payment binding and output commitment without calling the API.
|
|
4
|
+
|
|
5
|
+
npm: `chit402-verify` · License: Apache-2.0 · Docs: https://chit402.com
|
|
6
|
+
|
|
7
|
+
`@xfuel/verify` 0.4.0 is an alias pinned to exactly this version. The `xfuel-verify` command stays.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install chit402-verify
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Library
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { verifyReceipt, verifyBinding, verifyNullifier } from '@xfuel/verify';
|
|
21
|
+
|
|
22
|
+
// Verify a receipt offline (binding only, no network)
|
|
23
|
+
const receipt = { /* Chit402 receipt JSON */ };
|
|
24
|
+
const result = verifyBinding(receipt);
|
|
25
|
+
console.log(result.matches); // true if binding verified
|
|
26
|
+
|
|
27
|
+
// Full verification including on-chain nullifier check
|
|
28
|
+
const fullResult = await verifyReceipt(receipt, { checkNullifier: true });
|
|
29
|
+
console.log(fullResult.overall); // 'verified' | 'partial' | 'failed'
|
|
30
|
+
console.log(fullResult.receipt_lane.freeze); // unsigned; does not change overall
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### CLI
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# History is always checked. With no file, a live snapshot must prefix-extend
|
|
37
|
+
# the copy shipped in this package, or that copy is used (as of 2026-10-14).
|
|
38
|
+
npx xfuel-verify receipt.json
|
|
39
|
+
|
|
40
|
+
# Offline history. --no-issuer-history, --strict, --accept-no-key-window,
|
|
41
|
+
# and --accept-shell are removed.
|
|
42
|
+
npx xfuel-verify receipt.json --issuer-history issuer-history.json
|
|
43
|
+
|
|
44
|
+
# Hash the canonical object (must match signed payload_hash).
|
|
45
|
+
npx xfuel-verify receipt.json --canonical-preimage preimage.json --issuer-history issuer-history.json
|
|
46
|
+
|
|
47
|
+
# A signed refusal (schema chit402.refusal.v1) is not a payment.
|
|
48
|
+
# The check proves the issuer refused, at the signed anchor, for that code.
|
|
49
|
+
# It does not prove a payment or that the block still stands.
|
|
50
|
+
npx xfuel-verify refusal.json
|
|
51
|
+
|
|
52
|
+
# With on-chain nullifier check (requires network)
|
|
53
|
+
npx xfuel-verify receipt.json --check-nullifier
|
|
54
|
+
|
|
55
|
+
# Output as JSON
|
|
56
|
+
npx xfuel-verify receipt.json --json
|
|
57
|
+
|
|
58
|
+
# From stdin
|
|
59
|
+
curl -s https://api.chit402.com/receipt/task-123?format=json | npx xfuel-verify -
|
|
60
|
+
|
|
61
|
+
# Dual anchor: inclusion, Solana memo, Base calldata
|
|
62
|
+
npx xfuel-verify receipt.json inclusion.json head.json --rpc
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## What This Verifies
|
|
66
|
+
|
|
67
|
+
| Check | Requires Network? | Description |
|
|
68
|
+
|-------|-------------------|-------------|
|
|
69
|
+
| Payment binding | No | Recompute commitment from receipt fields |
|
|
70
|
+
| Issuer signature | No, unless `--fetch-jwks` | ES256. Trusted only via JWKS (by kid) or a pinned RFC 7638 kid |
|
|
71
|
+
| Output hash | No | Hash is on the receipt |
|
|
72
|
+
| On-chain settlement | Yes | Query Base RPC for tx |
|
|
73
|
+
| Nullifier anchor | Yes | Query ZKVerifierSP1 contract |
|
|
74
|
+
| Tree head and inclusion | No | Head ES256 signature over size and root, then an index-and-size-bound inclusion proof |
|
|
75
|
+
| Anchored receipt root | Yes, with `--rpc` | The offline head and inclusion checks, then Solana memo and fee payer and Base calldata and sender |
|
|
76
|
+
| Canonical preimage | No | SHA-256 of `--canonical-preimage`, or the stored canonical object, matches signed `payload_hash` |
|
|
77
|
+
| Issuer history | Always. File, else a live prefix of the shipped snapshot, else the shipped snapshot | `iat` is inside the latest `not_before` and the earliest `not_after` for that kid. A changed `not_before` is `kid_status_regression` (exit 1). No covering key is exit 2 |
|
|
78
|
+
|
|
79
|
+
## Issuer Signature Verification (ES256)
|
|
80
|
+
|
|
81
|
+
Receipts include `issuer_signature.jws` (compact ES256 / P-256). The signature
|
|
82
|
+
counts only when the verifying key is trusted:
|
|
83
|
+
|
|
84
|
+
1. **JWKS by kid** — a key in `--jwks-file`, `--jwks-url`, or `--fetch-jwks`
|
|
85
|
+
(allowlisted host, default `api.chit402.com`) whose `kid` matches the JWS.
|
|
86
|
+
2. **Pinned thumbprint** — the embedded `issuer_jwk` is used only when its
|
|
87
|
+
RFC 7638 thumbprint equals a trusted kid. The default offline pin is the
|
|
88
|
+
production kid `IvFpmC-vPhkY_v0vidsrWVT9uzlE5XWKZgAEOeJTq1Q`. Override with
|
|
89
|
+
`--trusted-kid`, or disable with `--no-trusted-kid`.
|
|
90
|
+
|
|
91
|
+
The epoch record and the tree head use those same sources, with or without `--rpc`. `xfuel-verify receipt.json inclusion.json head.json` checks the head signature and the inclusion proof offline. `--rpc` adds the chain checks. An embedded key still has to match the trusted-kid pin, or the kid has to be in a verified issuer-history entry. The head's signed `anchors.base.from` and `anchors.solana.fee_payer` must be on the anchor-wallet list (the package pin, or a verified `/.well-known/anchor-wallets.json`). The chain sender and fee payer must be those wallets. `--version` prints the package version.
|
|
92
|
+
|
|
93
|
+
Anchor mode also checks a content-addressed issuer pin when the receipt or the head carries `issuer_key_pin`, or when you pass `--issuer-pin`. The pin is a 40-hex commit plus the SHA-256 of `docs/well-known/issuer-key.json`. A branch name is not fetched. A claimed pin requires `issuer_signature.jws` verified under that key. Unsigned envelope kids do not satisfy the check. The registration self-signature is checked when the file has one. A claimed era with no pin fails `ISSUER_PIN_DOWNGRADE`. Receipts that do not claim the era skip this check. The pin chain is Base Sepolia (`eip155:84532`). See [issuer-key-pin.md](../../docs/product/issuer-key-pin.md).
|
|
94
|
+
|
|
95
|
+
`issuer_jwk` on the receipt is not a trust root. A copy re-signed with an
|
|
96
|
+
arbitrary P-256 key reports `key untrusted` (`issuer_signature.valid === false`),
|
|
97
|
+
including when `--jwks-file` points at the real JWKS.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# History is always on. A local snapshot:
|
|
101
|
+
npx xfuel-verify receipt.json --issuer-history issuer-history.json
|
|
102
|
+
|
|
103
|
+
# Recompute a published canonical preimage and check the kid window.
|
|
104
|
+
# curl -f stops on HTTP errors. Most receipts have no /preimage (a 404).
|
|
105
|
+
# A missing preimage is not a verification failure: omit --canonical-preimage.
|
|
106
|
+
curl -fsS "https://api.chit402.com/receipt/RECEIPT_ID?format=json" -o receipt.json
|
|
107
|
+
curl -fsS "https://api.chit402.com/.well-known/issuer-history.json?version=1" -o issuer-history.json
|
|
108
|
+
if curl -fsS "https://api.chit402.com/receipt/RECEIPT_ID/preimage" -o preimage.json; then
|
|
109
|
+
npx xfuel-verify receipt.json --canonical-preimage preimage.json --issuer-history issuer-history.json
|
|
110
|
+
else
|
|
111
|
+
npx xfuel-verify receipt.json --issuer-history issuer-history.json
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Trust the published JWKS instead of (or in addition to) the pin
|
|
115
|
+
curl -o issuer-jwks.json https://api.chit402.com/.well-known/jwks.json
|
|
116
|
+
npx xfuel-verify receipt.json --jwks-file issuer-jwks.json --no-trusted-kid
|
|
117
|
+
|
|
118
|
+
# Fetch the issuer JWKS (https://api.chit402.com/.well-known/jwks.json)
|
|
119
|
+
npx xfuel-verify receipt.json --fetch-jwks
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { verifyReceipt } from '@xfuel/verify';
|
|
124
|
+
|
|
125
|
+
const result = await verifyReceipt(receipt);
|
|
126
|
+
console.log(result.issuer_signature.valid); // true only for a trusted key
|
|
127
|
+
console.log(result.issuer_signature.key_trusted);
|
|
128
|
+
console.log(result.amount_usdc); // from verified claims, not the outer copy
|
|
129
|
+
console.log(result.claim_mismatches); // any signed path whose outer copy disagrees
|
|
130
|
+
console.log(result.unsigned_fields); // outer paths the signature does not cover
|
|
131
|
+
console.log(result.verified_scope); // 'signed_claims'
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Amount, payer, payee, asset, and tx are read from the verified JWS claims.
|
|
135
|
+
Every signed path is compared with the outer receipt. A mismatch fails.
|
|
136
|
+
Paths the signature does not cover are `unsigned_fields` and are printed as
|
|
137
|
+
`UNVERIFIED`. Overall does not verify them. The outer document `schema` is
|
|
138
|
+
not a copy of the JWS `schema`. `--check-payer` on Base confirms payer, payee,
|
|
139
|
+
asset, and amount in the USDC `Transfer` log.
|
|
140
|
+
|
|
141
|
+
`xfuel-verify receipt.json inclusion.json head.json` also runs those receipt
|
|
142
|
+
checks. A matching inclusion proof does not make a tampered receipt VERIFIED.
|
|
143
|
+
|
|
144
|
+
A paid USDC receipt whose signed `binding.expected_commitment` is null is
|
|
145
|
+
reported as “No payment-binding commitment”, not as an unmetered or TFUEL receipt.
|
|
146
|
+
|
|
147
|
+
The CLI does **not** fetch JWKS unless `--fetch-jwks` or `--jwks-url` is set.
|
|
148
|
+
Exit code 1 is returned when the key is untrusted, the signature is invalid, or
|
|
149
|
+
a signed claim disagrees with the outer copy.
|
|
150
|
+
|
|
151
|
+
## Frozen Fields
|
|
152
|
+
|
|
153
|
+
These fields are immutable once set and verifiable by any third party:
|
|
154
|
+
|
|
155
|
+
- `task_id` — unique task identifier
|
|
156
|
+
- `route.provider` — compute hub (theta-edgecloud, akash-network)
|
|
157
|
+
- `route.model` — model that served the request
|
|
158
|
+
- `payment.gross_amount` — total charged in USDC atomic units
|
|
159
|
+
- `payment.ref` — settlement reference (network:txHash)
|
|
160
|
+
- `output.hash` — commitment to model output
|
|
161
|
+
- `proof.nullifier` — single-use nullifier anchored on-chain
|
|
162
|
+
|
|
163
|
+
## Verification Algorithm
|
|
164
|
+
|
|
165
|
+
The binding commitment is computed as:
|
|
166
|
+
|
|
167
|
+
```solidity
|
|
168
|
+
// Payment-only binding
|
|
169
|
+
keccak256(abi.encodePacked(
|
|
170
|
+
keccak256(payment_ref),
|
|
171
|
+
keccak256(task_id),
|
|
172
|
+
rail_discriminant, // 1=usdc, 2=tfuel
|
|
173
|
+
amount
|
|
174
|
+
))
|
|
175
|
+
|
|
176
|
+
// PBR (Payment-Bound Receipt) — includes model + output
|
|
177
|
+
keccak256(abi.encodePacked(
|
|
178
|
+
keccak256(payment_ref),
|
|
179
|
+
keccak256(task_id),
|
|
180
|
+
rail_discriminant,
|
|
181
|
+
amount,
|
|
182
|
+
model_commitment,
|
|
183
|
+
output_hash
|
|
184
|
+
))
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This matches `SP1ProofHooks.computePaymentCommitment` on-chain.
|
|
188
|
+
|
|
189
|
+
## Exit Codes (CLI)
|
|
190
|
+
|
|
191
|
+
| Code | Meaning |
|
|
192
|
+
|------|---------|
|
|
193
|
+
| 0 | Verified |
|
|
194
|
+
| 1 | Verification failed |
|
|
195
|
+
| 2 | Partial (binding ok, nullifier not checked) |
|
|
196
|
+
| 3 | Input error |
|
|
197
|
+
|
|
198
|
+
`unsupported_version` uses exit 1. No flag (`--check-payer`, `--rpc`, `--fetch-jwks`, `--no-issuer-history`, `--no-preimage`, `--no-trusted-kid`, `--accept-shell`) turns an unknown version into a pass. The check runs before any chain RPC.
|
|
199
|
+
|
|
200
|
+
## Receipt versions
|
|
201
|
+
|
|
202
|
+
The signed JWS payload is the version. It is an exact JSON number. Strings, booleans, arrays, null, and non-integers are `unsupported_version`. `11.0` and `1.1e1` are the number 11.
|
|
203
|
+
|
|
204
|
+
| Signed field | Accepted |
|
|
205
|
+
|---|---|
|
|
206
|
+
| `v` | `11`, and the claims must not contain `payload_version` |
|
|
207
|
+
| `payload_version` | integers `1` through `10`, and `11` only when `issuer_root` is signed |
|
|
208
|
+
| refusal `payload_version` | integers `1`, `2`, `3` |
|
|
209
|
+
| neither field | outer version `4` or `5`, and no field introduced at v6 or later (`version_source: inferred`) |
|
|
210
|
+
|
|
211
|
+
An outer `v` or `issuer_signature.payload_version` that differs in value or type is `version_mismatch`. A `v` or `payload_version` in the JWS protected header is ignored.
|
|
212
|
+
|
|
213
|
+
Receipts issued after 2026-10-08 require `@xfuel/verify` >= 0.3.5. Older versions may print VERIFIED for formats they do not understand. `--help` and `--version` print this list.
|
|
214
|
+
|
|
215
|
+
## API Reference
|
|
216
|
+
|
|
217
|
+
### `verifyBinding(receipt)`
|
|
218
|
+
|
|
219
|
+
Verify payment binding locally. Returns:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
{
|
|
223
|
+
verified: boolean;
|
|
224
|
+
expected: string | null;
|
|
225
|
+
recomputed: string | null;
|
|
226
|
+
matches: boolean;
|
|
227
|
+
covers: string[];
|
|
228
|
+
reason?: string;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `verifyNullifier(receipt, options?)`
|
|
233
|
+
|
|
234
|
+
Verify nullifier is anchored on-chain. Requires network access.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
{
|
|
238
|
+
verified: boolean;
|
|
239
|
+
nullifier: string | null;
|
|
240
|
+
anchored: boolean | null;
|
|
241
|
+
reason?: string;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### `verifyReceipt(receipt, options?)`
|
|
246
|
+
|
|
247
|
+
Full verification combining binding and optional nullifier check.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
{
|
|
251
|
+
receipt_id: string;
|
|
252
|
+
binding: BindingVerification;
|
|
253
|
+
nullifier: NullifierVerification;
|
|
254
|
+
output_hash: string | null;
|
|
255
|
+
hub: string | null;
|
|
256
|
+
model: string | null;
|
|
257
|
+
amount_usdc: string | null;
|
|
258
|
+
tx: string | null;
|
|
259
|
+
overall: 'verified' | 'partial' | 'failed';
|
|
260
|
+
errors: string[];
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Build digest
|
|
265
|
+
|
|
266
|
+
`npm run digest` writes `BUILD_DIGEST.txt`. That file is the SHA-256 of the sorted `src/**/*.ts` listing, not a bit-reproducible `tsc` binary. The same sources produce the same digest. The receipt-tree genesis leaf copies this digest so a holder can see which verifier source the issuer vouches for. It does not prove the process that served the receipt ran that exact binary.
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
Apache-2.0
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bind an anchored tree head to the issuer.
|
|
3
|
+
*
|
|
4
|
+
* Anchor mode trusts a head only when its ES256 `issuer_signature` verifies
|
|
5
|
+
* under the same rules as a receipt: a JWKS key matched by kid, an embedded
|
|
6
|
+
* key whose RFC 7638 thumbprint is a pinned kid, or a kid in a verified
|
|
7
|
+
* issuer-history document. The signed payload must match the outer fields
|
|
8
|
+
* the anchor check uses.
|
|
9
|
+
*
|
|
10
|
+
* The Base sender and the Solana fee payer in that payload must be on the
|
|
11
|
+
* issuer's anchor-wallet list, and the transactions the RPC returns must
|
|
12
|
+
* have been sent by those wallets.
|
|
13
|
+
*
|
|
14
|
+
* The list is the package pin, plus wallets inside a verified
|
|
15
|
+
* `chit402.anchor_wallets.v1` document or a verified issuer-history
|
|
16
|
+
* payload's `anchor_wallets`. Two later sources are named and not read:
|
|
17
|
+
* the Base `ChitIssuerRoot` registry (draft #485) and the DNS TXT at
|
|
18
|
+
* `_issuer.chit402.com`. This module does not embed a registry address
|
|
19
|
+
* and does not resolve DNS.
|
|
20
|
+
*/
|
|
21
|
+
import { type Es256Jwk } from './jws.js';
|
|
22
|
+
import { type IssuerHistoryDocument } from './issuer-history.js';
|
|
23
|
+
/** Confirmed sender of Base tx 0x1d8d7ea255170c8d4b87bef9382e13555dded2072fd877ce35f995f1ab54ee09. */
|
|
24
|
+
export declare const PINNED_BASE_ANCHOR_WALLET = "0x1844D1F5FE42aff1Cce6F776514Fd40374079582";
|
|
25
|
+
/** Fee payer of Solana memo 61RHMsPPseUc35v5oxDEtANEDCvXk5fmxZdrFDMknGWc5m7U8YhMfz4En1eFFj3z9n67ZtMiha1zkwnneL2LUiXk. */
|
|
26
|
+
export declare const PINNED_SOLANA_ANCHOR_FEE_PAYER = "BHTnbPu6UZ7zQZ7Qpkpz4LcUQbMN73YDsMtvaNXpEioD";
|
|
27
|
+
/** A head signed before `anchors.base.from` and `anchors.solana.fee_payer` existed. Never VERIFIED. */
|
|
28
|
+
export declare const LEGACY_HEAD_UNPINNED_SIGNER = "LEGACY_HEAD_UNPINNED_SIGNER";
|
|
29
|
+
export declare const ANCHOR_WALLETS_SCHEMA = "chit402.anchor_wallets.v1";
|
|
30
|
+
export declare const ANCHOR_WALLETS_JWT_TYP = "chit402-anchor-wallets+jwt";
|
|
31
|
+
/** Sources this version does not query. A later build can fill the same list. */
|
|
32
|
+
export declare const ANCHOR_WALLET_SOURCES_NOT_CONSULTED: readonly ["issuer_root", "dns"];
|
|
33
|
+
export declare const HEAD_TRUST_MESSAGES: Record<string, string>;
|
|
34
|
+
interface JwksLike {
|
|
35
|
+
keys?: Es256Jwk[];
|
|
36
|
+
}
|
|
37
|
+
export interface TreeHeadDocument {
|
|
38
|
+
schema?: string;
|
|
39
|
+
payload_version?: number;
|
|
40
|
+
root?: string | null;
|
|
41
|
+
tree_size?: number | null;
|
|
42
|
+
epoch?: number | null;
|
|
43
|
+
prev_epoch_root?: string | null;
|
|
44
|
+
prev_epoch_size?: number | null;
|
|
45
|
+
published_at?: string | null;
|
|
46
|
+
anchors?: {
|
|
47
|
+
base?: {
|
|
48
|
+
status?: string | null;
|
|
49
|
+
tx?: string | null;
|
|
50
|
+
calldata?: string | null;
|
|
51
|
+
chain_id?: number | null;
|
|
52
|
+
from?: string | null;
|
|
53
|
+
} | null;
|
|
54
|
+
solana?: {
|
|
55
|
+
status?: string | null;
|
|
56
|
+
signature?: string | null;
|
|
57
|
+
slot?: number | null;
|
|
58
|
+
cluster?: string | null;
|
|
59
|
+
memo?: string | null;
|
|
60
|
+
fee_payer?: string | null;
|
|
61
|
+
} | null;
|
|
62
|
+
} | null;
|
|
63
|
+
issuer_signature?: {
|
|
64
|
+
jws?: string;
|
|
65
|
+
kid?: string;
|
|
66
|
+
issuer_jwk?: Es256Jwk;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface HeadTrustResult {
|
|
70
|
+
ok: boolean;
|
|
71
|
+
reason: string | null;
|
|
72
|
+
message: string | null;
|
|
73
|
+
kid: string | null;
|
|
74
|
+
trust: 'jwks' | 'pinned_kid' | 'issuer_history' | null;
|
|
75
|
+
payload: Record<string, unknown> | null;
|
|
76
|
+
}
|
|
77
|
+
export interface AnchorWalletList {
|
|
78
|
+
base: string[];
|
|
79
|
+
solana: string[];
|
|
80
|
+
sources: Array<'pin' | 'well_known' | 'issuer_history'>;
|
|
81
|
+
not_consulted: readonly ['issuer_root', 'dns'];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* True when a verified head anchors a chain but the signature does not name
|
|
85
|
+
* that chain's signer. Those fields were added after the epoch-1 head and
|
|
86
|
+
* after the epoch-2 head signed at 07:43 ET on 2026-10-07. A pending side
|
|
87
|
+
* is not this case.
|
|
88
|
+
*/
|
|
89
|
+
export declare function headOmitsPinnedSigner(payload: Record<string, unknown> | null | undefined): boolean;
|
|
90
|
+
/** Signed payload must carry the same root, size, epoch, and anchors the check uses. */
|
|
91
|
+
export declare function headClaimsMatch(outer: TreeHeadDocument, payload: Record<string, unknown>): string | null;
|
|
92
|
+
export declare function verifyTreeHeadTrust(head: TreeHeadDocument | null | undefined, { jwks, trustedKids, issuerHistory, strictIssuerHistory, }?: {
|
|
93
|
+
jwks?: JwksLike;
|
|
94
|
+
trustedKids?: readonly string[];
|
|
95
|
+
issuerHistory?: IssuerHistoryDocument | null;
|
|
96
|
+
strictIssuerHistory?: boolean;
|
|
97
|
+
}): HeadTrustResult;
|
|
98
|
+
/**
|
|
99
|
+
* Named inclusion head. The gateway summary is `{ root, tree_size, signature }`
|
|
100
|
+
* and is not a tree-head document, so `headClaimsMatch` cannot run on it.
|
|
101
|
+
* Trust the JWS under JWKS or a pinned kid first, then use the verified
|
|
102
|
+
* payload as the head. A key carried on the summary is ignored unless that
|
|
103
|
+
* key is pinned. The summary root and size, when present, must match the payload.
|
|
104
|
+
* Returns null when the JWS is missing or untrusted: the caller must not
|
|
105
|
+
* treat the summary as a head.
|
|
106
|
+
*/
|
|
107
|
+
export declare function headTrustedFromInclusion(inclusion: {
|
|
108
|
+
head?: unknown;
|
|
109
|
+
} | null | undefined, { jwks, trustedKids, }?: {
|
|
110
|
+
jwks?: JwksLike;
|
|
111
|
+
trustedKids?: readonly string[];
|
|
112
|
+
}): TreeHeadDocument | null;
|
|
113
|
+
export declare function verifyAnchorWalletDocument(doc: {
|
|
114
|
+
schema?: string;
|
|
115
|
+
base?: unknown;
|
|
116
|
+
solana?: unknown;
|
|
117
|
+
issuer_signature?: {
|
|
118
|
+
jws?: string;
|
|
119
|
+
issuer_jwk?: Es256Jwk;
|
|
120
|
+
};
|
|
121
|
+
} | null | undefined, { jwks, trustedKids }?: {
|
|
122
|
+
jwks?: JwksLike;
|
|
123
|
+
trustedKids?: readonly string[];
|
|
124
|
+
}): {
|
|
125
|
+
ok: true;
|
|
126
|
+
base: string[];
|
|
127
|
+
solana: string[];
|
|
128
|
+
} | {
|
|
129
|
+
ok: false;
|
|
130
|
+
reason: string;
|
|
131
|
+
};
|
|
132
|
+
export declare function compileAnchorWallets({ jwks, trustedKids, issuerHistory, published, }?: {
|
|
133
|
+
jwks?: JwksLike;
|
|
134
|
+
trustedKids?: readonly string[];
|
|
135
|
+
issuerHistory?: IssuerHistoryDocument | null;
|
|
136
|
+
published?: {
|
|
137
|
+
base: string[];
|
|
138
|
+
solana: string[];
|
|
139
|
+
} | null;
|
|
140
|
+
}): AnchorWalletList;
|
|
141
|
+
export declare function walletListed(list: readonly string[], value: string, chain: 'base' | 'solana'): boolean;
|
|
142
|
+
export declare function sameBaseAddress(left: string, right: string): boolean;
|
|
143
|
+
export {};
|
|
144
|
+
//# sourceMappingURL=anchor-trust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-trust.d.ts","sourceRoot":"","sources":["../src/anchor-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAQL,KAAK,QAAQ,EACd,MAAM,UAAU,CAAC;AAClB,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,qBAAqB,CAAC;AAE7B,sGAAsG;AACtG,eAAO,MAAM,yBAAyB,+CAA+C,CAAC;AAEtF,yHAAyH;AACzH,eAAO,MAAM,8BAA8B,iDAAiD,CAAC;AAE7F,uGAAuG;AACvG,eAAO,MAAM,2BAA2B,gCAAgC,CAAC;AAEzE,eAAO,MAAM,qBAAqB,8BAA8B,CAAC;AACjE,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AAEnE,iFAAiF;AACjF,eAAO,MAAM,mCAAmC,iCAAkC,CAAC;AAEnF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBtD,CAAC;AAEF,UAAU,QAAQ;IAChB,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE;YACL,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACvB,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SACtB,GAAG,IAAI,CAAC;QACT,MAAM,CAAC,EAAE;YACP,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACxB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SAC3B,GAAG,IAAI,CAAC;KACV,GAAG,IAAI,CAAC;IACT,gBAAgB,CAAC,EAAE;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,QAAQ,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,gBAAgB,GAAG,IAAI,CAAC;IACvD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,KAAK,CAAC,KAAK,GAAG,YAAY,GAAG,gBAAgB,CAAC,CAAC;IACxD,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;CAChD;AAkHD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAWlG;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAkBxG;AAcD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACzC,EACE,IAAI,EACJ,WAAW,EACX,aAAoB,EACpB,mBAA2B,GAC5B,GAAE;IACD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC7C,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B,GACL,eAAe,CAyDjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EAChD,EACE,IAAI,EACJ,WAAW,GACZ,GAAE;IACD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,gBAAgB,GAAG,IAAI,CAkCzB;AA2CD,wBAAgB,0BAA0B,CACxC,GAAG,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACzI,EAAE,IAAI,EAAE,WAAW,EAAE,GAAE;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAC/E;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAchF;AA6BD,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,WAAW,EACX,aAAoB,EACpB,SAAgB,GACjB,GAAE;IACD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC7C,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;CACpD,GAAG,gBAAgB,CAqBxB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAMtG;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpE"}
|