Skip to main content
For the complete documentation index, see llms.txt

ZK Loan DApp

ZK Loan 예제는 신용 데이터가 사용자 기기를 벗어나지 않은 채로 비공개 신용 데이터에 따라 대출 신청을 평가하는 방법을 보여줍니다. 은행 형태의 attestation 제공자가 오프체인에서 신용 프로필에 서명하고, 스마트 컨트랙트는 영지식 circuit 안에서 서명을 검증한 뒤 대출 상태와 승인 금액만 ledger에 기록합니다.

신용 점수, 월 소득, 재직 기간, attestation 서명, 사용자 PIN은 모두 비공개로 유지됩니다. 블록체인은 사용자가 어떤 등급에 해당하는지만 알 뿐, 그 이유는 결코 알지 못합니다.

What this DApp demonstrates​

이 예제는 최소 DApp보다 의도적으로 규모가 큽니다 — 여러 Midnight 구성 요소를 한곳에 엮어냅니다.

  • Witness가 제공하는 private input: TypeScript 코드가 proving 시점에 신용 데이터를 영지식 prover에 공급하되 온체인에는 노출하지 않습니다.
  • circuit 안에서의 Schnorr-on-Jubjub 서명 검증: 직접 작성한 Compact 모듈(표준 라이브러리에 검증기가 추가되면 교체할 예정)이 신용 데이터가 등록된 attestation 제공자에게서 왔음을 증명합니다.
  • PIN에서 파생한 온체인 신원: witness가 공급한 사용자 secret을 비밀 PIN과 함께 해싱해 사용자별 공개 키를 만듭니다. 이 키는 secret도 PIN도 드러내지 않지만, 연결 불가능한 신원이 아니라 가명입니다. 한 사용자의 모든 대출은 loans 안에서 그 하나의 키 아래 모이고, attestation 제공자는 신용 데이터와 함께 그 키의 해시에 서명하며, changePin은 같은 트랜잭션에서 이전 키와 새 키를 함께 지정합니다.
  • 2단계 승인 흐름: 사용자 등급 한도를 넘는 요청은 Proposed로 저장되며, 수락하거나 거절하려면 이어서 respondToLoan을 호출해야 합니다.
  • Witness에서 파생한 관리자와 신원: 호출자 신원(관리자와 사용자 모두)은 32바이트 witness secret에서 파생됩니다. 컨트랙트는 ownPublicKey()를 사용하지 않습니다. 관리자 secret 보유자만 제공자를 등록하거나, 사용자를 차단하거나, 관리자 역할을 교체할 수 있습니다. 사용자 secret 보유자만 해당 사용자의 대출을 요청하거나 수락하거나 거절할 수 있습니다.

Project structure​

이 예제는 midnight-examples 모노레포의 examples/zk-loan에 있습니다.

examples/zk-loan/
├── contract/
│ ├── zk-loan.compact # 대출 로직, 자격 등급, ledger
│ ├── schnorr.compact # circuit 내 서명 검증
│ ├── witnesses.ts # private state와 witness (TypeScript)
│ └── index.ts # witness를 붙여 컴파일한 컨트랙트
├── attestation-api/ # 신용 데이터에 서명하는 REST server
│ └── {signing,server,index,types}.ts
├── src/
│ ├── test/zk-loan.test.ts # Midnight 네트워크 대상 엔드투엔드 스위트
│ ├── test/zk-loan.sim.test.ts # 컨트랙트 단위 테스트, 인메모리 시뮬레이터
│ └── {config,providers,wallet}.ts # 공용 테스트 하네스
├── ui/ # React + Vite UI (Lace wallet, 기본값 Preprod)
└── compose.yml # 로컬 노드 + indexer + proof server

Prerequisites​

  • Node.js 22: SDK가 Iterator helper를 사용하므로, 노드 20은 첫 동기화에서 크래시합니다
  • Corepack — Yarn 4용: corepack enable
  • Docker와 Docker Compose — 로컬 네트워크용
  • Compact toolchain: compact devtool로 설치한 뒤 compact update 0.31.1을 실행하고 compact compile --version으로 확인하세요
  • Midnight Lace wallet — UI에서만 필요합니다. 설치 가이드를 참조하세요

이 예제는 Compact 언어 0.23, 컴파일러 0.31.1, @midnight-ntwrk/midnight-js-* 4.1.1, wallet SDK 1.2.0으로 검증되었습니다. compose.yml의 로컬 네트워크는 midnightntwrk/midnight-node:1.0.0, midnightntwrk/indexer-standalone:4.3.3, midnightntwrk/proof-server:8.1.0을 실행합니다.

Set it up​

1. Install and compile​

git clone https://github.com/midnightntwrk/midnight-examples.git
cd midnight-examples
yarn install && yarn compile
cd examples/zk-loan

yarn compile은 contract/managed/zk-loan/을 생성합니다 — JavaScript 바인딩, prover 및 verifier 키, ZK IR입니다.

2. Run the full flow on a local network​

yarn env:up # node, indexer and proof server from compose.yml
yarn wait:dust # wait until the test wallet can pay fees
yarn test:local

이 스위트는 attestation API를 인프로세스로 띄운 뒤 컨트랙트를 배포하고, API의 키를 관리자로 등록하고, 두 차입자에게 여러 등급의 대출을 요청하며, 변조된 신용 데이터·관리자가 아닌 호출·차단된 차입자·전임 관리자가 거부되는지 확인합니다. 성공하는 호출마다 실제 증명을 생성하므로 몇 분이 걸립니다. 네트워크는 yarn env:down으로 중지하세요.

컨트랙트의 시뮬레이터 테스트는 네트워크가 필요 없습니다.

yarn test:sim

3. Run the attestation API​

attestation API는 Jubjub 위에서 Schnorr signature로 신용 데이터에 서명하고, 컨트랙트는 ZK circuit 안에서 그 서명을 검증합니다.

PROVIDER_SECRET_KEY="$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')" \
yarn attestation:start

이 API는 4000 포트에서 수신하며(PORT로 변경), Provider ID(기본값 1)와 공개 키의 x, y 값을 출력합니다. 이 값들은 관리자가 registerProvider로 온체인에 등록하는 값입니다. PROVIDER_SECRET_KEY는 잘 보관하세요. 이 키가 없으면 재시작할 때마다 새 키가 생성되어 온체인 등록이 무효화됩니다.

4. Run the UI​

ui/의 UI는 Midnight Lace wallet을 통해 연결하며, 기본값은 Preprod입니다. Lace는 로컬 undeployed 체인의 트랜잭션 잔액을 맞추거나 서명할 수 없으므로, 로컬에서 반복 작업을 할 때는 테스트 스위트를 사용하세요.

cd ui
yarn dev # http://localhost:5173

이 UI는 컨트랙트를 배포하거나 참여하고, (3단계의 attestation API에서 서명을 받아) 대출을 요청하고, 제안을 수락하거나 거절할 수 있습니다. 다만 attestation 제공자는 등록할 수 없습니다. UI에서 직접 배포한 컨트랙트에서는 다른 무엇으로도 등록할 수 없습니다. 관리자 역할은 그 브라우저 탭 메모리에만 존재하는 secret에서 나오고, UI는 이를 내보낼 수 없기 때문입니다. 그래서 UI로 배포한 컨트랙트에서는 모든 대출 요청이 Attestation provider not registered로 실패합니다. 대출이 처음부터 끝까지 진행되는 과정을 보려면 2단계의 테스트 스위트를 사용하세요. 페이지를 새로고침해도 secret이 사라지며, 그와 함께 대출 내역과 관리자 역할도 사라집니다. 설정과 브라우저별 세부 사항은 UI README를 참조하세요.

Learn how it works​

3부작 튜토리얼이 이 예제의 코드를 차례로 설명합니다: witness/disclose 분리, circuit 내 Schnorr 검증, PIN에서 파생한 신원, 온체인 테스트 스위트입니다.