ผู้เขียนบทความนี้: 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
https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649
https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627
https://medium.com/@VitalikButerin/zk-snarks-under-the-hood-b33151a013f6
หรืออ่านสิ่งที่ 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
r1cs_gg_ppzksnark_processed_verification_key
r1cs_gg_ppzksnark_proof
const bool ans = r1cs_gg_ppzksnark_verifier_strong_IC
const bool ans2 = r1cs_gg_ppzksnark_online_verifier_strong_IC
คุณสามารถดูสิ่งที่แต่ละขั้นตอนทำได้จากชื่อฟังก์ชัน "ยาวมาก" แต่คุณไม่สามารถดูรายละเอียดวิธีสร้างวงจรได้ ในความเป็นจริงนี่เป็นเพียงการเรียก 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
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
// Define variables
pb_variable
pb_variable
pb_variable
pb_variable
pb_variable
ถัดไป เชื่อมต่อตัวแปรแต่ละตัวกับบอร์ดโปรโตบอร์ด ซึ่งเทียบเท่ากับการใส่ส่วนประกอบแต่ละรายการลงใน "เขียงหั่นขนม" ตัวแปรประเภทสตริงที่สองของฟังก์ชัน 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
// x*x = sym_1
pb.add_r1cs_constraint(r1cs_constraint
// sym_1 * x = y
pb.add_r1cs_constraint(r1cs_constraint
// y + x = sym_2
pb.add_r1cs_constraint(r1cs_constraint
// sym_2 + 5 = ~out
pb.add_r1cs_constraint(r1cs_constraint
จนถึงขณะนี้ ข้อจำกัดระหว่างตัวแปรได้ถูกเพิ่มเข้าไปด้วย และสร้างวงจรสำหรับประพจน์แล้ว เข้าสู่ขั้นตอนที่สองของ "สี่ขั้นตอน" ที่กล่าวถึงข้างต้น: ใช้อัลกอริทึมการสร้าง (G) เพื่อสร้างพารามิเตอร์สาธารณะ (pk และ vk) สำหรับข้อเสนอ นั่นคือ การตั้งค่าที่เชื่อถือได้ สามารถรับรหัสพิสูจน์และรหัสยืนยันที่สร้างขึ้นได้ผ่าน keypair.pk และ keypair.vk ตามลำดับ
const r1cs_constraint_system
const r1cs_gg_ppzksnark_keypair
ไปที่ขั้นตอนที่สามเพื่อสร้างใบรับรอง ขั้นแรกให้ระบุค่าเฉพาะสำหรับการป้อนข้อมูลและการเป็นพยานของสาธารณะ ดูได้ไม่ยากว่า 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
สุดท้าย เราจะตรวจสอบหลักฐานโดยใช้ฟังก์ชันตัวตรวจสอบ หากตรวจสอบแล้ว = จริง แสดงว่าการตรวจสอบหลักฐานสำเร็จ
bool verified = r1cs_gg_ppzksnark_verifier_strong_IC
จากเอาต์พุตบันทึก จะเห็นได้ว่าผลการตรวจสอบเป็นจริง จำนวนข้อจำกัด 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
const size_t n,
const pb_linear_combination
const pb_linear_combination
const pb_variable
const pb_variable
const std::string &annotation_prefix="")
แกดเจ็ตจำเป็นต้องผ่านพารามิเตอร์หลายตัว: n ระบุจำนวนหลัก A และ B เป็นตัวเลขสองตัวที่จะเปรียบเทียบ และ less และ less_or_eq ใช้เพื่อระบุว่าความสัมพันธ์ระหว่างตัวเลขทั้งสองนั้น "น้อยกว่า" หรือ "น้อยกว่า" มากกว่าหรือเท่ากับ" หลักการของการใช้แกดเจ็ตคือการแปลงการเปรียบเทียบระหว่าง A และ B เป็น 2^n + B - การแทนค่าระดับบิต การใช้งานที่เฉพาะเจาะจงยังใช้โปรแกรมเบ็ดเตล็ดพื้นฐานอื่นๆ อีกจำนวนหนึ่ง ซึ่งสามารถส่งผ่านการเปรียบเทียบ_gadget
ที่นี่คุณต้องสร้างตัวแปรต่อไปนี้ เชื่อมต่อ x และ max กับ pb และตั้งค่าสูงสุดเป็น 60 ซึ่งแสดงถึงขีดจำกัดบนของค่า
protoboard
pb_variable
pb_variable
x.allocate(pb, "x");
max.allocate(pb, "max");
pb.val(max)= 60;
ใช้ comparison_gadget เพื่อสร้าง cmp กรอกพารามิเตอร์ก่อนหน้า และเรียกใช้เมธอด create_r1cs_constraints() ที่มาพร้อมกับแกดเจ็ต ในเวลาเดียวกัน ให้เพิ่มข้อจำกัดอีกข้อหนึ่ง โดยกำหนดให้น้อยกว่า * 1 = 1 นั่นคือ จะต้องเป็นจริงน้อยกว่า
comparison_gadget
cmp.generate_r1cs_constraints();
pb.add_r1cs_constraint(r1cs_constraint
ป้อนพยาน (ค่าลับ 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
{
libff::print_header("R1CS GG-ppzkSNARK Generator");
r1cs_gg_ppzksnark_keypair
printf("\n"); libff::print_indent(); libff::print_mem("after generator");
libff::print_header("Preprocess verification key");
r1cs_gg_ppzksnark_processed_verification_key
libff::print_header("R1CS GG-ppzkSNARK Prover");
r1cs_gg_ppzksnark_proof
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
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
assert(ans == ans2);
return ans;
}
template
void test_r1cs_gg_ppzksnark(size_t num_constraints, size_t input_size)
{
r1cs_example
const bool bit = run_r1cs_gg_ppzksnark
assert(bit);
}
int main () {
default_r1cs_gg_ppzksnark_pp::init_public_params();
test_r1cs_gg_ppzksnark
return 0;
}
test.cpp
ตัวอย่างที่สอง test.cpp ตัวอย่างนี้แสดงวิธีใช้ libsnark เพื่อสร้างวงจรที่ง่ายที่สุด
#include
#include
#include
using namespace libsnark;
using namespace std;
int main () {
typedef libff::Fr
// Initialize the curve parameters
default_r1cs_gg_ppzksnark_pp::init_public_params();
// Create protoboard
protoboard
// Define variables
pb_variable
pb_variable
pb_variable
pb_variable
pb_variable
// 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
// sym_1 * x = y
pb.add_r1cs_constraint(r1cs_constraint
// y + x = sym_2
pb.add_r1cs_constraint(r1cs_constraint
// sym_2 + 5 = ~out
pb.add_r1cs_constraint(r1cs_constraint
const r1cs_constraint_system
// generate keypair
const r1cs_gg_ppzksnark_keypair
// 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
// verify
bool verified = r1cs_gg_ppzksnark_verifier_strong_IC
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
// Initialize the curve parameters
default_r1cs_gg_ppzksnark_pp::init_public_params();
// Create protoboard
protoboard
pb_variable
pb_variable
x.allocate(pb, "x");
max.allocate(pb, "max");
pb.val(max)= 60;
comparison_gadget
cmp.generate_r1cs_constraints();
pb.add_r1cs_constraint(r1cs_constraint
const r1cs_constraint_system
// generate keypair
const r1cs_gg_ppzksnark_keypair
// Add witness values
pb.val(x) = 18; // secret
cmp.generate_r1cs_witness();
// generate proof
const r1cs_gg_ppzksnark_proof
// verify
bool verified = r1cs_gg_ppzksnark_verifier_strong_IC
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;
}
