Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2dad556
chore: open release branch for mainnet-hardening bundle
TaprootFreak May 26, 2026
feacd31
fix(ci): set ZKCOINS_E2E_ALLOW_FEATURE_TRIMMED_SERVER on PRD api-e2e …
TaprootFreak May 26, 2026
7805692
fix(network-config): require ESPLORA_URL + ESPLORA_WS_URL on Mainnet …
TaprootFreak May 26, 2026
a7231a1
docs: replace "server" with "node"/"API" in root design docs
TaprootFreak May 26, 2026
bf75aec
feat(db): persist inscription kind (mint/send) + expose via /api/insc…
TaprootFreak May 26, 2026
68028da
docs(program-plonky2,script-plonky2,shared): replace remaining "serve…
TaprootFreak May 26, 2026
b53ee42
refactor(node): rename internal server-named bindings to node
TaprootFreak May 26, 2026
8ad972d
chore: update workflow comments to use "node"/"API"
TaprootFreak May 26, 2026
7f3f44b
test: drop 0010 length CHECKs in the 5 corrupt-row-plant tests
TaprootFreak May 26, 2026
ce4307c
docs(migrations): replace remaining "server" with "node" in SQL comments
TaprootFreak May 26, 2026
d1d691d
test(account_node): also disable accounts_history_trigger in wrong-le…
TaprootFreak May 26, 2026
8d803fd
Merge branch 'release/mainnet-hardening' into chore/rename-server-to-…
TaprootFreak May 26, 2026
93117c4
test(db): update creates_all_tables to expect the full 0010 schema
TaprootFreak May 26, 2026
e29a488
Merge pull request #121 from zk-coins/chore/rename-server-to-node
TaprootFreak May 26, 2026
bf05cee
test(db): DROP TABLE pending_inscriptions CASCADE in rollback test
TaprootFreak May 26, 2026
b852b53
fix(publisher): revert blanket status='failed' on broadcast error
TaprootFreak May 26, 2026
af52a7f
fix(db): tag account_history.source correctly for mint / send / recei…
TaprootFreak May 26, 2026
79f3335
test: close 100% line/function coverage gate for #113 stack
TaprootFreak May 26, 2026
ff9dbae
test: close remaining coverage gaps (13 lines)
TaprootFreak May 27, 2026
ced1ee7
test(api_remote): value-bearing field coverage + lockstep error-strin…
TaprootFreak May 27, 2026
8e0b691
Merge pull request #117 from zk-coins/release/mainnet-hardening
TaprootFreak May 27, 2026
5d1bf35
hotfix(migrations): revert ce4307c SQL comment edits to restore sqlx …
TaprootFreak May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ jobs:
# this, runs against the public Mutinynet API can take >60 s per
# test. Mirrors the pre-push hook.
ESPLORA_URL: http://127.0.0.1:1/api
# `USERNAME_DOMAIN` is required by the server bootstrap (no
# `USERNAME_DOMAIN` is required by the node bootstrap (no
# default — see node/src/main.rs and issue #95). The test value
# is irrelevant for the `info_returns_*` assertions (they only
# check non-empty + shape).
Expand Down Expand Up @@ -265,7 +265,7 @@ jobs:
# plus smart scheduling (slow tests start first). `--test-threads 1`
# is preserved — the repo invariant is that tests run serially to
# avoid testcontainers port races and shared-state pollution.
# `api_remote` is the live-DEV-server verification integration test
# `api_remote` is the live-DEV-node verification integration test
# (node/tests/api_remote.rs). It targets `https://dev-api.zkcoins.app`
# by default and is meant to run AFTER a deploy, from the `api-e2e`
# job in deploy-dev.yaml — not against whatever DEV currently runs
Expand Down Expand Up @@ -346,7 +346,7 @@ jobs:
#
# The `api_remote` integration test (node/tests/api_remote.rs)
# is excluded for the same reason as in `node-tests` above: it
# targets the live DEV server and belongs in the post-deploy
# targets the live DEV node and belongs in the post-deploy
# `api-e2e` job, not the hermetic coverage gate. The MVP coverage
# scope is measured by the rest of the suite, which covers the
# in-process axum handlers via oneshot().
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/deploy-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
reset_state:
description: 'Reset server state (clear blockchain data)'
description: 'Reset node state (clear blockchain data)'
required: false
type: boolean
default: false
Expand Down Expand Up @@ -126,12 +126,12 @@ jobs:
echo "::error::DEV /api/info never returned 200 within ~5 min after deploy"
exit 1

# Functional verification of the deployed DEV server.
# Functional verification of the deployed DEV node.
#
# The smoke test in `build-and-deploy` only proves the HTTP listener
# is bound; this job exercises all 15 routes end-to-end (read-only,
# negative-path, full mint→send→commit and username-claim roundtrips
# against the live server). Runs on the same self-hosted M3 Ultra
# against the live node). Runs on the same self-hosted M3 Ultra
# runner as `node-tests` / `coverage`, so sccache hits the warm
# cache populated by previous runs and the build itself stays
# well under a minute on a hot cache.
Expand All @@ -144,7 +144,7 @@ jobs:
RUSTC_WRAPPER: sccache
ZKCOINS_API_URL: https://dev-api.zkcoins.app
# The bootstrap `lazy_static`s panic if these are unset; the
# integration test only talks to the deployed server but the
# integration test only talks to the deployed node but the
# lib's panic-on-load behaviour is unconditional. Values are
# placeholders — nothing in the test path reads them.
USERNAME_DOMAIN: dev.zkcoins.app
Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/deploy-prd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
# produces no stdout for >60s. Mirrors deploy-dev.yaml; see
# the comment there for the failure mode that motivated this
# (PR #111 merge run 26419696840 — SSH dropped mid-recreate,
# exit 255, container actually came up server-side).
# exit 255, container actually came up node-side).
ssh -i ~/.ssh/deploy_key \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=8 \
Expand Down Expand Up @@ -100,7 +100,7 @@ jobs:
echo "::error::PRD /api/info never returned 200 within ~5 min after deploy"
exit 1

# Functional verification of the deployed PRD server. Mirrors the
# Functional verification of the deployed PRD node. Mirrors the
# Deploy DEV api-e2e job, but excludes the three roundtrip tests —
# they would consume real publisher UTXOs and write coins into the
# production SMT/MMR. `--skip _roundtrip_` is a substring match; the
Expand All @@ -115,7 +115,7 @@ jobs:
RUSTC_WRAPPER: sccache
ZKCOINS_API_URL: https://api.zkcoins.app
# The bootstrap `lazy_static`s panic if these are unset; the
# integration test only talks to the deployed server but the
# integration test only talks to the deployed node but the
# lib's panic-on-load behaviour is unconditional. Values are
# placeholders — nothing in the read-only test path reads them.
USERNAME_DOMAIN: zkcoins.app
Expand All @@ -135,6 +135,13 @@ jobs:
sccache --show-stats

- name: Run API E2E suite against PRD (skip roundtrips)
env:
# PRD image is MVP-only by policy (see Dockerfile FEATURES
# arg — "both DEV and PRD images ship the MVP-only feature
# set so the two environments run the identical binary").
# The gated address-list/lnurl tests skip cleanly instead of
# panicking the CI canary. Mirrors deploy-dev.yaml.
ZKCOINS_E2E_ALLOW_FEATURE_TRIMMED_SERVER: "true"
run: cargo test -p node --release --all-features --test api_remote -- --test-threads=1 --nocapture --skip _roundtrip_

- name: sccache stats (post-build)
Expand Down
20 changes: 10 additions & 10 deletions ARKADE_INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ any of them is a design-level rethink, not a tweak.
| **A2** | **No protocol changes to zkCoins or Arkade for A1.** The atomic-swap construction uses primitives both papers already specify: Shielded CSV §5.1 (shared accounts), §A.1.1 (time-locked nullifiers), §A.1.2 (atomic swap); Arkade Script HTLC template (`arkade-os/compiler`, `docs.arkadeos.com/learn/smart-contracts/hash-time-locked-contract`). | No 12th divergence to track in [`SPEC.md`](./SPEC.md) §15. No deviation from the Ark whitepaper. The integration adds wiring, not protocol changes. |
| **A3** | **Arkade operator and zkCoins federation remain independent trust domains.** A user holding a VTXO trusts the Arkade operator's rationality (Ark §5 Table 1). A user holding a zkCoins coin pegged to BTC trusts the zkCoins bridge (Phase 1 federation or Phase 2 BitVM2 setup). The two assumptions do not collapse into one; an atomic-swap counterparty may simultaneously occupy both roles, but the trust analyses stay separate. | Operating both an Arkade `arkd` instance and a zkCoins bridge node in the same datacentre is permitted; the security argument tracks each role independently. §8 is the canonical reference for which assumption applies where. |
| **A4** | **No confidential-VTXO work in the integration roadmap.** Bringing ZK privacy to Arkade VTXOs (§6.5) is genuine open research — Pedersen commitments + range proofs + redesigned forfeit mechanism + a PCD-style ZK validity proof per Arkade batch. Estimated 1–2 year paper-stage work; no existing protocol or implementation. | This document records confidential VTXOs as a research direction worth tracking but explicitly out-of-scope for any near-term zkCoins effort. If Arkade ships such a feature upstream, this section becomes a re-evaluation gate. |
| **A5** | **Pipeline use (§6.3) is layered on top of A1, not a separate primitive.** "BTC → Arkade → zkCoins → Arkade → BTC" decomposes into: Arkade boarding (Ark §4.5), an HTLC swap into zkCoins (A1), zkCoins-internal transfers, an HTLC swap back out, Arkade exit. Each step is independently specified and the pipeline composes them. | No new design work for the pipeline as long as A1 lands. The wallet-side UX of routing a user through the pipeline is `zk-coins/app` work, not a server-side primitive. |
| **A5** | **Pipeline use (§6.3) is layered on top of A1, not a separate primitive.** "BTC → Arkade → zkCoins → Arkade → BTC" decomposes into: Arkade boarding (Ark §4.5), an HTLC swap into zkCoins (A1), zkCoins-internal transfers, an HTLC swap back out, Arkade exit. Each step is independently specified and the pipeline composes them. | No new design work for the pipeline as long as A1 lands. The wallet-side UX of routing a user through the pipeline is `zk-coins/app` work, not a node-side primitive. |
| **A6** | **Cross-asset DEX (§6.6) is a v2 follow-up to A1.** A swap between an Arkade Asset (Arkade Labs' native-asset proposal) and a zkCoins asset is structurally identical to A1 with two field substitutions on each side. It does not require new crypto, but it does require the zkCoins multi-asset shared-account semantics from [`MULTI_ASSET.md`](./MULTI_ASSET.md) to be live, and Arkade Assets to be in production beyond beta. | Tracked as a v2 milestone; not in the initial A1 implementation scope. The first integration ships before chasing this. |

These mirror the lockedness pattern of [`MULTI_ASSET.md`](./MULTI_ASSET.md) §2
Expand Down Expand Up @@ -267,7 +267,7 @@ maturity.
### 6.1 Layer 0 — independent systems

A user holds an Arkade wallet pointing at some Arkade instance and a
zkCoins wallet pointing at a zkCoins server. The wallets do not
zkCoins wallet pointing at a zkCoins node. The wallets do not
interoperate. The user manually converts between BTC and zkCoins via
the bridge ([`BRIDGE_MVP.md`](./BRIDGE_MVP.md) or
[`BITVM_BRIDGE.md`](./BITVM_BRIDGE.md)) and between BTC and Arkade VTXOs
Expand Down Expand Up @@ -386,7 +386,7 @@ the initial boarding):
(no custody handoff possible without preimage reveal), bounded by
`T_e` on the Arkade side and the publisher's nullifier-publication
cadence on the zkCoins side.
- zkCoins-internal transfers: per [`SPEC.md`](./SPEC.md) — server-side
- zkCoins-internal transfers: per [`SPEC.md`](./SPEC.md) — node-side
compute correctness + Schnorr signature security.

§8 has the full trust-stacking analysis.
Expand Down Expand Up @@ -539,12 +539,12 @@ flow, failure modes, trust argument.
### 7.1 Parties and pre-conditions

- **User (Alice):** Arkade wallet pointing at some Arkade instance,
zkCoins wallet pointing at a zkCoins server, an existing zkCoins
zkCoins wallet pointing at a zkCoins node, an existing zkCoins
account.
- **Counterparty (Bob, "swap provider"):** Arkade wallet with VTXO
inventory, zkCoins server with sufficient inventory in some operator
inventory, zkCoins node with sufficient inventory in some operator
account. May be the same operator that runs the Arkade instance and
the zkCoins server, or a third party; the protocol does not require
the zkCoins node, or a third party; the protocol does not require
it.
- **Pre-agreed parameters:** swap amount `A`, provider fee `F`, the
on-Arkade HTLC timeout `T_htlc`, the zkCoins-side recovery timeout
Expand Down Expand Up @@ -785,7 +785,7 @@ reasoning about real-world security.
| Arkade operator (rational) | Operator follows protocol | Operator loses their own funds, not users'; users still exit (Ark §5 Table 1) |
| Arkade operator (malicious) | Operator deviates | NL, FL still hold; NS, AS, FS violations cost the operator, not users |
| Arkade MuSig2 covenant emulation | 1-of-n VTXO holders + operator follow signing protocol | VTXT well-formed (Ark §3.2, §4 Remark 4.5) |
| zkCoins server-side compute | Server runs the published Plonky2 circuit honestly | Per [`CONTRIBUTING.md`](./CONTRIBUTING.md) invariant 1 + invariant 2; closed test environment today, in-circuit verification long-term |
| zkCoins node-side compute | Node runs the published Plonky2 circuit honestly | Per [`CONTRIBUTING.md`](./CONTRIBUTING.md) invariant 1 + invariant 2; closed test environment today, in-circuit verification long-term |
| zkCoins Schnorr signatures | BIP-340 / secp256k1 secure | Standard Bitcoin cryptographic assumption |
| zkCoins publisher liveness | Some publisher willing to inscribe | Permissionless — alternative publishers can take the nullifier |
| zkCoins bridge Phase 1 (federation) | M-of-N federation honesty ([`BRIDGE_MVP.md`](./BRIDGE_MVP.md)) | M+ colluders can steal BTC reserves; zkCoins-side internal transfers unaffected |
Expand All @@ -800,7 +800,7 @@ The HTLC atomic swap of §7 requires:
fallback if violated).
- Bitcoin L1 (for confirmation of the inscriptions and any unilateral
Arkade exit).
- zkCoins server-side compute (so the publisher accepts and processes
- zkCoins node-side compute (so the publisher accepts and processes
the nullifier).
- BIP-340 Schnorr security (for both sides' signatures).

Expand All @@ -817,7 +817,7 @@ The pipeline composes:

- Arkade onboarding → Arkade rational operator + Bitcoin L1
- §7 HTLC swap into zkCoins → as in §8.2
- zkCoins-internal transfers → zkCoins server-side compute + Schnorr
- zkCoins-internal transfers → zkCoins node-side compute + Schnorr
- §7 HTLC swap out of zkCoins → as in §8.2
- Arkade exit → Arkade rational operator (cooperative) or pure Bitcoin
L1 (unilateral)
Expand Down Expand Up @@ -971,7 +971,7 @@ between systems.
successfully on the other side? Auto-nullify the recovery to free
the shared account?
- **Recommendation:** track as `zk-coins/app` wallet UX issue once
A1 lands; not a server-side concern.
A1 lands; not a node-side concern.

### 10.6 Multi-asset semantics in A1 (vs. A6)

Expand Down
16 changes: 8 additions & 8 deletions BITVM_BRIDGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,13 @@ Step 4. User (or their wallet, or any helper service) generates a
Bitcoin Light Client Proof showing MovetoVault is in the
canonical chain at depth ≥ 6.

Step 5. User submits to a zkCoins server an IssuanceProof request:
Step 5. User submits to a zkCoins node an IssuanceProof request:
- Their account state (initial, balance = 0)
- The Bitcoin LCP for MovetoVault
- The peg-in UTXO outpoint
- The non-inclusion proof against peg_in_consumed_smt

Step 6. zkCoins server (or the user's own prover, in a more
Step 6. zkCoins node (or the user's own prover, in a more
decentralised future) generates the IssuanceProof:
- Verifies the Bitcoin LCP
- Verifies the deposit amount equals the requested mint
Expand Down Expand Up @@ -436,13 +436,13 @@ needed.
| Vault sweeps multiple deposits without proper mint authorisation | Pre-signing prevents this (vault can only spend via pre-signed paths) |
| User's LCP is forged or stale | Circuit re-verifies LCP from headers; forgery requires breaking PoW |
| Bitcoin reorg removes MovetoVault | LCP becomes invalid; user retries after deeper confirmation |
| zkCoins server malicious — refuses to generate IssuanceProof | User goes to another zkCoins server (server-side compute is replicable; any party with the protocol can mint). This requires multiple zkCoins servers to exist; currently single-server. |
| zkCoins node malicious — refuses to generate IssuanceProof | User goes to another zkCoins node (node-side compute is replicable; any party with the protocol can mint). This requires multiple zkCoins nodes to exist; currently single-node. |

### 5.5 The "user pays an operator to mint" alternative

The above puts proof generation on the user side (or their chosen
zkCoins server). A simpler MVP variant: the federation includes
zkCoins-server operators who automatically generate the IssuanceProof
zkCoins node). A simpler MVP variant: the federation includes
zkCoins-node operators who automatically generate the IssuanceProof
when they see a confirmed MovetoVault. This is more centralised but
operationally simpler. Trade-off documented as open question §10.

Expand Down Expand Up @@ -540,7 +540,7 @@ A realistic implementation sequence:
| 3 | Bitcoin Light Client gadget in circuit | 2–3 weeks | Phase 0 |
| 4 | `IssuanceProof` circuit branch | 2 weeks | Phase 0, Phase 3 |
| 5 | `BurnProof` circuit branch | 1–2 weeks | Phase 0 |
| 6 | Bridge server-side state (peg_in_consumed_smt, burned_coins_smt, pending_payouts) | 1 week | Phase 4, Phase 5 |
| 6 | Bridge node-side state (peg_in_consumed_smt, burned_coins_smt, pending_payouts) | 1 week | Phase 4, Phase 5 |
| 7 | Federation node software (signer + operator + watchtower roles) | 4–6 weeks | Phase 2a, Phase 6 |
| 8 | Integration testing with all federation members on signet | 2–4 weeks | Phase 7 |
| 9 | Mainnet launch | TBD | Phase 8 |
Expand Down Expand Up @@ -669,9 +669,9 @@ Zcash's t/z address model.

## 10. Open Questions

1. **Who pays for proof generation in Phase 4–5?** Server-side
1. **Who pays for proof generation in Phase 4–5?** Node-side
(zkCoins operator) is operationally simpler; user-side
(decentralised) is more trustless. Default: server-side for v1
(decentralised) is more trustless. Default: node-side for v1
with a clear migration path to user-side later.

2. **Federation size and composition.** Minimum credible: 5
Expand Down
Loading
Loading