Nullorm

circom을 이용한 zkp 생성 및 검증하기 본문

Web3/Web3 수학&암호학

circom을 이용한 zkp 생성 및 검증하기

Null0rm 2025. 1. 9. 12:37
반응형

Terminology

  • signal
    • input, output, intermediate로 정의할 수 있음
    • main component에서만 public, private구분하면 됨
  • constraint
    • signal들의 관계를 나타낸 것
    • constraint = set of gates 정도로 이해하고있긴 함
    • A * B + C = 0의 형태로 나타내야 함(근데 컴파일 과정에서 이렇게 변환해준다는 말인듯)
      • R1CS꼴로 나타내려면 이렇게 해야 함
  • witness
    • constraint를 만족하는 input signal의 set으로 이해하고 있음

Circom

circom을 통해 circuit을 컴파일하고, r1cs 형식으로 formatting하여 zkp를 생성 및 검증할 수 있다. 

Prerequisite: circom, snarkjs 설치

0. circom을 통한 zkp 생성 순서

  1. upside.circom 작성
  2. upside.circom 컴파일 (by circom)
    output files: upside_cpp / upside_js / upside.sym / upside.r1cs
  3. upside.circom에 대한 witness 생성(valid/invalid한 input에 대한 signal set을 binary로 생성)
    output files: witness.wtns
  4. Groth16을 이용한 zkp 생성 (by snarkjs)
    1. trusted setup을 통한
      1. Powers of Tau: general한 초기 설정
      2. Phase2: circuit 특화 설정

1. upside.circom 작성

각종 signal에 대한 constraint를 표현한 upside.circom작성

pragma circom 2.0.0;

template Add() {
    signal input a[3];

    a[0] === a[1] + a[2];
}

component main  = Add();

2. 컴파일 및 input값 generate

circom compiler를 통해 compile

circom upside.circom --wasm --c --sym --r1cs
  • wasm: witness 생성 위한 wasm 디렉토리 생성 (upside_js)
  • c: cpp witness 생성 위한 cpp 디렉토리 생성 (upside_cpp)
  • sym: 디버깅용 symbolic 파일 생성 (upside.sym)
  • r1cs: constraint를 R1CS형식으로 변환한 파일 생성 (upside.r1cs)

verifier에 verify를 하기 위한 input값 생성(json으로)

echo '{"a": [3, 1, 2]}' >> input.json

3. witness 생성 (아래 두 방법 중 택1)

constraint를 만족하는 input값을 알고있다는 것을 증명하기 위한 signal set인 witness.wtns를 생성. snarkjs에서 zkp를 생성할 때 사용한다.

3-1. wasm

cd upside_js/
node generate_witness.js upside.wasm input.json witness.wtns # 실행

3-2. cpp(wasm보다 큰 circuit 처리속도 빠름)

cd upside_cpp
make # Makefile 통해 build
./upside input.json witness.wtns # 실행

4. zkp 생성 및 검증

circuitwitness.wtns에 대한 증명, 검증을 하는 단계

Groth16 protocol

zk-SNARK의 가장 많이 사용하는 구현.

trusted setup, prove, verify 이렇게 세 단계로 나뉜다.

1. Trusted Setup

Power of Tau, Phase2 이렇게 두 단계로 나뉜다.

1-1. Power of Tau

groth16을 사용하기 위한 기초적인 환경 세팅 단계

1-1-1. Lagrange Interpolation, 다항식 연산 효율적 수행을 위한 기초 데이터 생성
snarkjs powersoftau new bn128 12 pot12_0000.ptau -v

- bn128: 사용된 곡선
- 12: circuit 크기 (2122^{12}212크기까지 처리 가능)
- pot12_0000.ptau 파일 생성

1-1-2. contribution: 보안 강화를 위해, 여러 사람 또는 system의 contrbution을 통해 data를 생성(각 참여자는 개인 entropy를 추가)
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v

- —name을 통해 entropy 추가 X, 이거 하면 입력하라고 뜸
- pot12_0001.ptau 파일로 내용 업데이트

1-1-3. Phase 2로 전환
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v

- pot12_final.ptau 생성됨

1-2. Phase2

제작한 circuit에 최적화된 data를 생성하는 단계. Powers of Tau에서 생성된 데이터를 바탕으로, 각 circuit의 constraint를 만족하기 위한 proving key, verification key를 생성하는 단계. R1CS를 GQP로 변환하는 과정도 포함된다.

1-2-1. .zkey 파일 생성
snarkjs groth16 setup multiplier2.r1cs pot12_final.ptau upside_0000.zkey

- r1cs 파일을 이용해 data를 생성함
- proving key: Prover가 zkp생성할 때 필요한 key
- verification key: verifier가 zkp를 검증하는 데 필요한 key
- proving key, verification keyupside.zkey

1-2-2. contribution

이 또한 여러 사람, 시스템이 기여할 수 있다.

snarkjs zkey contribute upside_0000.zkey upside_0001.zkey --name="Contribute" -v
1-2-3. 검증키 추출
snarkjs zkey export verificationkey upside_0001.zkey verification_key.json

- verifier가 verify과정에 사용할 수 있는 verification key를 추출

2. 증명 생성

snarkjs groth16 prove upside_0001.zkey witness.wtns proof.json public.json

- zkey, witness.wtns 이용해 zkp, public input 생성

3. 증명 검증

3-1. local에서 검증
snarkjs groth16 verify verification_key.json public.json proof.json

- ok 라고 나오면 증명 된 것임

3-2. verifier.sol 만들기
snarkjs zkey export solidityverifier upside_0001.zkey verifier.sol

- verifier.sol 파일 생성됨 (solidity)

snarkjs generatecall

- witness.wtns를 calldata형식으로 만들어주는 명령어
- verifyProof()함수 호출해서 True 반환하면 성공

 

(그런데 .sol에서 public signal의 수가 0일 때 함수의 매개변수로 uint[0]을 넣도록 코드가 작성되는데, 이 경우 solc에서 error를 띄우기 때문에, 이를 해결할 방법을 찾아야할 것 같긴 하다.)

반응형