Initial interactive proof implementation

This commit is contained in:
Wekuz 2025-04-03 22:50:38 +03:00
commit 2319593641
Signed by: Wekuz
GPG key ID: 2E502F2AABD32DF9
10 changed files with 932 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

213
Cargo.lock generated Normal file
View file

@ -0,0 +1,213 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "blake3"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34a796731680be7931955498a16a10b2270c7762963d5d570fdbfe02dcbf314f"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
]
[[package]]
name = "cc"
version = "1.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "dot2"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "855423f2158bcc73798b3b9a666ec4204597a72370dc91dbdb8e7f9519de8cc3"
[[package]]
name = "getrandom"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha",
"rand_core",
"zerocopy",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "three-coloring-zkp"
version = "0.1.0"
dependencies = [
"blake3",
"dot2",
"rand",
"rand_chacha",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

10
Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "three-coloring-zkp"
version = "0.1.0"
edition = "2021"
[dependencies]
blake3 = "1.7.0"
dot2 = "1.0.0"
rand = "0.9.0"
rand_chacha = "0.9.0"

4
README.md Normal file
View file

@ -0,0 +1,4 @@
# Resources
- https://youtu.be/Otvcbw6k4eo
- https://blog.cryptographyengineering.com/2014/11/27/zero-knowledge-proofs-illustrated-primer
- https://www.cs.cmu.edu/~goyal/s18/15503/scribe_notes/lecture23.pdf

View file

@ -0,0 +1,6 @@
2 1 2
0 0 2
1 0 1 3 4
0 2 5
2 2 5
1 3 4

11
example-graphs/graph1.txt Normal file
View file

@ -0,0 +1,11 @@
0 1 2
1 0 2 3
2 0 1 3 6
0 1 2 5
2 5
1 3 4
1 2 7 9
2 6 8 9
0 7
0 6 7 10
2 9

10
example-graphs/graph2.txt Normal file
View file

@ -0,0 +1,10 @@
0 1 3 5
2 0 2 8
1 1 4 7
2 0 6 7
0 2 5 6
2 0 4 9
1 3 4 8
0 2 3 9
0 1 6 9
1 5 7 8

View file

@ -0,0 +1,5 @@
0 1 2
1 0 3 4
1 0 3
0 2 1 4
2 1 3

371
out.dot Normal file
View file

@ -0,0 +1,371 @@
graph nodes {
N0[label="0"][color="blue"];
N1[label="1"][color="green"];
N2[label="2"][color="red"];
N3[label="3"][color="red"];
N4[label="4"][color="green"];
N5[label="5"][color="red"];
N6[label="6"][color="blue"];
N7[label="7"][color="blue"];
N8[label="8"][color="red"];
N9[label="9"][color="blue"];
N10[label="10"][color="green"];
N11[label="11"][color="blue"];
N12[label="12"][color="green"];
N13[label="13"][color="green"];
N14[label="14"][color="blue"];
N15[label="15"][color="red"];
N16[label="16"][color="blue"];
N17[label="17"][color="blue"];
N18[label="18"][color="green"];
N19[label="19"][color="red"];
N20[label="20"][color="red"];
N21[label="21"][color="red"];
N22[label="22"][color="green"];
N23[label="23"][color="red"];
N24[label="24"][color="green"];
N25[label="25"][color="green"];
N26[label="26"][color="blue"];
N27[label="27"][color="blue"];
N28[label="28"][color="green"];
N29[label="29"][color="red"];
N30[label="30"][color="green"];
N31[label="31"][color="green"];
N32[label="32"][color="red"];
N33[label="33"][color="green"];
N34[label="34"][color="red"];
N35[label="35"][color="green"];
N36[label="36"][color="blue"];
N37[label="37"][color="blue"];
N38[label="38"][color="blue"];
N39[label="39"][color="red"];
N40[label="40"][color="green"];
N41[label="41"][color="red"];
N42[label="42"][color="green"];
N43[label="43"][color="red"];
N44[label="44"][color="red"];
N45[label="45"][color="red"];
N46[label="46"][color="blue"];
N47[label="47"][color="red"];
N48[label="48"][color="green"];
N49[label="49"][color="red"];
N50[label="50"][color="green"];
N51[label="51"][color="red"];
N52[label="52"][color="blue"];
N53[label="53"][color="red"];
N54[label="54"][color="blue"];
N55[label="55"][color="green"];
N56[label="56"][color="blue"];
N57[label="57"][color="green"];
N58[label="58"][color="blue"];
N59[label="59"][color="red"];
N60[label="60"][color="green"];
N61[label="61"][color="green"];
N62[label="62"][color="green"];
N63[label="63"][color="red"];
N64[label="64"][color="red"];
N65[label="65"][color="blue"];
N66[label="66"][color="red"];
N67[label="67"][color="green"];
N68[label="68"][color="green"];
N69[label="69"][color="green"];
N70[label="70"][color="blue"];
N71[label="71"][color="green"];
N72[label="72"][color="blue"];
N73[label="73"][color="green"];
N74[label="74"][color="blue"];
N75[label="75"][color="red"];
N76[label="76"][color="green"];
N77[label="77"][color="blue"];
N78[label="78"][color="blue"];
N79[label="79"][color="red"];
N80[label="80"][color="blue"];
N81[label="81"][color="red"];
N82[label="82"][color="red"];
N83[label="83"][color="blue"];
N84[label="84"][color="blue"];
N85[label="85"][color="red"];
N86[label="86"][color="red"];
N87[label="87"][color="blue"];
N88[label="88"][color="green"];
N89[label="89"][color="red"];
N90[label="90"][color="green"];
N91[label="91"][color="blue"];
N92[label="92"][color="green"];
N93[label="93"][color="red"];
N94[label="94"][color="red"];
N95[label="95"][color="red"];
N96[label="96"][color="green"];
N97[label="97"][color="green"];
N98[label="98"][color="green"];
N99[label="99"][color="green"];
N0 -- N1[label=""];
N0 -- N2[label=""];
N0 -- N3[label=""];
N0 -- N4[label=""];
N0 -- N5[label=""];
N0 -- N8[label=""];
N0 -- N12[label=""];
N0 -- N15[label=""];
N0 -- N19[label=""];
N0 -- N25[label=""];
N0 -- N32[label=""];
N0 -- N35[label=""];
N0 -- N55[label=""];
N0 -- N88[label=""];
N1 -- N6[label=""];
N1 -- N15[label=""];
N1 -- N23[label=""];
N1 -- N37[label=""];
N1 -- N75[label=""];
N2 -- N17[label=""];
N2 -- N48[label=""];
N2 -- N57[label=""];
N2 -- N84[label=""];
N3 -- N30[label=""];
N3 -- N31[label=""];
N3 -- N33[label=""];
N3 -- N38[label=""];
N3 -- N58[label=""];
N3 -- N68[label=""];
N3 -- N90[label=""];
N4 -- N7[label=""];
N4 -- N43[label=""];
N4 -- N54[label=""];
N4 -- N56[label=""];
N4 -- N64[label=""];
N4 -- N66[label=""];
N5 -- N9[label=""];
N5 -- N13[label=""];
N5 -- N42[label=""];
N5 -- N77[label=""];
N5 -- N99[label=""];
N6 -- N10[label=""];
N6 -- N47[label=""];
N6 -- N49[label=""];
N6 -- N53[label=""];
N6 -- N57[label=""];
N6 -- N98[label=""];
N7 -- N22[label=""];
N7 -- N41[label=""];
N7 -- N57[label=""];
N7 -- N88[label=""];
N7 -- N89[label=""];
N8 -- N11[label=""];
N8 -- N18[label=""];
N8 -- N24[label=""];
N8 -- N38[label=""];
N8 -- N97[label=""];
N9 -- N31[label=""];
N9 -- N51[label=""];
N9 -- N60[label=""];
N9 -- N89[label=""];
N9 -- N99[label=""];
N10 -- N21[label=""];
N10 -- N23[label=""];
N10 -- N26[label=""];
N10 -- N34[label=""];
N10 -- N49[label=""];
N10 -- N54[label=""];
N10 -- N78[label=""];
N11 -- N20[label=""];
N11 -- N22[label=""];
N11 -- N32[label=""];
N11 -- N39[label=""];
N11 -- N41[label=""];
N11 -- N42[label=""];
N12 -- N15[label=""];
N12 -- N83[label=""];
N13 -- N14[label=""];
N13 -- N41[label=""];
N13 -- N47[label=""];
N13 -- N79[label=""];
N14 -- N29[label=""];
N14 -- N69[label=""];
N14 -- N75[label=""];
N14 -- N76[label=""];
N15 -- N16[label=""];
N15 -- N35[label=""];
N15 -- N40[label=""];
N15 -- N46[label=""];
N15 -- N60[label=""];
N15 -- N68[label=""];
N16 -- N47[label=""];
N16 -- N81[label=""];
N16 -- N96[label=""];
N17 -- N40[label=""];
N17 -- N41[label=""];
N17 -- N53[label=""];
N17 -- N57[label=""];
N17 -- N59[label=""];
N17 -- N63[label=""];
N17 -- N68[label=""];
N17 -- N95[label=""];
N18 -- N19[label=""];
N18 -- N47[label=""];
N18 -- N54[label=""];
N18 -- N63[label=""];
N18 -- N81[label=""];
N18 -- N87[label=""];
N19 -- N27[label=""];
N19 -- N37[label=""];
N19 -- N52[label=""];
N19 -- N76[label=""];
N19 -- N87[label=""];
N20 -- N24[label=""];
N20 -- N26[label=""];
N20 -- N48[label=""];
N20 -- N74[label=""];
N21 -- N26[label=""];
N21 -- N67[label=""];
N21 -- N96[label=""];
N22 -- N63[label=""];
N23 -- N28[label=""];
N23 -- N40[label=""];
N23 -- N55[label=""];
N23 -- N74[label=""];
N23 -- N97[label=""];
N23 -- N98[label=""];
N24 -- N36[label=""];
N24 -- N45[label=""];
N24 -- N54[label=""];
N24 -- N63[label=""];
N25 -- N38[label=""];
N25 -- N64[label=""];
N25 -- N84[label=""];
N25 -- N86[label=""];
N26 -- N71[label=""];
N27 -- N32[label=""];
N27 -- N81[label=""];
N27 -- N93[label=""];
N28 -- N51[label=""];
N28 -- N72[label=""];
N28 -- N94[label=""];
N29 -- N35[label=""];
N29 -- N84[label=""];
N30 -- N43[label=""];
N30 -- N44[label=""];
N30 -- N81[label=""];
N30 -- N86[label=""];
N31 -- N43[label=""];
N31 -- N51[label=""];
N31 -- N54[label=""];
N31 -- N80[label=""];
N32 -- N50[label=""];
N32 -- N74[label=""];
N32 -- N76[label=""];
N32 -- N88[label=""];
N32 -- N92[label=""];
N33 -- N49[label=""];
N33 -- N54[label=""];
N33 -- N65[label=""];
N34 -- N67[label=""];
N35 -- N47[label=""];
N35 -- N63[label=""];
N35 -- N64[label=""];
N35 -- N75[label=""];
N35 -- N78[label=""];
N35 -- N79[label=""];
N35 -- N89[label=""];
N35 -- N95[label=""];
N36 -- N48[label=""];
N36 -- N59[label=""];
N36 -- N95[label=""];
N37 -- N41[label=""];
N37 -- N51[label=""];
N37 -- N96[label=""];
N37 -- N98[label=""];
N39 -- N52[label=""];
N39 -- N76[label=""];
N40 -- N51[label=""];
N40 -- N75[label=""];
N41 -- N70[label=""];
N41 -- N71[label=""];
N41 -- N72[label=""];
N41 -- N78[label=""];
N41 -- N83[label=""];
N41 -- N87[label=""];
N42 -- N44[label=""];
N42 -- N85[label=""];
N43 -- N48[label=""];
N43 -- N57[label=""];
N44 -- N77[label=""];
N44 -- N98[label=""];
N45 -- N69[label=""];
N46 -- N60[label=""];
N46 -- N96[label=""];
N48 -- N51[label=""];
N48 -- N58[label=""];
N48 -- N59[label=""];
N48 -- N82[label=""];
N48 -- N87[label=""];
N48 -- N94[label=""];
N49 -- N76[label=""];
N49 -- N77[label=""];
N49 -- N83[label=""];
N49 -- N88[label=""];
N50 -- N86[label=""];
N50 -- N91[label=""];
N51 -- N78[label=""];
N52 -- N62[label=""];
N52 -- N73[label=""];
N52 -- N95[label=""];
N53 -- N84[label=""];
N53 -- N99[label=""];
N54 -- N55[label=""];
N54 -- N59[label=""];
N54 -- N63[label=""];
N54 -- N88[label=""];
N54 -- N89[label=""];
N55 -- N65[label=""];
N56 -- N75[label=""];
N57 -- N64[label=""];
N57 -- N86[label=""];
N57 -- N93[label=""];
N58 -- N61[label=""];
N58 -- N76[label=""];
N59 -- N92[label=""];
N59 -- N99[label=""];
N60 -- N80[label=""];
N60 -- N95[label=""];
N61 -- N87[label=""];
N62 -- N85[label=""];
N63 -- N69[label=""];
N64 -- N72[label=""];
N64 -- N76[label=""];
N64 -- N78[label=""];
N64 -- N83[label=""];
N65 -- N75[label=""];
N65 -- N76[label=""];
N65 -- N79[label=""];
N66 -- N78[label=""];
N67 -- N80[label=""];
N68 -- N75[label=""];
N69 -- N75[label=""];
N69 -- N87[label=""];
N70 -- N81[label=""];
N70 -- N86[label=""];
N70 -- N96[label=""];
N71 -- N82[label=""];
N71 -- N93[label=""];
N72 -- N73[label=""];
N72 -- N92[label=""];
N72 -- N96[label=""];
N74 -- N79[label=""];
N74 -- N85[label=""];
N74 -- N86[label=""];
N74 -- N89[label=""];
N76 -- N80[label=""];
N76 -- N89[label=""];
N79 -- N88[label=""];
N80 -- N92[label=""];
N81 -- N92[label=""];
N83 -- N96[label=""];
N84 -- N98[label=""];
N86 -- N92[label=""];
N87 -- N93[label=""];
N93 -- N96[label=""];
N93 -- N99[label=""];
N94 -- N97[label=""];
N95 -- N96[label=""];
}

301
src/main.rs Normal file
View file

@ -0,0 +1,301 @@
use std::fs;
use rand::{Rng, SeedableRng};
type Graph = Vec<Vec<u64>>;
fn gen_graph(num_nodes: &usize, initial_edge_chance: &f64, edge_chance_change: &f64) -> (Graph, Vec<u64>) {
let mut graph: Graph = Default::default();
let mut colors: Vec<u64> = vec![];
let mut rng = rand::rng();
let colors_selection = (0..=2).collect::<Vec<u64>>();
for i in 0..*num_nodes {
let mut c = rng.random_range(0..=2);
let mut color = colors_selection[c];
let mut neighbors: Vec<u64> = vec![];
let mut edge_chance_bonus: f64 = 0.0;
if !graph.is_empty() {
while neighbors.is_empty() {
for (j, n) in graph.iter_mut().enumerate() {
let neighbor = n;
if colors[j] != color && !neighbor.contains(&(i as u64)) && rng.random_bool(
std::cmp::min_by(
*initial_edge_chance + edge_chance_bonus,
1.0,
|a, b| a.total_cmp(b))
) {
neighbor.push(i as u64);
neighbors.push(j as u64);
edge_chance_bonus = 0.0;
}
}
if initial_edge_chance + edge_chance_bonus >= 1.0 && neighbors.is_empty() {
c = (c + 1) % 3;
color = colors_selection[c];
edge_chance_bonus = 0.0;
continue;
}
edge_chance_bonus += edge_chance_change;
}
}
graph.push(neighbors);
colors.push(color);
}
(graph, colors)
}
type Hash = [u8; 32];
fn commit(colors: &[u64], rng: &mut impl rand::Rng) -> (Hash, (Vec<Hash>, usize, Vec<Hash>)) {
let mut salts: Vec<Hash> = Vec::with_capacity(colors.len());
let mut nodes: Vec<Hash> = (0..colors.len()).map(|i| {
let mut salt: Hash = [0; 32];
rng.fill_bytes(&mut salt);
salts.push(salt);
*blake3::keyed_hash(&salt, &colors[i].to_be_bytes()).as_bytes()
}).collect();
while nodes.len().next_power_of_two() != nodes.len() {
nodes.push(*nodes.last().unwrap());
}
let num_leaves = nodes.len();
let mut level_start = 0;
let mut level_size = num_leaves;
while level_size > 1 {
for i in (0..level_size).step_by(2) {
let left = nodes[level_start + i];
assert!(i + 1 < level_size, "Number of leaves is not a power of 2"); // Uneven tree
let right = nodes[level_start + i + 1];
let mut salt: Hash = [0; 32];
rng.fill_bytes(&mut salt);
salts.push(salt);
let mut hasher = blake3::Hasher::new();
hasher.update(&left);
hasher.update(&right);
nodes.push(*hasher.finalize().as_bytes());
}
level_start += level_size;
level_size = (level_size + 1) / 2;
}
(*nodes.last().unwrap(), (nodes, num_leaves, salts))
}
type ProofMerkle = (((u64, Hash), Vec<(Hash, bool)>), ((u64, Hash), Vec<(Hash, bool)>));
fn prove(graph: &Graph, colors: &[u64], nodes: &[Hash], num_leaves: &usize, salts: &[Hash], challange: &(u64, u64)) -> ProofMerkle {
assert!(challange.0 < *num_leaves as u64 && challange.1 < *num_leaves as u64,
"Leaf requested by the challange is out of range");
assert!(graph[challange.0 as usize].contains(&challange.1) &&
graph[challange.1 as usize].contains(&challange.0),
"vertices don't share a edge ({}, {})", challange.0, challange.1);
let proof: Vec<((u64, Hash), Vec<(Hash, bool)>)> = (0..=1).map(|ci| {
let leaf_idx: usize = if ci == 0 {
challange.0 as usize
} else {
challange.1 as usize
};
let mut path = vec![];
let mut current_index = leaf_idx;
let mut level_start = 0;
let mut level_size = *num_leaves;
while level_size > 1 {
let sibling_index = if current_index % 2 == 0 {
current_index + 1
} else {
current_index - 1
};
if sibling_index < level_start + level_size {
let sibling_hash = nodes[sibling_index];
let direction = if current_index % 2 == 0 {
true // right
} else {
false // left
};
path.push((sibling_hash, direction));
}
current_index = level_start + level_size + current_index / 2 - level_start / 2;
level_start += level_size;
level_size = (level_size + 1) / 2;
}
((colors[leaf_idx], salts[leaf_idx]), path)
}).collect();
(proof.first().unwrap().to_owned(), proof.last().unwrap().to_owned())
}
fn challange(graph: &Graph, rng: &mut impl rand::Rng) -> (u64, u64) {
let v0 = rng.random_range(0..graph.len());
let v0_edges = &graph[v0];
let v1 = v0_edges[rng.random_range(0..v0_edges.len())].to_owned();
(v0 as u64, v1)
}
fn verify(commitment: &Hash, proof: &ProofMerkle) -> bool {
if proof.0.0.0 == proof.1.0.0 {
return false;
}
for v in 0..1 {
let cur_proof = if v == 0 {
&proof.0
} else {
&proof.1
};
let mut current_hash = *blake3::keyed_hash(&cur_proof.0.1, &cur_proof.0.0.to_be_bytes()).as_bytes();
for (sibling_hash, direction) in &cur_proof.1 {
let mut hasher = blake3::Hasher::new();
match direction {
false => { // left
hasher.update(sibling_hash);
hasher.update(&current_hash);
},
true => { // right
hasher.update(&current_hash);
hasher.update(sibling_hash);
}
};
current_hash = *hasher.finalize().to_owned().as_bytes();
}
if &current_hash != commitment {
return false;
}
}
true
}
fn parse_graph(data: &str) -> (Graph, Vec<u64>) {
let mut graph: Graph = Default::default();
let mut colors: Vec<u64> = vec![];
for v in data.split('\n') {
if v.is_empty() { continue; }
let (color, edges) = v.split_once(' ').unwrap();
graph.push(vec![]);
colors.push(color.parse::<u64>().unwrap());
for e in edges.split(' ') {
graph.last_mut().unwrap().push(e.parse::<u64>().unwrap());
}
}
(graph, colors)
}
type Nd = usize;
type Ed<'a> = &'a (usize, usize);
#[derive(Debug, Clone)]
struct DotGraph {
nodes: Vec<(usize, u64)>,
edges: Vec<(usize, usize)>,
}
impl<'a> dot2::Labeller<'a> for DotGraph {
type Node = Nd;
type Edge = Ed<'a>;
type Subgraph = ();
fn kind(&self) -> dot2::Kind {
dot2::Kind::Graph
}
fn graph_id(&'a self) -> dot2::Result<dot2::Id<'a>> {
dot2::Id::new("nodes")
}
fn node_id(&'a self, n: &Nd) -> dot2::Result<dot2::Id<'a>> {
dot2::Id::new(format!("N{}", n))
}
fn node_label<'b>(&'b self, n: &Nd) -> dot2::Result<dot2::label::Text<'b>> {
Ok(dot2::label::Text::LabelStr((self.nodes[*n].0.to_string()).into()))
}
fn node_color(&'a self, n: &Nd) -> Option<dot2::label::Text<'a>> {
match self.nodes[*n].1 {
0 => Some(dot2::label::Text::label("red")),
1 => Some(dot2::label::Text::label("blue")),
2 => Some(dot2::label::Text::label("green")),
_ => None
}
}
}
impl<'a> dot2::GraphWalk<'a> for DotGraph {
type Node = Nd;
type Edge = Ed<'a>;
type Subgraph = ();
fn nodes(&self) -> dot2::Nodes<'a,Nd> {
(0..self.nodes.len()).collect()
}
fn edges(&'a self) -> dot2::Edges<'a,Ed<'a>> {
self.edges.iter().collect()
}
fn source(&self, e: &Ed) -> Nd {
let & &(s,_) = e;
s
}
fn target(&self, e: &Ed) -> Nd {
let & &(_,t) = e;
t
}
}
pub fn save_graph<W: std::io::Write>(graph: &Graph, colors: &[u64], output: &mut W) -> dot2::Result {
let nodes: Vec<(usize, u64)> = graph.iter().enumerate().map(|node| {
(node.0, colors[node.0])
}).collect();
let edges: Vec<(usize, usize)> = graph.iter().enumerate().flat_map(|node| {
node.1.iter().map(|neighbor| {
(node.0, *neighbor as usize)
}).collect::<Vec<(usize, usize)>>()
}).filter(|e| e.0 < e.1).collect();
let graph = DotGraph { nodes, edges };
dot2::render(&graph, output)
}
fn main() {
let (graph, colors) = gen_graph(&100, &0.08, &0.01);
// parse_graph(&fs::read_to_string("example-graphs/graph3.txt")
// .expect("Unable to read graph data"));
println!("{:?}", graph);
let mut rng = rand_chacha::ChaCha8Rng::from_os_rng();
let rounds = graph.len().pow(2);
let mut successes: u64 = 0;
use std::time::Instant;
let now = Instant::now();
for _ in 0..rounds {
let commitment = commit(&colors, &mut rng);
let challange = challange(&graph, &mut rng);
let proof = prove(&graph, &colors, &commitment.1.0, &commitment.1.1, &commitment.1.2, &challange);
let success = verify(&commitment.0, &proof);
println!("Egress: {} Ingress: {}", size_of_val(&commitment.0) + size_of_val(&proof), size_of_val(&challange));
if success {
successes += 1;
}
else {
println!("Failed challange: {:?}", challange);
}
}
let elapsed = now.elapsed();
println!("Verified in {:.2?}", elapsed);
println!("Success rate: {:.2}% ({} / {})", successes as f64 / rounds as f64 * 100_f64, successes, rounds);
let mut f = std::fs::File::create("out.dot").unwrap();
_ = save_graph(&graph, &colors, &mut f);
}