# nationality-in-v1

the ISO 3166-1 numeric nationality is in the list whose Merkle root is public. Proven without revealing the attribute, against a
commitment `Poseidon(value, salt)` the issuer recorded in SuperWalletTrustRegistry
(`commitment(credentialId, circuitId)`), and bound to the verifier's nonce.

| | |
|---|---|
| circuitId | `0xbd9e44ea613d973a0c384ec4b6430bbad1dea1e2cc38ea1afd85f825f88fc2ca` = keccak256("nationality-in-v1") |
| vkHash | `0xcc46974ca56304b8a4cd40fbf758be844420d36ccded5a29efd08e454732d80c` |
| ceremonyContributions | 1 (one Super Wallet contribution plus a public Xphere beacon; see TRANSCRIPT.md) |
| Committed attribute | `nationality`: ISO 3166-1 numeric code (KR 410, LA 418, VN 704) |
| Public signals | `commitment`, `root`, `nonce` (snarkjs order; the commitment first, the nonce last) |
| Verifier call | `verifyProof(uint256[2],uint256[2][2],uint256[2],uint256[3])` |
| Request (`<sw-verify request>`) | `nationality-in:410,418,704` -> predicate `{ "circuit": "nationality-in-v1", "params": { "codes": [410, 418, 704] } }` |
| Registry | `0x852688D815410e3FEa015FD3a07028BF6b981d80` on Xphere (chain id 20250217). **Not registered as of 2026-09-27**: until `register-circuit` is sent, `circuit(0xbd9e44ea...)` returns the zero address and verifiers refuse this predicate |
| uri | https://trendy.storydot.kr/sdk/zk/nationality-in-v1/ |

## What a verifier checks

1. The circuit is registered and its vkHash is the one above; the proof passes `verifyProof` on
   the registered verifier contract (a view `eth_call`).
2. `publicSignals[0]` equals `commitment(credentialId, circuitId)` on the registry, the credential
   is anchored and not revoked, and its issuer is active.
3. The last public signal equals the nonce the verifier issued for this request (a proof made for
   another request fails the pairing check, and is refused before that).
4. The statement inputs are what was asked: `root` equals the Merkle root the verifier rebuilds from its own list (leaves Poseidon(code), sorted and de-duplicated, empty slots 0, depth 8).

`@superwallet/verifier-sdk` `verify(response, { zkRequest })` and `<sw-verify request="nationality-in:410,418,704">`
do all four.

## Files

| File | What it is |
|---|---|
| `nationality-in-v1.circom`, `templates/`, `lib/` | The circuit (circom 2.1.9; circomlib 2.0.5 for Poseidon, bitify and comparators) |
| `nationality-in-v1.r1cs` | The constraint system (2,414 constraints); needed to check the ceremony |
| `nationality-in-v1.wasm` | Witness calculator, for proving |
| `nationality-in-v1.zkey` | The final proving key; contains the whole contribution chain |
| `verification_key.json` | The Groth16 verification key |
| `Verifier.sol`, `solc-input.json` | The snarkjs Solidity verifier and the exact compiler input (solc 0.8.24, optimizer 200 runs, evmVersion paris) |
| `circuit.json` | Machine-readable manifest and ceremony record |
| `TRANSCRIPT.md` | The ceremony, step by step, with every hash |

## Reproduce

```sh
npm install circomlib@2.0.5 circom2@0.2.23
npx circom2 nationality-in-v1.circom --r1cs --wasm --O2 -l . -l node_modules
sha256sum nationality-in-v1.r1cs      # 3ec32d1296f3b0b72e7d081e55398dc2036e49cf4c094bc133e9c95519fc450e

npx --yes solc@0.8.24 --standard-json < solc-input.json > solc-output.json
# keccak256(runtime bytecode of contracts/nationality-in-v1.Verifier.sol:Groth16Verifier) = 0x11a67443d27eaff0575ccd3ec45489fc01f519ad59d704039f81652bba56df47
# (../tier-threshold-v1/check-bytecode.cjs compares it with the deployed contract)
```

vkHash: RFC 8785 canonical JSON of `verification_key.json` (keys sorted, no whitespace), then
keccak256: `node scripts/governance.cjs vk-hash verification_key.json` in chain/trust-registry, or
`vkHash()` in @superwallet/zk-kit.
