commit dfed91779981482b8a4e8d3e8428d7c608fdf53a Author: Your Name Date: Sun Apr 20 19:16:03 2025 -0400 works diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1982e90 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,288 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "apet_ex1" +version = "0.1.0" +dependencies = [ + "curve25519-dalek", + "num-bigint", + "num-traits", + "rand", + "rand_core 0.4.2", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "crypto-common", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[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.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +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 = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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 = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[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", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6a6c1ab --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "apet_ex1" +version = "0.1.0" +edition = "2024" + +[features] +stdout=[] + +[dependencies] +curve25519-dalek = { version = "4.1.3", features = ["rand_core", "digest"] } +rand = "0.8.5" +rand_core = "0.4" +num-bigint = "0.4" +num-traits = "0.2" \ No newline at end of file diff --git a/src/circuit.rs b/src/circuit.rs new file mode 100644 index 0000000..c72a07d --- /dev/null +++ b/src/circuit.rs @@ -0,0 +1,149 @@ +use std::collections::HashMap; + +use crate::{gate::Gate, gate_type::GateType}; + + +#[derive(Debug, Clone)] +pub struct Circuit { + pub input_bits: Vec, // Input wires + pub gates: Vec, // All gates +} + +impl Circuit { + pub fn eval(self) -> bool { + let mut evaluated_gates = vec!(); + + for gate in self.gates { + let result = gate.eval(&self.input_bits, &evaluated_gates); + evaluated_gates.push(result); + } + + match evaluated_gates.pop() { + Some(result) => result, + None => panic!("Bruh moment") + } + } + + /* + This method should create a circuit that outputs 1 if the first number A (encoded in the first n + bits) is greater than the second number B (encoded in the next n bits) . + */ + pub fn compare_n_bit_numbers(input_bits: Vec, n: usize) -> Self { + if input_bits.len() < 2*n { + panic!("Expected input_bits to be of at least length {}, but it was {}", 2*n, input_bits.len()) + } + + /* + base case n=1: 1-bit: + + A B | A > B + ----------- + 0 0 | 0 + 0 1 | 0 + 1 0 | 1 + 1 1 | 0 + + n+1 case: 2-bit: + + A1 A0 B1 B0 | A > B | (A1 > B1) || ((A1 == B1) && (A0 > B0)) + ----------------------|------------------------------------ + 0 0 0 0 | 0 | 0 + 0 0 0 1 | 0 | 0 + 0 0 1 0 | 0 | 0 + 0 0 1 1 | 0 | 0 + 0 1 0 0 | 1 | 1 + 0 1 0 1 | 0 | 0 + 0 1 1 0 | 0 | 0 + 0 1 1 1 | 1 | 1 + 1 0 0 0 | 1 | 1 + 1 0 0 1 | 1 | 1 + 1 0 1 0 | 0 | 0 + 1 0 1 1 | 0 | 0 + 1 1 0 0 | 1 | 1 + 1 1 0 1 | 1 | 1 + 1 1 1 0 | 1 | 1 + 1 1 1 1 | 0 | 0 + + The inductive pattern should become apparent now. + For illustration here's the case for n=4, which should show the + recursive characteristics of the formula. + + (A3 > B3) || + ((A3 == B3) && (A2 > B2)) || + ((A3 == B3) && (A2 == B2) && (A1 > B1)) || + ((A3 == B3) && (A2 == B2) && (A1 == B1)) && (A0 > B0)) + + */ + + let gates = create_n_bit_comparator_gates(n); + return Circuit { input_bits, gates } + } +} + + +fn create_n_bit_comparator_gates(n: usize) -> Vec{ + let mut indices = HashMap::::new(); + let mut all_gates: Vec = vec!(); + let mut and_gate_indices: Vec = vec!(); + + rec_n_bit_comperator_gates(0, n, &mut all_gates, &mut and_gate_indices, &mut indices, ); + + // the OR spanning all ANDs + let or_gate = Gate::new( GateType::Or, and_gate_indices, vec!()); + + all_gates.push(or_gate); + + return all_gates; +} + +fn rec_n_bit_comperator_gates(curr: usize, max: usize, all_gates: &mut Vec, and_gate_incides: &mut Vec, indices: &mut HashMap){ + // Incrementing gate index + let a_curr_gt_b_curr_gate_index = all_gates.len(); + + // Gate(A_current > B_current) + let a_curr_gt_b_curr_gate = Gate::new(GateType::Bigger, vec!(), vec!(curr, curr+max)); + + // A_current>B_current + //print!(" ( ({}) ", format!("{} > {}", format!("A{}", max-1-curr), format!("B{}", max-1-curr))); + + all_gates.push(a_curr_gt_b_curr_gate); + + let mut this_recursion_gate_indices: Vec = vec!(a_curr_gt_b_curr_gate_index); + + for i in 0..curr { + // A_i + let a_i = format!("A{}", max-1-i); + //B_i + let b_i = format!("B{}", max-1-i); + + // A_i=B_i + let a_i_eq_b_i = format!("{} = {}", a_i, b_i); + // The curr-1'th equality gate is the only one that doesn't exist yet + if i == curr-1 { + // Gate(A_current > B_current) + let a_i_eq_b_i_gate = Gate::new(GateType::Equal, vec!(), vec!(i, i+max)); + let a_curr_gt_b_curr_gate_index = all_gates.len(); + indices.insert(a_i_eq_b_i.clone(), a_curr_gt_b_curr_gate_index); + all_gates.push(a_i_eq_b_i_gate); + } + let eq_gate_index = indices.get(&a_i_eq_b_i).unwrap(); + + //print!("&& ({}) ", a_i_eq_b_i); + + // Index of this equality gate + this_recursion_gate_indices.push(*eq_gate_index); + } + + let and_curr_index = all_gates.len(); + let and_curr_gate = Gate::new(GateType::And, this_recursion_gate_indices, vec!()); + + all_gates.push(and_curr_gate); + and_gate_incides.push(and_curr_index); + + if curr+1 == max { + //println!(")"); + return; + } + //println!(") ||"); + return rec_n_bit_comperator_gates(curr+1, max, all_gates, and_gate_incides, indices); +} \ No newline at end of file diff --git a/src/gate.rs b/src/gate.rs new file mode 100644 index 0000000..654da0b --- /dev/null +++ b/src/gate.rs @@ -0,0 +1,122 @@ +use crate::gate_type::GateType; + +#[derive(Debug, Clone)] +pub struct Gate { + pub gate_type: GateType, + pub input_gates_indices: Vec, // Which previous gates to use + pub input_bits_indices: Vec, // Which input bits to use +} + +impl Gate { + pub fn new( + gate_type: GateType, + input_gates_indices: Vec, + input_bits_indices: Vec, + ) -> Self { + let num_input_wires = input_bits_indices.len() + input_gates_indices.len(); + + if num_input_wires == 0 { + panic!("A gate without inputs is invalid"); + } + + if gate_type == GateType::Bigger && num_input_wires != 2 { + if num_input_wires != 2 { + panic!("Bigger gates need exactly two inputs") + } + } + + if gate_type == GateType::Equal && num_input_wires != 2 { + if num_input_wires != 2 { + panic!("Equal gates need exactly two inputs") + } + } + Self { + gate_type, + input_gates_indices, + input_bits_indices, + } + } + + pub fn eval(&self, input_bits: &Vec, evaluated_gates: &Vec) -> bool { + /* + The goal is to perform short-circuit evaluation with the minimum number of vector lookups. + */ + + match self.gate_type { + /* + A multi-input gate that outputs 1 if all input bits are 1, and 0 otherwise + */ + GateType::And => { + + for &index in self.input_bits_indices.iter(){ + if !input_bits[index] { + return false; + } + } + for &index in self.input_gates_indices.iter(){ + if !evaluated_gates[index] { + return false; + } + } + + return true; + } + + /* + A multi-input gate that outputs 1 if at least one input bit is 1, and 0 otherwise. + */ + GateType::Or => { + for &index in self.input_bits_indices.iter(){ + if input_bits[index] { + return true; + } + } + for &index in self.input_gates_indices.iter(){ + if evaluated_gates[index] { + return true; + } + } + return false; + }, + + /* + A gate with exactly two inputs. It outputs 1 if the two input bits are equal + (i.e., both 0 or both 1), and 0 otherwise + */ + GateType::Equal => match self.input_bits_indices.len() { + 0 => evaluated_gates[self.input_gates_indices[0]] == evaluated_gates[self.input_gates_indices[1]], + 1 => input_bits[self.input_bits_indices[0]] == evaluated_gates[self.input_gates_indices[0]], + 2 => { + input_bits[self.input_bits_indices[0]] == input_bits[self.input_bits_indices[1]] + }, + _ => panic!{"How'd we get here?"} + }, + + /* + A gate with exactly two inputs. It outputs 1 if the first input is 1 and the second + input is 0; otherwise, it outputs 0. This gate is used to compare individual bits a_i > b_i + + A B | A > B | A && !B + -------------------- + 0 0 | 0 | 0 0 1 + 0 1 | 0 | 0 0 0 + 1 0 | 1 | 1 1 1 + 1 1 | 0 | 1 0 0 + */ + GateType::Bigger => match self.input_bits_indices.len() { + // bits: [] gates: [A,B] + 0 => evaluated_gates[self.input_gates_indices[0]] && !evaluated_gates[self.input_gates_indices[1]], + + // bits: [A] gates: [B] + 1 => input_bits[self.input_bits_indices[0]] && !evaluated_gates[self.input_gates_indices[0]], + + // bits: [A,B] gates: [] + 2 => { + input_bits[self.input_bits_indices[0]] && !input_bits[self.input_bits_indices[1]] + }, + + _ => panic!{"How'd we get here? {}", input_bits.len()} + }, + } + } +} diff --git a/src/gate_type.rs b/src/gate_type.rs new file mode 100644 index 0000000..8a9ece3 --- /dev/null +++ b/src/gate_type.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Clone, PartialEq)] +pub enum GateType { + And, // multi-input + Or, // multi-input + Equal, // 2-input + Bigger, // 2-input (A > B) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a16f47d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod circuit; +pub mod gate; +pub mod gate_type; +pub mod scalar_to_bits; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b273066 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,26 @@ +use apet_ex1::{circuit::Circuit, scalar_to_bits::scalar_to_bits}; +use curve25519_dalek::Scalar; +use num_bigint::BigUint; +use rand::rngs::OsRng; + +fn main() { + let a = Scalar::random(&mut OsRng); + let b = Scalar::random(&mut OsRng); + // Convert to bit representation + let a_bits = scalar_to_bits(&a); + let b_bits = scalar_to_bits(&b); + + // Combine A and B bits into one input vector + let mut input_bits = Vec::with_capacity(512); + input_bits.extend_from_slice(&a_bits); + input_bits.extend_from_slice(&b_bits); + + // Build and evaluate the comparison circuit + let circuit = Circuit::compare_n_bit_numbers(input_bits, 256); + let circuit_result = circuit.eval(); + + let a_int = BigUint::from_bytes_le(&a.to_bytes()); + let b_int = BigUint::from_bytes_le(&b.to_bytes()); + + println!("{}", circuit_result == (a_int > b_int)) +} diff --git a/src/scalar_to_bits.rs b/src/scalar_to_bits.rs new file mode 100644 index 0000000..737ed1b --- /dev/null +++ b/src/scalar_to_bits.rs @@ -0,0 +1,16 @@ +use curve25519_dalek::Scalar; + +pub fn scalar_to_bits(scalar: &Scalar) -> Vec { + let bytes = scalar.to_bytes(); + let mut bits = Vec::with_capacity(256); + + // MSB first + for &byte in bytes.iter().rev() { + for i in (0..8).rev() { + let bit = (byte >> i) & 1; + bits.push(bit != 0); + } + } + + bits +} diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..bda51ca --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,38 @@ +#[cfg(test)] +mod tests { + use apet_ex1::{circuit::Circuit, scalar_to_bits::scalar_to_bits}; + use curve25519_dalek::scalar::Scalar; + use num_bigint::BigUint; + use rand::rngs::OsRng; + #[test] + fn test_scalar_comparison_via_circuit() { + for _ in 0..100 { + let a = Scalar::random(&mut OsRng); + let b = Scalar::random(&mut OsRng); + + // Convert to bit representation + let a_bits = scalar_to_bits(&a); + let b_bits = scalar_to_bits(&b); + + // Combine A and B bits into one input vector + let mut input_bits = Vec::with_capacity(512); + input_bits.extend_from_slice(&a_bits); + input_bits.extend_from_slice(&b_bits); + + // Build and evaluate the comparison circuit + let circuit = Circuit::compare_n_bit_numbers(input_bits, 256); + let circuit_result = circuit.eval(); + + // Compare expected result using BigUint + let a_int = BigUint::from_bytes_le(&a.to_bytes()); + let b_int = BigUint::from_bytes_le(&b.to_bytes()); + let expected = a_int > b_int; + + assert_eq!( + circuit_result, expected, + "Mismatch: A = {:?}, B = {:?}, A > B = {}, but circuit says {}", + a_int, b_int, expected, circuit_result + ); + } + } +}