위험 경고: '가상화폐', '블록체인'이라는 이름으로 불법 자금 모집 위험에 주의하세요. — 은행보험감독관리위원회 등 5개 부처
검색
로그인
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
시장 동향 보기
제로 지식 증명 코딩 학습: libsnark 시작하기
安比(SECBIT)实验室
特邀专栏作者
2020-01-03 10:19
이 기사는 약 17557자로, 전체를 읽는 데 약 26분이 소요됩니다
이 일련의 기사를 통해 모든 개발자가 단기간에 libsnark를 시작하고 libsnark의 기본 개념을 단계별로 이해할 수 있기를 바랍니다.

이기사작성자: p0n1@安比计划

libsnark는 현재 zk-SNARKs 회로를 구현하는 가장 중요한 프레임워크이며 많은 개인 거래 또는 프라이버시 컴퓨팅 관련 프로젝트에서 널리 사용되고 있으며 그 중 Zcash가 가장 유명합니다. Zcash는 Sapling 버전이 업그레이드될 때까지 libsnark를 사용하여 회로를 구현했습니다(나중에 bellman으로 대체됨). libsnark가 zk-SNARKs 기술의 최초 대규모 적용을 지원하고 촉진하여 영지식 증명 기술의 최신 이론과 엔지니어링 구현 사이의 격차를 메운다고 해도 과언이 아닙니다.

이 일련의 기사를 통해 모든 개발자가 단기간에 libsnark를 시작하고, libsnark의 기본 개념을 단계별로 이해하고, zk-SNARKs 회로를 개발하는 방법을 배우고, 증명 생성 및 검증을 완료하고, 마지막으로 실제 비즈니스에 적용된 영지식 증명을 변환합니다.

1. zk-SNARKs 및 libsnark의 배경 소개

영지식 증명은 현재 가장 유망하고 창의적인 암호 블랙 기술일 수 있습니다. 그리고 zk-SNARKs는 일종의 영지식 증명 체계의 약어이며 전체 이름은 영지식 간결한 비대화형 지식 인수입니다. 이 이름은 거의 모든 기술적 특징을 포함하고 있습니다. 즉 명제의 정확성은 다른 정보를 노출하지 않고 증명할 수 있으며 최종 생성된 증명은 간결(Succinct)하여 최종 생성된 증명이 충분히 작다는 의미이며, 계산량과는 관계가 없으며 상수입니다. 평이한 영어로는 사생활을 전혀 노출하지 않고 이론적으로 다른 모든 사람에게 무엇인가를 증명할 수 있으며, 생성된 증명은 그 내용을 증명하는 데 필요한 계산량에 관계없이 크기가 작고 검증 비용이 매우 낮습니다. 사실이 되기에는 너무 좋은 것 같습니다!

zk-SNARK는 개인 정보 보호, 블록체인 확장, 검증 가능한 컴퓨팅 등과 같은 다양한 시나리오에 적용될 수 있습니다. 이 기사는 zk-SNARKS 및 영지식 증명에 대한 이론적 세부 사항을 소개하지 않습니다.

zk-SNARK에 대한 Vitalik의 유명한 3개 블로그 게시물 등.

또는 Xiang Cheng @HUST가 쓴 글 읽어보기"간단한 용어로 영지식 증명의 Zk-SNARK", 그리고 Dongze는 썼다"영지식 증명에 대해 이야기하기 II: 단기 상호작용 증명(SNARK)"

물론 Ambi Lab에 관심을 가져 주셔서 감사합니다.그리고그리고"zk-SNARK 처음부터 배우기" 시리즈, 그리고 Ambi Labs에서 유지"제로 지식 증명 학습 리소스 요약"에서 더 많은 정보를 찾아보세요.

이 기사의 주인공인 libsnark는 SCIPR Lab에서 개발 및 유지 관리하는 zk-SNARKs 애플리케이션 개발을 위한 C++ 코드 라이브러리입니다. libsnark 엔지니어링 구현의 이론적 기반은 최근 몇 년 동안(특히 2013년 이후) 영지식 증명, 특히 zk-SNARK의 방향에 대한 일련의 중요한 논문입니다. 가장 유명한 것 중 일부는 다음과 같습니다.

  • [GGPR13] Quadratic span programs and succinct NIZKs without PCPs , Rosario Gennaro, Craig Gentry, Bryan Parno, Mariana Raykova, EUROCRYPT 2013

  • [PGHR13] Pinocchio: Nearly Practical Verifiable Computation , Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova, IEEE Symposium on Security and Privacy (Oakland) 2013

  • [BCGTV13] SNARKs for C: Verifying Program Executions Succinctly and in Zero Knowledge , Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, Eran Tromer, Madars Virza, CRYPTO 2013

  • [BCIOP13] Succinct non-interactive arguments via linear interactive Proofs , Nir Bitansky, Alessandro Chiesa, Yuval Ishai, Rafail Ostrovsky, Omer Paneth, Theory of Cryptography Conference 2013

  • [BCTV14a] Succinct non-interactive zero knowledge for a von Neumann architecture , Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, USENIX Security 2014

  • [BCTV14b] Scalable succinct non-interactive arguments via cycles of elliptic curves , Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, CRYPTO 2014

  • [Groth16] On the Size of Pairing-based Non-interactive Arguments , Jens Groth, EUROCRYPT 2016

libsnark의 개발자는 위의 많은 논문의 공동 저자인 Eran Tromer와 같이 이 분야의 최고 학자 또는 연구 전문가이기도 합니다.

견고한 이론적 기반과 엔지니어링 기능을 통해 libsnark 작성자는 복잡성을 단순화하고 아래 그림에 표시된 고급 이론과 복잡한 공식을 하나씩 실현하고 개발자의 편의를 위해 높은 수준의 엔지니어링으로 간결한 인터페이스를 추상화할 수 있습니다. 뛰어난 이론적 연구를 대규모 응용 프로그램으로 확장한 이 개척자들에게 찬사를 보냅니다.

다음 그림은 MIT의 libsnark 코드 기여의 첫 번째 저자인 Madars Virza에서 가져온 libsnark의 모듈 개요입니다.박사 논문

)

libsnark 프레임워크는 BCTV14a 및 Groth16이 더 많이 사용되는 여러 일반 증명 시스템의 구현을 제공합니다.

안에:

안에:

  • zk_proof_systems/ppzksnark/r1cs_ppzksnark는 BCTV14a에 해당합니다.

  • zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark는 Groth16에 해당합니다.

이 두 프로토콜의 구현 세부 사항을 연구하려면 이 두 디렉토리에서 직접 시작할 수 있습니다. ppzksnark는 zkSNARK 전처리를 의미합니다. 여기서 pp/전처리는 실제로 우리가 자주 말하는 신뢰할 수 있는 설정을 의미합니다. 즉, 증명이 생성되고 검증되기 전에 생성 알고리즘을 통해 관련 공개 매개변수(프로빙 키 및 검증 키)를 생성해야 합니다. 또한 미리 생성된 이 매개변수를 "공통 참조 문자열"(공통 참조 문자열) 또는 간단히CRS

2. 기본 원칙 및 단계

libsnark 라이브러리를 사용하여 zk-SNARKs 애플리케이션을 개발하는 것은 원칙적으로 다음 네 단계로 간략하게 요약할 수 있습니다.

  • 증명할 명제를 R1CS(Rank One Constraint System)로 표현

  • 생성 알고리즘(G)을 사용하여 이 명제에 대한 공용 매개변수 생성

  • 증명 알고리즘(P)을 사용하여 R1CS 만족 증명 생성

  • 검증 알고리즘(V)을 사용하여 증명을 검증합니다.

이 기사이 기사

비밀 x가 방정식 x^3 + x + 5 == out을 만족하는지 여부를 판단하고 만족하면 true를 반환하는 함수 C(x, out)가 있습니다.

function C(x, out) {
 return ( x^3 + x + 5 == out );
}

첫 번째 단계에서는 libsnark에서 C(x, out) 함수를 표현해야 합니다. 여기서는 생략하고 자세한 과정은 추후에 소개하도록 하겠습니다.

다음 생성기 함수(G)에 해당하는 두 번째 단계인 람다는 무작위로 생성되며, 이는 종종 신뢰할 수 있는 설정 프로세스 중에 생성된다고 합니다."toxic waste". 사람들은 그것을 적절하게 폐기해야 하기 때문에 "유독성 폐기물"이라고 부르기를 좋아합니다(예: 파괴해야 하며 아무도 그것에 대해 알 수 없음). 그렇지 않으면 증명 프로토콜의 보안에 영향을 미칩니다.

lambda <- random()

(pk, vk) = G(C, lambda)

마지막으로 증명 키(pk)와 검증 키(vk)가 생성됩니다.

세 번째 단계는 증명 기능(P)을 사용하여 증명을 생성하는 것에 해당합니다. 여기서 증명하고 싶은 것은 증명자가 비밀 값 x를 알고 있고 계산 결과 출력이 방정식을 만족시킬 수 있다는 것입니다. 따라서 x, out 및 pk를 P에 대한 입력으로 전달하고 마지막으로 증명을 생성합니다.

proof = P(pk, out, x)

네 번째 단계는 Verify 함수(V)를 사용하여 증명을 검증하고 증명을 통과하고 vk를 G로 통과한 다음 비밀을 밝히지 않고 방정식을 만족하는 비밀 값이 있음을 증명하는 것입니다.

V(vk, out, proof) ?= true

개발자의 주요 작업 부하는 libsnark의 인터페이스 규칙에 따라 손으로 명제를 설명하기 위해 C++ 회로 코드를 작성하고 코드에서 R1CS 제약 조건을 구성해야 하는 첫 번째 단계에 집중되어 있습니다. 전체 과정은 아래 그림에서도 Computation -> Arithmetic Circuit -> R1CS에 해당합니다.

3. zk-SNARKs 애플리케이션 개발 환경 구축

실습 링크를 입력하고 신속하게 libsnark를 시작하고 예제를 실행해 보겠습니다.

먼저 이 문서에 해당하는 libsnark 최소 사용 가능한 예제 코드 라이브러리 libsnark_abc를 다운로드합니다.

git clone https://github.com/sec-bit/libsnark_abc.git

git 하위 모듈을 통해 libsnark 코드를 가져옵니다.

cd libsnark_abc
git submodule update --init --recursive

참조 libsnark프로젝트 문서관련 종속성 설치를 완료합니다. Ubuntu 16.04 LTS를 예로 들면 다음 구성 요소를 설치해야 합니다.

sudo apt-get install build-essential cmake git libgmp3-dev libprocps4-dev python-markdown libboost-all-dev libssl-dev

빌드 폴더를 초기화합니다.

mkdir build && cd build && cmake ..

이 단계는 macOS 시스템에서 문제가 발생할 수 있습니다.이 문제다루다. 또는 다음 명령을 사용해 보십시오.

mkdir build && cd build && CPPFLAGS=-I/usr/local/opt/openssl/include LDFLAGS=-L/usr/local/opt/openssl/lib PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig cmake -DWITH_PROCPS=OFF -DWITH_SUPERCOP=OFF ..

성공 후에도 여전히 빌드 디렉토리에서 컴파일됩니다.

make

컴파일에 성공하면 build/src 디렉터리에서 3개의 바이너리 파일을 볼 수 있습니다.


main
range
test

이 시점에서 샘플 프로젝트의 컴파일을 완료했습니다. 샘플 코드를 실행해 보십시오.


./src/main

마지막으로 모든 것이 정상임을 나타내는 다음 로그가 나타납니다. zkSNARK 애플리케이션 개발 환경을 성공적으로 소유했으며 첫 번째 zk-SNARKs 데모를 성공적으로 실행했습니다.

4. 샘플 코드 이해

함께 코드를 자세히 살펴보겠습니다. 샘플 프로젝트에는 3개의 코드가 포함되어 있습니다(기사 끝에 있는 부록 참조).

먼저 src/main.cpp를 살펴보겠습니다. 이 예는 Howard Wu의libsnark_tutorial, 그는 또한 libsnark의 저자 중 한 명입니다. 이 기사에서 libsnark_abc의 프로젝트 구조는 "공식 권장 스타일"에 속하는 그의 libsnark_tutorial에 따라 구축되었습니다. 안심하고 드세요😆.

수십 줄의 코드만 있으며 그 중 run_r1cs_gg_ppzksnark()가 주요 부분입니다. 실제 작동하는 실질적인 코드는 다음 5줄에 불과하다는 것을 쉽게 알 수 있습니다.


r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(example.constraint_system);

r1cs_gg_ppzksnark_processed_verification_key pvk = r1cs_gg_ppzksnark_verifier_process_vk(keypair.vk);

r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input);

const bool ans = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, proof);

const bool ans2 = r1cs_gg_ppzksnark_online_verifier_strong_IC(pvk, example.primary_input, proof);

"매우 긴" 함수 이름에서 각 단계가 수행하는 작업을 볼 수 있지만 회로를 구성하는 방법에 대한 세부 정보는 볼 수 없습니다. 사실 여기에서는 내장 r1cs_example을 호출하고 구현 세부 정보를 숨깁니다.

그렇다면 좀 더 직관적인 예제를 통해 회로 세부 사항을 알아보도록 하겠습니다. src/test.cpp를 연구하십시오. 이 예제는 Christian Lundkvist의libsnark-tutorial

코드 시작 부분에는 다음 세 개의 헤더 파일만 참조됩니다.


#include
#include
#include

앞서 언급했듯이 r1cs_gg_ppzksnark는 Groth16 체계에 해당합니다. 여기에 r1cs_ppzksnark(즉, BCTV14a scheme)를 구별하기 위해 gg를 추가했는데, 이는 Generic Group Model(일반 그룹 모델)을 의미합니다. Groth16의 보안 증명은 더 나은 성능과 더 짧은 증명을 위해 더 강력한 보안 가정을 ​​교환하는 일반 그룹 모델에 의존합니다.

첫 번째 헤더 파일은 default_r1cs_gg_ppzksnark_pp 유형을 소개하는 것이고, 두 번째는 증명과 관련된 다양한 인터페이스를 소개하는 것입니다. pb_variable은 회로 관련 변수를 정의하는 데 사용됩니다.

다음으로, 사용된 유한 필드를 정의하고 곡선 매개변수를 초기화하기 위해 일부 초기화가 필요합니다. 이것은 모든 준비와 동일합니다.


typedef libff::Fr FieldT;
default_r1cs_gg_ppzksnark_pp::init_public_params();

다음으로 "증명해야 할 명제"가 무엇인지 명확히 할 필요가 있습니다. 여기서 우리는 비밀 x가 방정식 x^3 + x + 5 == out을 만족한다는 것을 증명하기 위해 이전 예를 사용할 수도 있습니다. 이것은 실제로 Vitalik 블로그 게시물입니다."Quadratic Arithmetic Programs: from Zero to Hero"에서 사용된 예. 아래 변경 사항을 처음 사용하는 경우 이 블로그 게시물을 읽어보세요.

중간 변수 sym_1, y 및 sym_2를 도입하여 x^3 + x + 5 = out을 여러 이차 방정식으로 평면화하고 단순 곱셈 또는 덧셈만 포함하는 여러 식은 산술 회로의 곱셈 게이트 및 덧셈에 해당합니다. . 해당 회로를 종이에 쉽게 그릴 수 있습니다.


x * x = sym_1
sym_1 * x = y
y + x = sym_2
sym_2 + 5 = out

일반적으로 여기의 기사에서는 위의 방정식을 R1CS 형식으로 배열하는 방법을 소개하고 특정 해당 벡터를 단계별로 추론합니다. 이는 Gate를 R1CS로 변환하는 방법을 이해하는 데 도움이 되지만 이 기사의 핵심 목적은 아닙니다. 그래서 여기서 백 단어가 생략되었습니다.

명제와 관련된 변수는 아래에 정의되어 있습니다. 먼저 생성된 protoboard는 libsnark에서 중요한 개념으로, 이름에서 알 수 있듯이 회로를 빠르게 구축하기 위해 사용되는 프로토타입 보드 또는 브레드보드입니다.zk-SNARKs 회로에서는 모든 변수, 구성 요소 및 제약. 다음 코드는 외부 입력 및 중간 변수가 필요한 모든 변수를 정의합니다.


// Create protoboard
protoboard pb;

// Define variables
pb_variable x;
pb_variable sym_1;
pb_variable y;
pb_variable sym_2;
pb_variable out;

다음으로 각 변수를 프로토보드와 연결합니다. 이는 각 구성 요소를 "브레드보드"에 삽입하는 것과 같습니다. allocate() 함수의 두 번째 문자열형 변수는 DEBUG 시 주석에만 사용되며, DEBUG 시 로그 보기에 편리합니다.


out.allocate(pb, "out");
x.allocate(pb, "x");
sym_1.allocate(pb, "sym_1");
y.allocate(pb, "y");
sym_2.allocate(pb, "sym_2");
pb.set_input_sizes(1);

여기서 pb에 대한 첫 번째 연결은 out 변수입니다. 우리는 각각 libsnark의 기본 및 보조 변수에 해당하는 zk-SNARK에 공개 입력 및 비공개 증인 개념이 있음을 알고 있습니다. 그렇다면 코드에서 구분하는 방법은 무엇입니까? protoboard에 연결된 public/primary 변수의 n개를 선언하려면 set_input_sizes(n)를 사용해야 합니다. 여기서 n = 1은 pb와 연결된 첫 번째 n = 1 변수가 공개되고 나머지는 비공개임을 나타냅니다.

지금까지 모든 변수가 프로토보드에 성공적으로 연결되었으며 다음으로 결정해야 할 것은 이러한 변수 간의 구속 관계입니다. 이것은 또한 이해하기 쉽습니다.유사한 구성 요소를 브레드 보드에 삽입한 후 회로 요구 사항에 따라 이들 간의 관계를 결정한 다음 연결 및 납땜해야 합니다. 다음과 같이 프로토보드의 add_r1cs_constraint() 함수를 호출하여 a * b = c 형식의 r1cs_constraint를 pb에 추가합니다. 즉 r1cs_constraint(a, b,c)의 매개변수는 a * b = c를 충족해야 합니다. 주석에 따라 각 방정식과 제약 조건의 관계를 이해하는 것은 어렵지 않습니다.

// x*x = sym_1
pb.add_r1cs_constraint(r1cs_constraint(x, x, sym_1));
// sym_1 * x = y
pb.add_r1cs_constraint(r1cs_constraint(sym_1, x, y));
// y + x = sym_2
pb.add_r1cs_constraint(r1cs_constraint(y + x, 1, sym_2));
// sym_2 + 5 = ~out
pb.add_r1cs_constraint(r1cs_constraint(sym_2 + 5, 1, out));

지금까지 변수간 제약도 추가했고, 명제를 위한 회로도 구성했다. 위에서 언급한 "4단계" 중 두 번째 단계로 들어가 보겠습니다. 생성 알고리즘(G)을 사용하여 명제, 즉 신뢰 설정에 대한 공개 매개변수(pk 및 vk)를 생성합니다. 생성된 증명 키와 검증 키는 각각 keypair.pk와 keypair.vk를 통해 얻을 수 있습니다.

const r1cs_constraint_system constraint_system = pb.get_constraint_system();
const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system);

세 번째 단계로 이동하여 인증서를 생성합니다. 먼저 공개 입력 및 증인에 대한 특정 값을 제공합니다. x = 3, out = 35가 원래 방정식의 해임을 보는 것은 어렵지 않습니다. 그런 다음 x, out 및 각 중간 변수에 값을 차례로 할당합니다.


pb.val(out) = 35;

pb.val(x) = 3;
pb.val(sym_1) = 9;
pb.val(y) = 27;
pb.val(sym_2) = 30;

그런 다음 각각 pb.primary_input() 및 pb.auxiliary_input()을 통해 액세스할 수 있는 증명을 위한 증명 함수에 공개 입력 및 증인의 값을 전달합니다. 생성된 증명은 proof 변수와 함께 저장됩니다.


const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input());

마지막으로 verifier 기능을 사용하여 증명을 확인합니다. verify = true인 경우 증명 확인이 성공한 것입니다.


bool verified = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof);

로그 출력에서 ​​검증 결과는 true이고, R1CS 제약 조건의 개수는 4개이며, public 입력과 private 입력의 개수는 각각 1개와 4개임을 알 수 있습니다. 로그 출력은 예상대로입니다.

실제 응용 프로그램에서 신뢰할 수 있는 설정, 증명 및 확인은 다른 역할에 의해 수행됩니다.최종 효과는 증명자가 검증자에게 간단한 증명과 공개 입력을 제공하고 검증자는 명제가 참인지 여부를 자체적으로 확인할 수 있다는 것입니다. 이전 예의 경우 방정식의 특정 솔루션 x를 알지 못해도 x^3 + x + 5 = out이 설정될 수 있도록 증명자가 비밀 x를 알고 있는지 확인할 수 있습니다.

수십 줄의 코드만으로 학계에서 zk-SNARK의 최신 연구 결과를 쉽게 조작할 수 있습니다.

5. 다시 실습하기

위의 예제를 통해 우리는 libsnark 라이브러리를 사용하여 zk-SNARKs 회로를 개발하는 모든 중요한 단계를 보았습니다.

이제 새로운 예제로 통합해 보겠습니다. 비밀 번호의 크기를 밝히지 않고 숫자가 60 미만임을 증명하십시오.

일반 프로그램에서 하나의 연산자로 할 수 있는 이 일을 libsnark에서 어떻게 표현해야 할까요?

zk-SNARKs 회로 개발의 주요 작업 부하 및 어려움은 제안의 모든 제약을 코드로 "정확하게" 설명하는 방법입니다. 설명이 "정확"하지 않으면 제약 조건이 누락되거나 잘못 작성되고 최종 회로가 증명하려는 내용이 원래 명제에서 멀어집니다. 이전 섹션의 예제는 r1cs_constraint의 가장 기본적인 형식과 일치하는 단순한 곱셈과 덧셈만 포함하므로 제약 조건의 표현이 비교적 쉽습니다. 또한 거의 모든 제약 조건이 직관적이지 않아 초보자가 제약 조건의 세부 사항을 정확하게 설명하기 어렵습니다.

운 좋게도 libsnark는 이미 우리를 위해 많은 기본 회로 위젯을 구현합니다. 사용할 준비가 된 gadgetlib1 및 gadgetlib2 아래에 여러 가젯이 제공됩니다. 그 중 가젯립1이 더 일반적으로 사용되며 sha256, 머클 트리, 페어링 및 기타 회로 구현을 포함한 해시 계산을 수집합니다.

DangDangDang, gadgetlib1/gadgets/basic_gadgets.hpp에 있는 comparison_gadget이 바로 우리에게 필요한 것입니다.


comparison_gadget(protoboard& pb,
                   const size_t n,
                   const pb_linear_combination &A,
                   const pb_linear_combination &B,
                   const pb_variable &less,
                   const pb_variable &less_or_eq,
                   const std::string &annotation_prefix="")

가젯은 많은 매개변수를 전달해야 합니다. n은 자릿수를 나타내고 A와 B는 비교할 두 숫자이며 less 및 less_or_eq는 두 숫자 간의 관계가 "보다 작음" 또는 "작음"인지 표시하는 데 사용됩니다. 보다 크거나 같음". 가젯 구현의 원칙은 단순히 A와 B 간의 비교를 2^n + B - A 비트 표현으로 변환하는 것입니다. 특정 구현은 또한 compare_gadget을 통해 전달될 수 있는 여러 가지 다른 기본 가젯을 사용합니다.::generate_r1cs_constraints() 연구.

여기에서 다음과 같은 변수를 생성하고 x와 max를 pb에 연결하고 max 값을 값의 상한을 나타내는 60으로 설정해야 합니다.


protoboard pb;

pb_variable x, max;
pb_variable less, less_or_eq;

x.allocate(pb, "x");
max.allocate(pb, "max");

pb.val(max)= 60;

Comparison_gadget을 사용하여 cmp를 만들고 이전 매개변수를 채우고 가젯과 함께 제공되는 generate_r1cs_constraints() 메서드를 호출합니다. 동시에 다른 제한을 추가하여 less * 1 = 1을 요구합니다. 즉, less가 참이어야 합니다.


comparison_gadget cmp(pb, 10, x, max, less, less_or_eq, "cmp");
cmp.generate_r1cs_constraints();
pb.add_r1cs_constraint(r1cs_constraint(less, 1, FieldT::one()));

증인(비밀 값 x)을 입력하고 x = 18이라고 합니다. 여기서도 compare_gadget의 generate_r1cs_witness 메서드를 호출해야 합니다.


// Add witness values
pb.val(x) = 18; // secret
cmp.generate_r1cs_witness();

나머지는 앞의 예와 일치한다. 즉, 비밀 번호의 크기를 공개하지 않고 숫자가 60 미만임을 증명할 수 있다. 같은 방식으로 값의 최대 값과 최소 값을 제한하는 "범위 증명"이 구현됩니다.

강력한 기본 라이브러리의 도움으로 더 짧은 코드로 증명 요구 사항을 달성했습니다.

6. What's NEXT?

이 글을 읽은 후에는 모든 사람이 libsnark 및 zk-SNARKs 회로 개발을 사용하는 방법에 대해 예비적으로 이해했다고 생각합니다.

libsnark가 상대적으로 사용하기 쉽고 실제로 초점은 zk-SNARKs 회로 개발에 있음을 발견했을 수 있습니다. 앞서 언급한 바와 같이 코드는 증명할 명제의 모든 제약을 "정확하게" 기술하기 위해 사용되어야 합니다. 제약을 "누락"하거나 "잘못 작성"하는 제약은 증명의 내용을 원래 의도와 매우 다르게 만들어 증명을 무의미하게 만듭니다. .

실제 비즈니스 로직을 zk-SNARK 회로 코드로 정확하고 효율적으로 변환하는 방법은 개발자가 지속적으로 연구하고 연습해야 하는 것입니다.

다행스럽게도 우리는 이미 libsnark 테스트 그라운드를 가지고 있으며 쉽게 코드를 수정하고 추가하여 시도할 수 있습니다.

회로 구현이 아무리 복잡하더라도 더 간단한 "회로 구성 요소"를 하나씩 결합하고 패키징하여 구성됩니다. 따라서 libsnark와 함께 제공되는 기본 라이브러리는 사용 방법을 배우는 것뿐만 아니라 구현 원리를 연구하는 데에도 매우 중요한 학습 자료입니다.

또한 HarryR's와 같은 다른 프로젝트의 회로 구현을 읽으면 ZKP를 실제 비즈니스에 적용하는 방법을 이해할 수 있습니다.ethsnarks-miximus그리고 루프링의protocol3-circuits. 이러한 프로젝트를 통해 대규모 회로를 설계하고 개발하는 방법과 회로 성능과 관련된 다양한 설계 최적화 세부 정보를 배우고 동시에 회로 제약 조건의 규모를 더 깊이 이해할 수 있습니다.

동시에 Ambi Lab의 "Zero-knowledge proof Learn by Coding: libsnark 시리즈"의 후속 기사에 계속 관심을 가져 주시기 바랍니다. 다음 번에는 zk-SNARK와 스마트 계약을 결합하려고 합니다. 회로 모듈 개발, 더 복잡한 libsnark 구현 사례, 회로 개발 프로세스 중에 밟기 쉬운 구덩이에 대해 자세히 설명합니다.

7. 부록

main.cpp

첫 번째 예제인 main.cpp는 libsnark 공식 예제의 샘플 코드를 호출합니다. 이 예제를 통해 libsnark의 기본적인 사용 과정과 주요 기능을 이해할 수 있습니다.


#include
#include
#include
#include

using namespace libsnark;

/**
* The code below provides an example of all stages of running a R1CS GG-ppzkSNARK.
*
* Of course, in a real-life scenario, we would have three distinct entities,
* mangled into one in the demonstration below. The three entities are as follows.
* (1) The "generator", which runs the ppzkSNARK generator on input a given
*     constraint system CS to create a proving and a verification key for CS.
* (2) The "prover", which runs the ppzkSNARK prover on input the proving key,
*     a primary input for CS, and an auxiliary input for CS.
* (3) The "verifier", which runs the ppzkSNARK verifier on input the verification key,
*     a primary input for CS, and a proof.
*/
template
bool run_r1cs_gg_ppzksnark(const r1cs_example > &example)
{
   libff::print_header("R1CS GG-ppzkSNARK Generator");
   r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(example.constraint_system);
   printf("\n"); libff::print_indent(); libff::print_mem("after generator");

   libff::print_header("Preprocess verification key");
   r1cs_gg_ppzksnark_processed_verification_key pvk = r1cs_gg_ppzksnark_verifier_process_vk(keypair.vk);

   libff::print_header("R1CS GG-ppzkSNARK Prover");
   r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input);
   printf("\n"); libff::print_indent(); libff::print_mem("after prover");

   libff::print_header("R1CS GG-ppzkSNARK Verifier");
   const bool ans = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, proof);
   printf("\n"); libff::print_indent(); libff::print_mem("after verifier");
   printf("* The verification result is: %s\n", (ans ? "PASS" : "FAIL"));

   libff::print_header("R1CS GG-ppzkSNARK Online Verifier");
   const bool ans2 = r1cs_gg_ppzksnark_online_verifier_strong_IC(pvk, example.primary_input, proof);
   assert(ans == ans2);

   return ans;
}

template
void test_r1cs_gg_ppzksnark(size_t num_constraints, size_t input_size)
{
   r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size);
   const bool bit = run_r1cs_gg_ppzksnark(example);
   assert(bit);
}

int main () {
   default_r1cs_gg_ppzksnark_pp::init_public_params();
   test_r1cs_gg_ppzksnark(1000, 100);

   return 0;
}

test.cpp

두 번째 예제 test.cpp. 이 예제는 libsnark를 사용하여 가장 간단한 회로를 구축하는 방법을 보여줍니다.


#include
#include
#include

using namespace libsnark;
using namespace std;

int main () {
   typedef libff::Fr FieldT;

   // Initialize the curve parameters
   default_r1cs_gg_ppzksnark_pp::init_public_params();
 
   // Create protoboard
   protoboard pb;

   // Define variables
   pb_variable x;
   pb_variable sym_1;
   pb_variable y;
   pb_variable sym_2;
   pb_variable out;

   // Allocate variables to protoboard
   // The strings (like "x") are only for debugging purposes    
   out.allocate(pb, "out");
   x.allocate(pb, "x");
   sym_1.allocate(pb, "sym_1");
   y.allocate(pb, "y");
   sym_2.allocate(pb, "sym_2");

   // This sets up the protoboard variables
   // so that the first one (out) represents the public
   // input and the rest is private input
   pb.set_input_sizes(1);

   // Add R1CS constraints to protoboard

   // x*x = sym_1
   pb.add_r1cs_constraint(r1cs_constraint(x, x, sym_1));

   // sym_1 * x = y
   pb.add_r1cs_constraint(r1cs_constraint(sym_1, x, y));

   // y + x = sym_2
   pb.add_r1cs_constraint(r1cs_constraint(y + x, 1, sym_2));

   // sym_2 + 5 = ~out
   pb.add_r1cs_constraint(r1cs_constraint(sym_2 + 5, 1, out));
   
   const r1cs_constraint_system constraint_system = pb.get_constraint_system();

   // generate keypair
   const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system);

   // Add public input and witness values
   pb.val(out) = 35;

   pb.val(x) = 3;
   pb.val(sym_1) = 9;
   pb.val(y) = 27;
   pb.val(sym_2) = 30;

   // generate proof
   const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input());

   // verify
   bool verified = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof);

   cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl;
   cout << "Primary (public) input: " << pb.primary_input() << endl;
   cout << "Auxiliary (private) input: " << pb.auxiliary_input() << endl;
   cout << "Verification status: " << verified << endl;
}

range.cpp

세 번째 예제 range.cpp. 이 예에서는 값 범위 증명을 실현하기 위해 libsnark와 함께 제공되는 comparison_gadget을 사용합니다.

#include

#include

#include

#include


using namespace libsnark;

using namespace std;


int main () {

    typedef libff::Fr FieldT;


    // Initialize the curve parameters

    default_r1cs_gg_ppzksnark_pp::init_public_params();

  

    // Create protoboard

    protoboard pb;


    pb_variable x, max;

    pb_variable less, less_or_eq;


    x.allocate(pb, "x");

    max.allocate(pb, "max");

    

    pb.val(max)= 60;


    comparison_gadget cmp(pb, 10, x, max, less, less_or_eq, "cmp");

    cmp.generate_r1cs_constraints();

    pb.add_r1cs_constraint(r1cs_constraint(less, 1, FieldT::one()));


    const r1cs_constraint_system constraint_system = pb.get_constraint_system();


    // generate keypair

    const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system);


    // Add witness values

    pb.val(x) = 18; // secret

    cmp.generate_r1cs_witness();


    // generate proof

    const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input());


    // verify

    bool verified = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof);


    cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl;

    cout << "Primary (public) input: " << pb.primary_input() << endl;

    cout << "Auxiliary (private) input: " << pb.auxiliary_input() << endl;

    cout << "Verification status: " << verified << endl;

}


开发者
安全
Odaily 공식 커뮤니티에 가입하세요