คำเตือนความเสี่ยง: ระวังความเสี่ยงจากการระดมทุนที่ผิดกฎหมายในนาม 'สกุลเงินเสมือน' 'บล็อกเชน' — จากห้าหน่วยงานรวมถึงคณะกรรมการกำกับดูแลการธนาคารและการประกันภัย
ข่าวสาร
ค้นพบ
ค้นหา
เข้าสู่ระบบ
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
ดูตลาด
Zero Knowledge Proof เรียนรู้โดยการเข้ารหัส: เริ่มต้นใช้งาน libsnark
安比(SECBIT)实验室
特邀专栏作者
2020-01-03 10:19
บทความนี้มีประมาณ 17557 คำ การอ่านทั้งหมดใช้เวลาประมาณ 26 นาที
ฉันหวังว่าผ่านบทความชุดนี้ นักพัฒนาทุกคนสามารถเริ่มต้นใช้งาน libsnark ได้ในเวลาอันสั้น และเข

ผู้เขียนบทความนี้: p0n1@安导实验

ปัจจุบัน libsnark เป็นเฟรมเวิร์กที่สำคัญที่สุดสำหรับการนำวงจร zk-SNARKs ไปใช้งาน และใช้กันอย่างแพร่หลายในธุรกรรมส่วนตัวหรือโครงการที่เกี่ยวข้องกับการประมวลผลความเป็นส่วนตัว ซึ่ง Zcash มีชื่อเสียงมากที่สุด Zcash ใช้ libsnark เพื่อใช้งานวงจรจนกระทั่งเวอร์ชัน Sapling ได้รับการอัปเกรด (ถูกแทนที่ด้วยพนักงานยกกระเป๋าในภายหลัง) ไม่ใช่เรื่องเกินจริงที่จะกล่าวว่า libsnark สนับสนุนและส่งเสริมการใช้เทคโนโลยี zk-SNARKs ขนาดใหญ่ครั้งแรก เป็นการเติมเต็มช่องว่างระหว่างทฤษฎีล่าสุดและการนำเทคโนโลยีการพิสูจน์ความรู้ที่ไม่มีความรู้ไปใช้ในเชิงวิศวกรรม

ฉันหวังว่าผ่านบทความชุดนี้ นักพัฒนาทุกคนสามารถเริ่มต้นใช้งาน libsnark ได้ในเวลาอันสั้น เข้าใจแนวคิดพื้นฐานของ libsnark ทีละขั้นตอน เรียนรู้วิธีพัฒนาวงจร zk-SNARKs สร้างและตรวจสอบการพิสูจน์ให้เสร็จสมบูรณ์ และแปลงหลักฐานความรู้เป็นศูนย์ไปใช้กับธุรกิจจริงในที่สุด

1. รู้เบื้องต้นเกี่ยวกับพื้นหลังของ zk-SNARK และ libsnark

การพิสูจน์ความรู้เป็นศูนย์อาจเป็นเทคโนโลยีการเข้ารหัสสีดำที่มีแนวโน้มและสร้างสรรค์ที่สุดในปัจจุบัน และ zk-SNARKs เป็นตัวย่อของโครงร่างการพิสูจน์ความรู้เป็นศูนย์ประเภทหนึ่ง ชื่อเต็มคือ Zero-Knowledge Succinct Non-interactive Arguments of Knowledge ชื่อนี้มีคุณลักษณะทางเทคนิคเกือบทั้งหมด นั่นคือ ความถูกต้องของประพจน์สามารถพิสูจน์ได้โดยไม่ต้องเปิดเผยข้อมูลอื่นใด และหลักฐานที่สร้างขึ้นขั้นสุดท้ายคือ รวบรัด (Succinct) ซึ่งหมายความว่าหลักฐานที่สร้างขึ้นขั้นสุดท้ายมีขนาดเล็กพอ และมี ไม่มีอะไรเกี่ยวข้องกับจำนวนของการคำนวณ มันเป็นค่าคงที่ ในภาษาอังกฤษธรรมดา คุณสามารถพิสูจน์บางสิ่งบางอย่างกับทุกคนได้ในทางทฤษฎีโดยไม่เปิดเผยความเป็นส่วนตัวใดๆ และหลักฐานที่สร้างขึ้นมีขนาดเล็ก และค่าใช้จ่ายในการตรวจสอบก็ต่ำมาก โดยไม่คำนึงถึงจำนวนเงินที่ต้องใช้ในการคำนวณเพื่อพิสูจน์เนื้อหา ฟังดูดีเกินจริง!

zk-SNARK สามารถใช้ได้กับหลายๆ สถานการณ์ เช่น การปกป้องความเป็นส่วนตัว การขยายบล็อคเชน การคำนวณที่ตรวจสอบได้ เป็นต้น บทความนี้ไม่ได้แนะนำรายละเอียดทางทฤษฎีของ zk-SNARKS และการพิสูจน์แบบ zero-knowledge นักเรียนที่ไม่คุ้นเคยหรือต้องการเรียนรู้เพิ่มเติมสามารถอ่านบทความหรือเอกสารอื่นๆ ได้

เช่นบล็อกโพสต์สามบล็อกที่มีชื่อเสียงของ Vitalik เกี่ยวกับ zk-SNARK

หรืออ่านสิ่งที่ Xiang Cheng @HUST เขียน"Zk-SNARK ของการพิสูจน์ความรู้เป็นศูนย์ด้วยเงื่อนไขง่ายๆ"และ Dongze เขียน"พูดคุยเกี่ยวกับการพิสูจน์ความรู้เป็นศูนย์ II: การพิสูจน์การไม่โต้ตอบแบบสั้น (SNARK)"

แน่นอนว่ายินดีต้อนรับสู่ Ambi Labและและชุด "การเรียนรู้ zk-SNARK ตั้งแต่เริ่มต้น"และจาก Ambi Labs ที่ได้รับการบำรุงรักษา"สรุปทรัพยากรการเรียนรู้ Zero Knowledge Proof"ค้นหาข้อมูลเพิ่มเติมใน

ตัวเอกของบทความนี้ libsnark เป็นไลบรารีโค้ด C++ สำหรับพัฒนาแอปพลิเคชัน zk-SNARKs ซึ่งพัฒนาและดูแลโดย SCIPR Lab พื้นฐานทางทฤษฎีที่อยู่เบื้องหลังการปรับใช้วิศวกรรม libsnark คือเอกสารสำคัญชุดหนึ่งในทิศทางของการพิสูจน์ความรู้เป็นศูนย์ โดยเฉพาะอย่างยิ่ง zk-SNARK ในช่วงไม่กี่ปีที่ผ่านมา (โดยเฉพาะตั้งแต่ปี 2013) บางส่วนของคนที่มีชื่อเสียงที่สุดมีดังนี้:

  • [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 ลดความซับซ้อนของความซับซ้อน ตระหนักถึงทฤษฎีขั้นสูงและสูตรที่ซับซ้อนที่แสดงในรูปด้านล่างทีละรายการ และสรุปอินเทอร์เฟซที่กระชับด้วยวิศวกรรมระดับสูงเพื่อความสะดวกของนักพัฒนา ขอชื่นชมผู้บุกเบิกเหล่านี้ที่ขยายการวิจัยเชิงทฤษฎีที่ไม่ธรรมดาไปสู่การใช้งานในระดับที่ใหญ่ขึ้น

รูปต่อไปนี้เป็นภาพรวมโมดูลของ libsnark ซึ่งนำมาจาก Madars Virza ผู้เขียนคนแรกของการสนับสนุนโค้ด libsnark ที่ MITวิทยานิพนธ์ปริญญาเอก

)

เฟรมเวิร์ก 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)

  • สร้างการพิสูจน์ความพึงพอใจของ R1CS โดยใช้อัลกอริทึมการพิสูจน์ (P)

  • ใช้อัลกอริทึมการตรวจสอบ (V) เพื่อตรวจสอบหลักฐาน

บทความนี้บทความนี้

มีฟังก์ชันเช่น C(x, out) ซึ่งใช้เพื่อตัดสินว่าความลับ x เป็นไปตามสมการ x^3 + x + 5 == ออกหรือไม่ และส่งกลับค่าจริงหากเป็นไปตามนั้น

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

ในขั้นแรก เราต้องแสดงฟังก์ชัน C(x, out) ใน libsnark มันถูกละไว้ที่นี่ และกระบวนการโดยละเอียดจะแนะนำในภายหลัง

ขั้นตอนที่สอง ซึ่งสอดคล้องกับฟังก์ชันตัวสร้างต่อไปนี้ (G) แลมบ์ดาจะถูกสร้างแบบสุ่ม ซึ่งมักกล่าวกันว่าสร้างขึ้นระหว่างกระบวนการตั้งค่าที่เชื่อถือได้"toxic waste". คนชอบเรียกมันว่า "ขยะพิษ" เพราะต้องกำจัดอย่างถูกต้อง (เช่น ต้องทำลายโดยไม่มีใครรู้เรื่องนี้) มิฉะนั้นจะส่งผลต่อความปลอดภัยของโปรโตคอลการพิสูจน์

lambda <- random()

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

ในที่สุด รหัสพิสูจน์ (pk) และรหัสยืนยัน (vk) จะถูกสร้างขึ้น

ขั้นตอนที่สามสอดคล้องกับการใช้ฟังก์ชัน Prove (P) เพื่อสร้างหลักฐาน สิ่งที่ผมอยากพิสูจน์คือผู้พิสูจน์รู้ค่าลับ x และผลการคำนวณออกมาตรงตามสมการ ดังนั้นให้ส่ง x, out และ pk เป็นอินพุตไปยัง P และสุดท้ายสร้างการพิสูจน์

proof = P(pk, out, x)

ขั้นตอนที่สี่คือการใช้ฟังก์ชัน Verify (V) เพื่อตรวจสอบการพิสูจน์ ผ่านการพิสูจน์ ออก และ vk ถึง G จากนั้นพิสูจน์ว่ามีค่าลับที่ตรงตามสมการโดยไม่เปิดเผยความลับ

V(vk, out, proof) ?= true

ภาระงานหลักของนักพัฒนามุ่งเน้นไปที่ขั้นตอนแรก ซึ่งจำเป็นต้องเขียนรหัสวงจร C++ เพื่ออธิบายข้อเสนอด้วยมือตามกฎอินเทอร์เฟซของ libsnark และสร้างข้อจำกัด R1CS จากรหัส กระบวนการทั้งหมดยังสอดคล้องกับ Computation -> Arithmetic Circuit -> R1CS ในรูปด้านล่าง

3. สร้างสภาพแวดล้อมการพัฒนาแอปพลิเคชัน zk-SNARKs

เข้าสู่ลิงก์ที่ใช้งานได้จริง เริ่มต้นใช้งาน libsnark อย่างรวดเร็ว และดูตัวอย่าง

ก่อนอื่นให้ดาวน์โหลดไลบรารีโค้ดตัวอย่างขั้นต่ำของ libsnark libsnark_abc ที่สอดคล้องกับบทความนี้

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

ดึงรหัส libsnark ผ่าน git submodule

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

หลังจากคอมไพล์สำเร็จ คุณจะเห็นไฟล์ไบนารี 3 ไฟล์ในไดเร็กทอรี build/src


main
range
test

ณ จุดนี้ คุณได้รวบรวมโครงการตัวอย่างเสร็จสมบูรณ์แล้ว ลองเรียกใช้โค้ดตัวอย่าง


./src/main

ในที่สุด บันทึกต่อไปนี้ปรากฏขึ้น บ่งชี้ว่าทุกอย่างเป็นปกติ คุณเป็นเจ้าของสภาพแวดล้อมการพัฒนาแอปพลิเคชัน zkSNARK สำเร็จและรันการสาธิต zk-SNARKs แรกสำเร็จแล้ว

4. ทำความเข้าใจโค้ดตัวอย่าง

มาดูรหัสกันดีกว่า โครงการตัวอย่างมีรหัส 3 รหัส (ดูภาคผนวกท้ายบทความด้วย)

มาดู src/main.cpp ก่อน ตัวอย่างนี้มาจาก Howard Wu'slibsnark_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 Lundkvistlibsnark-tutorial

มีการอ้างอิงไฟล์ส่วนหัวเพียงสามไฟล์เท่านั้นที่จุดเริ่มต้นของโค้ด ได้แก่:


#include
#include
#include

ตามที่กล่าวไว้ก่อนหน้านี้ r1cs_gg_ppzksnark สอดคล้องกับโครงการ Groth16 gg ถูกเพิ่มที่นี่เพื่อแยกความแตกต่างของ r1cs_ppzksnark (นั่นคือ แผนภาพ BCTV14a) ซึ่งหมายถึง Generic Group Model (โมเดลกลุ่มทั่วไป) การพิสูจน์ความปลอดภัยของ Groth16 ขึ้นอยู่กับ Generic Group Model ซึ่งแลกเปลี่ยนสมมติฐานด้านความปลอดภัยที่แข็งแกร่งกว่าเพื่อประสิทธิภาพที่ดีขึ้นและการพิสูจน์ที่สั้นลง

ไฟล์ส่วนหัวแรกจะแนะนำประเภท default_r1cs_gg_ppzksnark_pp และไฟล์ที่สองคือแนะนำอินเทอร์เฟซต่างๆ ที่เกี่ยวข้องกับการพิสูจน์ pb_variable ใช้เพื่อกำหนดตัวแปรที่เกี่ยวข้องกับวงจร

ถัดไป จำเป็นต้องมีการกำหนดค่าเริ่มต้นบางอย่างเพื่อกำหนดฟิลด์จำกัดที่ใช้และกำหนดค่าเริ่มต้นให้กับพารามิเตอร์เส้นโค้ง นี่เท่ากับการเตรียมการทุกอย่าง


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

ต่อไปจำเป็นต้องชี้แจงว่า "ข้อเสนอที่จะพิสูจน์" คืออะไร ในที่นี้ เราอาจใช้ตัวอย่างก่อนหน้านี้เพื่อพิสูจน์ว่าความลับ x เป็นไปตามสมการ x^3 + x + 5 == ออกไป นี่คือโพสต์บล็อกของ 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 แต่ไม่ใช่จุดประสงค์หลักของบทความนี้ ดังนั้นคำร้อยคำจึงถูกละไว้ที่นี่

ตัวแปรที่เกี่ยวข้องกับประพจน์กำหนดไว้ด้านล่าง โปรโตบอร์ดที่สร้างขึ้นก่อนเป็นแนวคิดที่สำคัญใน 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;

ถัดไป เชื่อมต่อตัวแปรแต่ละตัวกับบอร์ดโปรโตบอร์ด ซึ่งเทียบเท่ากับการใส่ส่วนประกอบแต่ละรายการลงใน "เขียงหั่นขนม" ตัวแปรประเภทสตริงที่สองของฟังก์ชัน allocation() ใช้สำหรับความคิดเห็นระหว่าง 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 ที่นี่คือตัวแปรออก เรารู้ว่ามีแนวคิดอินพุตสาธารณะและพยานส่วนตัวใน zk-SNARK ซึ่งสอดคล้องกับตัวแปรหลักและตัวแปรเสริมใน libsnark ตามลำดับ ดังนั้นจะสร้างความแตกต่างในรหัสได้อย่างไร เราจำเป็นต้องใช้ set_input_sizes(n) เพื่อประกาศจำนวน n ของตัวแปรสาธารณะ/ตัวแปรหลักที่เชื่อมต่อกับโปรโตบอร์ด ในที่นี้ n = 1 แสดงว่าตัวแปร n = 1 ตัวแรกที่เชื่อมต่อกับ pb เป็นแบบสาธารณะ และตัวแปรที่เหลือเป็นแบบส่วนตัว

จนถึงตอนนี้ ตัวแปรทั้งหมดเชื่อมต่อกับโปรโตบอร์ดสำเร็จแล้ว และสิ่งต่อไปที่ต้องพิจารณาคือความสัมพันธ์จำกัดระหว่างตัวแปรเหล่านี้ นอกจากนี้ยังง่ายต่อการเข้าใจหลังจากใส่ส่วนประกอบที่คล้ายกันลงในเขียงหั่นขนมแล้วความสัมพันธ์ระหว่างส่วนประกอบทั้งสองจะต้องได้รับการพิจารณาตามข้อกำหนดของวงจรจากนั้นจึงเชื่อมต่อและบัดกรี เรียกใช้ฟังก์ชัน add_r1cs_constraint() ของโปรโตบอร์ดดังต่อไปนี้เพื่อเพิ่ม r1cs_constraint ในรูปแบบ a * b = c ไปยัง 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));

จนถึงขณะนี้ ข้อจำกัดระหว่างตัวแปรได้ถูกเพิ่มเข้าไปด้วย และสร้างวงจรสำหรับประพจน์แล้ว เข้าสู่ขั้นตอนที่สองของ "สี่ขั้นตอน" ที่กล่าวถึงข้างต้น: ใช้อัลกอริทึมการสร้าง (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() ตามลำดับ การพิสูจน์ที่สร้างขึ้นจะถูกบันทึกด้วยตัวแปรการพิสูจน์


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

สุดท้าย เราจะตรวจสอบหลักฐานโดยใช้ฟังก์ชันตัวตรวจสอบ หากตรวจสอบแล้ว = จริง แสดงว่าการตรวจสอบหลักฐานสำเร็จ


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

จากเอาต์พุตบันทึก จะเห็นได้ว่าผลการตรวจสอบเป็นจริง จำนวนข้อจำกัด R1CS คือ 4 และจำนวนอินพุตสาธารณะและอินพุตส่วนตัวคือ 1 และ 4 ตามลำดับ เอาต์พุตบันทึกเป็นไปตามที่คาดไว้

ในการใช้งานจริง การตั้งค่า พิสูจน์ และยืนยันที่เชื่อถือได้จะดำเนินการโดยบทบาทที่แตกต่างกัน ผลสุดท้ายคือ ผู้พิสูจน์จะให้หลักฐานสั้น ๆ แก่ผู้ตรวจสอบและป้อนข้อมูลสาธารณะและผู้ตรวจสอบสามารถตรวจสอบได้ว่าข้อเสนอเป็นจริงหรือไม่ สำหรับตัวอย่างก่อนหน้านี้ สามารถตรวจสอบได้ว่าผู้พิสูจน์ทราบความลับของ x ดังนั้น จึงสามารถสร้าง x^3 + x + 5 = out ได้โดยไม่ต้องรู้คำตอบเฉพาะของ x ของสมการ

ด้วยโค้ดเพียงไม่กี่สิบบรรทัด คุณสามารถจัดการผลการวิจัยล่าสุดของ zk-SNARK ในแวดวงวิชาการได้อย่างง่ายดาย

5. ลงมือทำอีกครั้ง

จากตัวอย่างข้างต้น เราได้เห็นขั้นตอนสำคัญทั้งหมดในการพัฒนาวงจร zk-SNARKs โดยใช้ไลบรารี libsnark

ตอนนี้เรามารวมกับตัวอย่างใหม่: พิสูจน์ว่าตัวเลขน้อยกว่า 60 โดยไม่เปิดเผยขนาดของหมายเลขลับ

สิ่งนี้ที่สามารถทำได้ด้วยตัวดำเนินการหนึ่งตัวในโปรแกรมปกติควรแสดงภายใต้ libsnark อย่างไร

ภาระงานหลักและความยากของการพัฒนาวงจร zk-SNARKs คือวิธีการอธิบายข้อจำกัดทั้งหมดในโจทย์ด้วยโค้ดอย่าง "แม่นยำ" เมื่อคำอธิบายไม่ "ถูกต้อง" ข้อจำกัดก็จะพลาดหรือเขียนผิด และเนื้อหาที่วงจรสุดท้ายต้องการพิสูจน์จะห่างไกลจากข้อเสนอเดิม ตัวอย่างในส่วนก่อนหน้านี้เกี่ยวข้องกับการคูณและการบวกอย่างง่ายเท่านั้น ซึ่งสอดคล้องกับรูปแบบพื้นฐานที่สุดของ r1cs_constraint ดังนั้นการแสดงออกของข้อจำกัดจึงค่อนข้างง่าย นอกจากนี้ ข้อจำกัดเกือบทั้งหมดยังใช้งานไม่ได้ง่ายนัก และเป็นการยากสำหรับผู้เริ่มต้นที่จะอธิบายรายละเอียดของข้อจำกัดอย่างถูกต้อง

โชคดีที่ libsnark ใช้วิดเจ็ตวงจรพื้นฐานจำนวนมากให้เราแล้ว แกดเจ็ตจำนวนหนึ่งมีให้ภายใต้ gadgetlib1 และ gadgetlib2 ซึ่งพร้อมใช้งาน ในหมู่พวกเขา gadgetlib1 เป็นที่นิยมใช้มากกว่า และรวบรวมการคำนวณแฮช รวมถึง sha256, merkle tree, การจับคู่ และการใช้งานวงจรอื่นๆ

แดง แดง แดง การเปรียบเทียบ_gadget ใน gadgetlib1/gadgets/basic_gadgets.hpp คือสิ่งที่เราต้องการจริงๆ


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 - การแทนค่าระดับบิต การใช้งานที่เฉพาะเจาะจงยังใช้โปรแกรมเบ็ดเตล็ดพื้นฐานอื่นๆ อีกจำนวนหนึ่ง ซึ่งสามารถส่งผ่านการเปรียบเทียบ_gadget::generate_r1cs_constraints() การวิจัย

ที่นี่คุณต้องสร้างตัวแปรต่อไปนี้ เชื่อมต่อ x และ max กับ pb และตั้งค่าสูงสุดเป็น 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 กรอกพารามิเตอร์ก่อนหน้า และเรียกใช้เมธอด create_r1cs_constraints() ที่มาพร้อมกับแกดเจ็ต ในเวลาเดียวกัน ให้เพิ่มข้อจำกัดอีกข้อหนึ่ง โดยกำหนดให้น้อยกว่า * 1 = 1 นั่นคือ จะต้องเป็นจริงน้อยกว่า


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 ที่นี่จำเป็นต้องเรียกเมธอด create_r1cs_witness ของการเปรียบเทียบ_gadget


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

ที่เหลือก็ตรงกับตัวอย่างที่แล้ว คือ พิสูจน์ได้ว่ามีจำนวนน้อยกว่า 60 โดยไม่ต้องเปิดเผยขนาดของเลขลับ ในทำนองเดียวกัน มีการใช้ "การพิสูจน์ช่วง" ที่จำกัดค่าสูงสุดและต่ำสุดของค่า

ด้วยความช่วยเหลือของไลบรารี่พื้นฐานที่ทรงพลัง เราบรรลุข้อกำหนดการพิสูจน์ด้วยโค้ดที่สั้นลง

6. What's NEXT?

หลังจากอ่านข้อความนี้แล้ว ผมเชื่อว่าทุกคนมีความเข้าใจเบื้องต้นเกี่ยวกับการใช้การพัฒนาวงจร libsnark และ zk-SNARKs

คุณอาจค้นพบว่า libsnark นั้นค่อนข้างใช้งานง่าย และจุดสนใจที่แท้จริงคือการพัฒนาวงจร zk-SNARKs โค้ดจะต้องใช้เพื่ออธิบายข้อจำกัดทั้งหมดในประพจน์ที่จะพิสูจน์ได้อย่าง "แม่นยำ" ข้อจำกัด "ขาดหายไป" หรือ "เขียนผิด" จะทำให้เนื้อหาของการพิสูจน์แตกต่างจากความตั้งใจเดิมอย่างมาก .

วิธีการแปลงตรรกะทางธุรกิจจริงให้เป็นรหัสวงจร zk-SNARKs อย่างถูกต้องและมีประสิทธิภาพเป็นสิ่งที่นักพัฒนาของเราจำเป็นต้องศึกษาและฝึกฝนอย่างต่อเนื่อง

โชคดีที่เรามีพื้นฐานในการพิสูจน์ libsnark อยู่แล้ว และเราสามารถแก้ไขและเพิ่มโค้ดเพื่อลองได้อย่างง่ายดาย

ไม่ว่าการใช้งานวงจรจะซับซ้อนเพียงใด วงจรนี้เกิดจากการรวมและบรรจุ "ส่วนประกอบวงจร" ที่เรียบง่ายขึ้นทีละรายการ ดังนั้น ไลบรารีพื้นฐานที่มาพร้อมกับ libsnark จึงเป็นสื่อการเรียนรู้ที่สำคัญมาก ไม่เพียงแต่เรียนรู้วิธีใช้งานเท่านั้น แต่ยังศึกษาหลักการนำไปใช้งานด้วย

เรายังสามารถเข้าใจวิธีนำ ZKP ไปใช้กับธุรกิจจริงโดยการอ่านวงจรการใช้งานของโครงการอื่นๆ เช่น HarryR'sethsnarks-miximusและของ Loopringprotocol3-circuits. จากโครงการเหล่านี้ คุณสามารถเรียนรู้วิธีสร้างและพัฒนาวงจรขนาดใหญ่ขึ้น ตลอดจนรายละเอียดการเพิ่มประสิทธิภาพการออกแบบต่างๆ ที่เกี่ยวข้องกับประสิทธิภาพของวงจร และในขณะเดียวกันก็มีความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับขนาดของข้อจำกัดของวงจร

ในเวลาเดียวกัน ทุกคนสามารถติดตามบทความ "Zero-knowledge Proof Learn by Coding: libsnark series" โดย Ambi Lab ต่อไป ครั้งต่อไปเราจะพยายามรวม 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 ตัวอย่างนี้ใช้ comparison_gadget ที่มาพร้อมกับ libsnark เพื่อรับการพิสูจน์ช่วงค่า

#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
กลุ่มสมาชิก
https://t.me/Odaily_News
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
บัญชีทางการ
https://twitter.com/OdailyChina
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
สรุปโดย AI
กลับไปด้านบน
ฉันหวังว่าผ่านบทความชุดนี้ นักพัฒนาทุกคนสามารถเริ่มต้นใช้งาน libsnark ได้ในเวลาอันสั้น และเข
ดาวน์โหลดแอพ Odaily พลาเน็ตเดลี่
ให้คนบางกลุ่มเข้าใจ Web3.0 ก่อน
IOS
Android