Skip to content

ChronoCoders/arcmint

Repository files navigation

ArcMint

DOI

Federated accountable-anonymous e-cash on Bitcoin.

ArcMint issues Cryptographic Bearer Notes (CBNs) — private, threshold-signed bearer instruments backed by BTC and redeemable via the Lightning Network. No wrapped token. No custodian. No trusted third party. Target: institutional OTC desk settlement and high-value private payments.

Live on Bitcoin mainnet. Federation running. Notes in production.


Table of Contents


How It Works

CBN Lifecycle

  1. Registration — User samples identity scalar θ_u ∈ Z_q, computes commitment T_u = g^θ_u, registers with the gateway, and receives an HMAC token bound to θ_u.
  2. Issuance — Cut-and-choose: user submits k candidates with Pedersen bit commitments, coordinator challenges k−1 to open, threshold FROST signs the unopened candidate. Notes carry a 90-day validity window.
  3. Spending — User presents note + signature; merchant issues a random challenge vector; user produces a selective opening proof; coordinator checks the double-spend registry atomically.
  4. Double-spend detection — A second spend of the same serial triggers identity recovery: XOR of blinding factors at differing-challenge positions recovers θ_u, identifying the cheating party without touching honest users.
  5. Anchoring — Merkle roots of issued and spent serial sets are committed to Bitcoin via OP_RETURN every 600 seconds.

Cryptographic Primitives

Primitive Choice Rationale
Group Ristretto255 Prime-order, cofactor-free, DL-hard
Threshold signing FROST (frost-ristretto255) EUF-CMA secure, no trusted dealer in production
Commitments Pedersen C = g^b · h^r Perfectly hiding, computationally binding
MAC HMAC-SHA256 Gateway token binding
Hash SHA-256 with domain separation arcmint:identity:v1, arcmint:note:v1, arcmint:theta:v1

Architecture

Workspace Crates

Crate Role
arcmint-core Cryptographic primitives — FROST, Ristretto255, Pedersen commitments
arcmint-federation Coordinator and Signer binaries
arcmint-gateway User registration, HMAC token issuance, IP rate limiting
arcmint-merchant Note verification, spend proof verification, payment acceptance
arcmint-wallet CLI wallet — identity, issuance, spending, atomic file writes
arcmint-loadtest Real HTTP load testing — register → issue → spend → double-spend
arcmint-adversary Protocol attack testing
arcmint-dkg Distributed key generation ceremony tooling
arcmint-monitor Ratatui TUI — Bitcoin node, service health, anchoring status

Services

Service Port Role
coordinator 7000 Orchestrates signing rounds, double-spend registry, anchoring
signer-1 7001 FROST signer with individual key package
signer-2 7002 FROST signer with individual key package
signer-3 7003 FROST signer with individual key package
gateway 7002 (internal) User registration, token issuance, identity resolution
merchant 7003 (internal) Note and spend proof verification
bitcoind 18443 Bitcoin Core (regtest) or mainnet pruned
lnd 8080, 10009 LND node 1
lnd-2 10009 (internal) LND node 2
certgen CA + mTLS certificate generation (init container)

Separation Invariants

  • Signers only trust requests authenticated by the coordinator's secret. They do not interact with end users.
  • The coordinator observes only blinded commitments, serial numbers, and spend proofs — never user identities.
  • The gateway manages identity registration and rate limiting but never sees note serials or spend proofs.
  • The wallet never shares long-term secrets; it communicates exclusively via the public HTTP APIs.
  • The merchant is untrusted by the federation; it must prove note validity through the coordinator's spend verification API.

All inter-service communication uses mutual TLS. The certificate authority is generated by the certgen init container; each service has its own cert signed by the federation CA.


Security Properties

Property Status Mechanism
Unforgeability Proven Reduction to DL hardness + FROST EUF-CMA
Traceability Proven Pedersen binding — double-spender identified with probability 1 − 2^{−ℓ}
Blindness Heuristic Holds under static corruption; formal proof under adaptive corruption is an open problem
Double-spend prevention Enforced BEGIN IMMEDIATE transaction with rollback coverage; atomic registry check
Secret isolation Enforced zeroize on all sensitive types (Scalar, BlindingFactor, UnsignedNote)
Timing safety Enforced subtle::ConstantTimeEq for all secret comparisons; all OPERATOR_SECRET variants checked without early return
Replay protection Enforced Spend proofs bound to merchant nonce + timestamp
Note expiry Enforced 90-day validity window; reissuance protocol included
Protocol versioning Enforced protocol_version: u8 on all message types
Input bounds Enforced MAX_CANDIDATES = 512 on issuance
Metrics authentication Enforced Bearer OPERATOR_SECRET required on all /metrics routes

Completed Audit Resolutions

  • C1 Hardcoded Bitcoin RPC credentials → env vars, no defaults
  • C2 Weak default secrets → blanks with comments; validate_secret() enforced at startup (min 32 chars)
  • C3 TLS validation disabled in test clients → danger_accept_invalid_certs removed; CA cert required
  • C4 Unauthenticated Prometheus metrics → bearer token required on all /metrics routes
  • H1 Services binding 0.0.0.0 → default 127.0.0.1; Docker sets BIND_ADDR=0.0.0.0
  • H2 MERCHANT_INSECURE=true plaintext option → removed; TLS mandatory

Quick Start (Docker)

Prerequisites

  • Docker
  • Docker Compose v2

Steps

# 1. Configure secrets
cp .env.example .env
# Edit .env — all secrets must be ≥ 32 random bytes; no dev- prefixes in production

# 2. Build and start
docker compose up --build

# 3. Run the load test to verify the stack
docker compose --profile loadtest run --rm arcmint-loadtest

All services expose /health for liveness checks wired into Docker health checks.

Default endpoints (host-side ports configurable via .env):

Service URL
Coordinator https://localhost:7000
Signer 1 https://localhost:7001
Signer 2 https://localhost:7002
Signer 3 https://localhost:7003
Gateway https://localhost:7002
Merchant https://localhost:7003

Wallet CLI

arcmint-wallet register --identity-id alice
arcmint-wallet generate-note --denomination 1000 --k 32
arcmint-wallet list-notes
arcmint-wallet spend --serial <note-serial-hex> --merchant-url https://localhost:7003

Key Management

Development

In development, FROST keys are generated by the keygen binary using a trusted dealer. Keys are written into a shared volume and mounted read-only into signers, coordinator, and merchant. Development keys are not suitable for production. The trusted dealer path is gated behind #[cfg(feature = "dev-keygen")].

Production (DKG Ceremony)

Production deployments must establish FROST keys via the distributed key generation ceremony using dkg_coordinator and dkg_participant. INIT_DEV_KEYS must be disabled.

Ceremony procedure:

  1. Each of the n federation operators runs on a separate machine in a separate jurisdiction. The internal CA certificate is distributed to all operators out-of-band.
  2. Operator 1 starts the ceremony with --create-ceremony, specifying the agreed threshold t and signer count n.
  3. All operators run dkg_participant with their --participant-id, --operator-token, and coordinator URL.
  4. On completion, each operator prints a transcript hash. Operators verify the hash out-of-band (voice call or secure channel). A differing hash indicates a compromised ceremony — abort and restart.
  5. Each operator's signer_{id}_key.json must be stored at mode 0600 and never transmitted off-machine. The shared public_key.json is safe to distribute to coordinator, gateway, and merchant.

Key rotation requires a full DKG ceremony. Old keys remain active until the new ceremony is confirmed live across all services.

Operator tokens must be high-entropy (≥ 32 random bytes), never reused across ceremonies, and destroyed after the ceremony completes.


TLS and Certificates

Development (Docker)

The certgen init container generates an internal CA, signer server certificates, coordinator and gateway server certificates, and mTLS client certificates. All services mount the /certs volume automatically.

Production Gateway TLS

Set ACME_DOMAIN to the externally reachable hostname for automatic Let's Encrypt certificate provisioning. The internal mesh (signers and coordinator) continues to use the internal CA.

ACME_DOMAIN=gateway.example.com
ACME_EMAIL=ops@example.com
ACME_CACHE_DIR=/var/lib/arcmint/acme

Certificate Rotation

Rotate internal certificates by re-running the certgen service and performing a rolling restart of signers, coordinator, and gateway. Long-lived clients must refresh their TLS configuration to trust the new CA before old certificates are revoked.


Bitcoin Anchoring

The coordinator anchors federation state to Bitcoin via OP_RETURN every 600 seconds.

Wallet setup:

bitcoin-cli createwallet anchor
bitcoin-cli -rpcwallet=anchor getnewaddress
# Fund the address with ≥ 0.001 BTC
bitcoin-cli -rpcwallet=anchor dumpprivkey <address>

Set ANCHOR_WALLET_WIF and ANCHOR_CHANGE_ADDRESS in .env.

Cost estimate: ~500–2000 satoshis per anchor at normal fee rates (1 input, 2 outputs, OP_RETURN).


Monitoring

docker compose --profile monitoring up
UI URL Credentials
Prometheus http://localhost:9090
Grafana http://localhost:3000 admin / GRAFANA_PASSWORD from .env

Auto-provisioned dashboards:

  • arcmint-federation.json — issuance, sessions, DB latency, anchor status
  • arcmint-lightning.json — channel balances, LN payment latency, mint-in/mint-out
  • arcmint-security.json — double-spend attempts, verification failures, rate limiting (default home)

/metrics endpoints are only exposed on the internal Docker network.


Load Testing

# Smoke (CI)
cargo run -p arcmint-loadtest -- run-all --config loadtest/smoke.toml --output report.json

# Standard (pre-release)
cargo run -p arcmint-loadtest -- run-all --config loadtest/standard.toml

# Stress
cargo run -p arcmint-loadtest -- run-all --config loadtest/stress.toml

# Docker
docker compose --profile loadtest up arcmint-loadtest

# Spend race only
cargo run -p arcmint-loadtest -- run-spend-race --config loadtest/standard.toml

Service-level objectives:

Metric Smoke Standard Stress
issuance p99 5000 ms 2000 ms 10000 ms
spend p99 3000 ms 1000 ms 5000 ms
signer RPC p99 1000 ms 500 ms 2000 ms
lightning settlement p99 10000 ms 5000 ms 30000 ms
signing failure rate ≤ 1% ≤ 0.1% ≤ 5%
spend false negatives 0 0 0

spend_false_negatives_allowed = 0 is never relaxed regardless of configuration.


Adversarial Testing

cargo run -p arcmint-adversary -- run-all \
  --coordinator-url https://localhost:7000 \
  --gateway-url https://localhost:7002 \
  --merchant-url https://localhost:7003 \
  --signer-urls https://localhost:7001 \
  --output report.json

The adversary suite covers:

Attack Expected outcome
attack_forged_signature Rejected — invalid FROST signature
attack_malformed_note_missing_pairs Rejected — empty commitment pairs
attack_malformed_note_wrong_denomination Rejected — denomination tampered post-signing
attack_registry_bypass_skip_issued_check Rejected — serial not in issued registry
attack_wrong_commitment_opening Rejected — incorrect opening in spend proof
attack_challenge_precomputation Rejected — proof generated for all-zero challenge
attack_double_spend Rejected + identity recovered
attack_double_spend_different_merchants Rejected + identity recovered
attack_theta_recovery_verification Identity secrets not recoverable from honest notes
attack_replay_spent_note Rejected — serial already spent
attack_flood_issuance Rate limited at gateway
attack_signer_direct_access Rejected — no coordinator client certificate
attack_malformed_issuance_reveal Rejected — InvalidProof
attack_expired_note Rejected — note past validity window

A PASS means the system rejected the attack as expected. A FAIL indicates the system accepted an attack that should have been rejected and represents a potential vulnerability.


Integration Tests

# Full workspace unit tests
cargo test --workspace

# End-to-end integration tests (requires running stack)
cargo test -p arcmint-integration --test full_flow -- --ignored

# Lightning integration tests (requires bitcoind + LND regtest)
docker compose up bitcoind lnd lnd-init miner
cargo test -p arcmint-integration --test full_flow -- --ignored --nocapture

Environment Variables

Variable Default Description
COORDINATOR_PORT 7000 Coordinator HTTP listen port
SIGNER1_PORT 7001 Host port for signer-1
SIGNER2_PORT 7002 Host port for signer-2
SIGNER3_PORT 7003 Host port for signer-3
GATEWAY_PORT 7002 Host port for gateway
MERCHANT_PORT 7003 Host port for merchant
FEDERATION_DB federation.db SQLite path for signer registry
GATEWAY_DB gateway.db SQLite path for gateway DB
MERCHANT_DB merchant.db SQLite path for merchant DB
FROST_KEY_FILE frost_key.json FROST key package file for signers
FROST_PUBKEY_FILE frost_pubkey.json FROST public key package file
SIGNER_ID 1 Signer identifier (unique per signer)
GATEWAY_SECRET Shared HMAC secret for gateway tokens (≥ 32 bytes)
FEDERATION_SECRET Gateway ↔ federation resolve secret (≥ 32 bytes)
COORDINATOR_SECRET Coordinator ↔ signer secret (≥ 32 bytes)
OPERATOR_SECRET Operator API secret; comma-separated for rotation (≥ 32 bytes each)
GATEWAY_RESOLVE_URL https://localhost:7002/resolve Coordinator callback into gateway
SIGNER_URLS https://localhost:7001 Comma-separated signer URLs
ANCHOR_INTERVAL_SECS 600 Bitcoin anchoring interval
ANCHOR_WALLET_WIF WIF private key for anchor transactions
ANCHOR_CHANGE_ADDRESS Change address for anchor transactions
ALLOWED_DENOMINATIONS (any) Comma-separated allowed msats; empty = accept any
BITCOIN_RPC_URL Bitcoin Core RPC endpoint
BITCOIN_RPC_USER Bitcoin Core RPC username
BITCOIN_RPC_PASS Bitcoin Core RPC password
COORDINATOR_URL https://localhost:7000 Coordinator base URL
GATEWAY_URL https://localhost:7002 Gateway base URL
WALLET_DIR ~/.arcmint Default wallet directory
MAX_REGISTRATIONS_PER_HOUR 10 Max registrations per IP per hour
TLS_CERT_FILE TLS certificate (gateway/signers)
TLS_KEY_FILE TLS private key (gateway/signers)
TLS_CA_FILE CA certificate for signer mTLS
INTERNAL_CA_FILE Internal CA for mTLS clients
COORDINATOR_TLS_CERT Coordinator TLS server certificate
COORDINATOR_TLS_KEY Coordinator TLS private key
COORDINATOR_CLIENT_CERT Coordinator client cert for mTLS to signers
COORDINATOR_CLIENT_KEY Coordinator client key for mTLS to signers
COORDINATOR_CN arcmint-coordinator Expected coordinator client cert CN
GATEWAY_CLIENT_CA CA verifying gateway client certificate
GATEWAY_CLIENT_CERT Gateway client cert for mTLS to coordinator
GATEWAY_CLIENT_KEY Gateway client key for mTLS to coordinator
GATEWAY_CN arcmint-gateway Expected gateway client cert CN
ACME_DOMAIN Public domain for Let's Encrypt TLS
ACME_EMAIL Contact email for Let's Encrypt
ACME_CACHE_DIR /var/lib/arcmint/acme ACME account and cert cache
ACME_STAGING false Use Let's Encrypt staging environment

Known Limitations

  • Trust assumption: An honest majority of federation signers is required. Collusion of a threshold-sized quorum can break unlinkability and forge notes.
  • Anonymity set: Effective anonymity is bounded by issuance volume and denomination structure. Highly distinctive denominations reduce the anonymity set.
  • Blindness proof: The blind threshold Schnorr construction holds heuristically under static corruption. A formal proof under adaptive corruption is an open research problem.
  • Side channels: The implementation does not mitigate all side channels (timing, cache, network metadata). Deployments in sensitive environments should apply additional OS and network hardening.
  • Anchor availability: OP_RETURN commitments timestamp the registry state but do not guarantee long-term data availability. Operators are responsible for archiving and monitoring anchor status.

Code Standards

  • No unsafe code anywhere in the workspace
  • All SQL queries use sqlx parameterized binds
  • mTLS between all internal services with CN verification
  • Rust edition 2021; zero compiler warnings required
  • All error paths return Result — no silent failures
  • All secrets implement Zeroize and are cleared on drop

License

Licensed under either of

at your option.

About

Federated accountable-anonymous e-cash on Bitcoin.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors