diff --git a/.github/workflows/leo.yml b/.github/workflows/leo.yml new file mode 100644 index 0000000000..750e79d6d8 --- /dev/null +++ b/.github/workflows/leo.yml @@ -0,0 +1,86 @@ +name: Leo Programs +on: + pull_request: + push: + branches: + - master +env: + RUST_BACKTRACE: 1 + +jobs: + new: + name: Hello Leo (from 'leo new') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Load snarkOS + run: | + mkdir ~/.ssh + echo "${{ secrets.SNARKOS_DEPLOY_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + eval $(ssh-agent -s) + ssh-add -k ~/.ssh/id_rsa + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + components: rustfmt + + - name: Install Leo + uses: actions-rs/cargo@v1 + env: + CARGO_NET_GIT_FETCH_WITH_CLI: true + with: + command: install + args: --path . + + - name: 'leo new' + run: | + cd .. + leo new hello_world + ls -la + cd hello_world && ls -la + leo run + + init: + name: Hello Leo (from 'leo init') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Load snarkOS + run: | + mkdir ~/.ssh + echo "${{ secrets.SNARKOS_DEPLOY_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + eval $(ssh-agent -s) + ssh-add -k ~/.ssh/id_rsa + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + components: rustfmt + + - name: Install Leo + uses: actions-rs/cargo@v1 + env: + CARGO_NET_GIT_FETCH_WITH_CLI: true + with: + command: install + args: --path . + + - name: 'leo init' + run: | + cd .. && mkdir hello_world && cd hello_world + leo init + ls -la + leo run diff --git a/Cargo.lock b/Cargo.lock index f4acc37b62..1808aa9982 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1284,7 +1284,7 @@ checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" [[package]] name = "snarkos-algorithms" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "blake2", "derivative", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "snarkos-curves" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "derivative", "rand", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "snarkos-derives" version = "0.1.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", @@ -1329,7 +1329,7 @@ dependencies = [ [[package]] name = "snarkos-dpc" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "blake2", "derivative", @@ -1350,7 +1350,7 @@ dependencies = [ [[package]] name = "snarkos-errors" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "base58", "bech32", @@ -1363,7 +1363,7 @@ dependencies = [ [[package]] name = "snarkos-gadgets" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "derivative", "digest 0.8.1", @@ -1378,7 +1378,7 @@ dependencies = [ [[package]] name = "snarkos-models" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "bincode", "derivative", @@ -1394,7 +1394,7 @@ dependencies = [ [[package]] name = "snarkos-objects" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "base58", "bech32", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "snarkos-parameters" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "hex", "snarkos-algorithms", @@ -1427,12 +1427,12 @@ dependencies = [ [[package]] name = "snarkos-profiler" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" [[package]] name = "snarkos-utilities" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#d80ce42f37c8dbdd8dcb0ab9574361aea3276dc3" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#c1bab103254631c892fade02662eaa66cc145d58" dependencies = [ "bincode", "rand", diff --git a/ast/src/leo.pest b/ast/src/leo.pest index f85a3cb42d..6cc5544816 100644 --- a/ast/src/leo.pest +++ b/ast/src/leo.pest @@ -348,8 +348,8 @@ input_keyword = { "input" } // Declared in functions/input/input.rs input = { - input_keyword - | function_input + function_input + | input_keyword } input_list = _{ (input ~ ("," ~ NEWLINE* ~ input)*)? } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 2f9f80f4f5..77388ae128 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -15,7 +15,7 @@ use leo_typed::{Input, LeoTypedAst, MainInput, Program}; use snarkos_errors::gadgets::SynthesisError; use snarkos_models::{ curves::{Field, PrimeField}, - gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem, TestConstraintSystem}, + gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem}, }; use sha2::{Digest, Sha256}; @@ -150,8 +150,8 @@ impl> Compiler { } /// Synthesizes the circuit for test functions with program input. - pub fn compile_test_constraints(self, cs: &mut TestConstraintSystem) -> Result<(), CompilerError> { - generate_test_constraints::(cs, self.program, self.program_input, &self.imported_programs) + pub fn compile_test_constraints(self) -> Result<(), CompilerError> { + generate_test_constraints::(self.program, self.program_input, &self.imported_programs) } /// Calls the internal generate_constraints method with arguments diff --git a/compiler/src/constraints/constraints.rs b/compiler/src/constraints/constraints.rs index fc74c6066e..ba4aaec894 100644 --- a/compiler/src/constraints/constraints.rs +++ b/compiler/src/constraints/constraints.rs @@ -42,7 +42,6 @@ pub fn generate_constraints, CS: Constrai } pub fn generate_test_constraints>( - cs: &mut TestConstraintSystem, program: Program, input: Input, imported_programs: &ImportParser, @@ -57,6 +56,7 @@ pub fn generate_test_constraints>( log::info!("Running {} tests", tests.len()); for (test_name, test_function) in tests.into_iter() { + let cs = &mut TestConstraintSystem::::new(); let full_test_name = format!("{}::{}", program_name.clone(), test_name.to_string()); let result = resolved_program.enforce_main_function( @@ -68,7 +68,7 @@ pub fn generate_test_constraints>( if result.is_ok() { log::info!( - "test {} passed. Constraint system satisfied: {}", + "test {} compiled successfully. Constraint system satisfied: {}", full_test_name, cs.is_satisfied() ); diff --git a/compiler/tests/integers/int_macro.rs b/compiler/tests/integers/int_macro.rs index e74265a6e0..a493ee949c 100644 --- a/compiler/tests/integers/int_macro.rs +++ b/compiler/tests/integers/int_macro.rs @@ -115,11 +115,18 @@ macro_rules! test_int { let a: $type_ = rand::random(); let b: $type_ = rand::random(); + // make sure that we can calculate the inverse of each number + // Leo signed integer division is non-wrapping. Thus attempting to calculate a + // division result that wraps should be ignored here. + if a.checked_neg().is_none() { + continue; + } + let bytes = include_bytes!("div.leo"); let mut program = parse_program(bytes).unwrap(); // expect an error when dividing by zero - if b == 0 || b == <$type_>::MIN { + if b == 0 { let main_input = generate_main_input(vec![ ("a", Some(InputValue::Integer($integer_type, a.to_string()))), ("b", Some(InputValue::Integer($integer_type, b.to_string()))), diff --git a/gadgets/src/signed_integer/arithmetic/div.rs b/gadgets/src/signed_integer/arithmetic/div.rs index eae60bd9d5..d834c5ac11 100644 --- a/gadgets/src/signed_integer/arithmetic/div.rs +++ b/gadgets/src/signed_integer/arithmetic/div.rs @@ -9,7 +9,6 @@ use crate::{ Int64, Int8, }; - use snarkos_models::{ curves::PrimeField, gadgets::{ @@ -24,7 +23,7 @@ use snarkos_models::{ }; macro_rules! div_int_impl { - ($($gadget:ident)*) => ($( + ($($gadget:ident),*) => ($( impl Div for $gadget { type ErrorType = SignedIntegerError; @@ -86,15 +85,52 @@ macro_rules! div_int_impl { &allocated_zero, )?; + // if the numerator is 0, return 0 let self_is_zero = Boolean::Constant(self.eq(&Self::constant(0 as <$gadget as Int>::IntegerType))); + // if other is the minimum number, the result will be zero or one + // -128 / -128 = 1 + // x / -128 = 0 fractional result rounds to 0 + let min = Self::constant(<$gadget as Int>::IntegerType::MIN); + let other_is_min = other.evaluate_equal( + &mut cs.ns(|| "other_min_check"), + &min + )?; + let self_is_min = self.evaluate_equal( + &mut cs.ns(|| "self_min_check"), + &min + )?; + let both_min = Boolean::and( + &mut cs.ns(|| "both_min"), + &other_is_min, + &self_is_min + )?; + + + // if other is the minimum, set other to -1 so the calculation will not fail + let negative_one = allocated_one.neg(&mut cs.ns(|| "allocated_one"))?; + let a_valid = min.add(&mut cs.ns(||"a_valid"), &allocated_one); + let a_set = Self::conditionally_select( + &mut cs.ns(|| "a_set"), + &self_is_min, + &a_valid?, + &self + )?; + + let b_set = Self::conditionally_select( + &mut cs.ns(|| "b_set"), + &other_is_min, + &negative_one, + &other + )?; + // If the most significant bits of both numbers are equal, the quotient will be positive - let a_msb = self.bits.last().unwrap(); let b_msb = other.bits.last().unwrap(); + let a_msb = self.bits.last().unwrap(); let positive = a_msb.evaluate_equal(cs.ns(|| "compare_msb"), &b_msb)?; // Get the absolute value of each number - let a_comp = self.neg(&mut cs.ns(|| "a_neg"))?; + let a_comp = a_set.neg(&mut cs.ns(|| "a_neg"))?; let a = Self::conditionally_select( &mut cs.ns(|| "a_abs"), &a_msb, @@ -102,12 +138,12 @@ macro_rules! div_int_impl { &self )?; - let b_comp = other.neg(&mut cs.ns(|| "b_neg"))?; + let b_comp = b_set.neg(&mut cs.ns(|| "b_neg"))?; let b = Self::conditionally_select( &mut cs.ns(|| "b_abs"), &b_msb, &b_comp, - &other, + &b_set, )?; let mut q = zero.clone(); @@ -142,13 +178,11 @@ macro_rules! div_int_impl { &b )?; - let sub = r.sub( &mut cs.ns(|| format!("subtract_divisor_{}", i)), &b ); - r = Self::conditionally_select( &mut cs.ns(|| format!("subtract_or_same_{}", i)), &can_sub, @@ -182,6 +216,22 @@ macro_rules! div_int_impl { &q_neg, )?; + // set to zero if we know result is fractional + q = Self::conditionally_select( + &mut cs.ns(|| "fraction"), + &other_is_min, + &allocated_zero, + &q, + )?; + + // set to one if we know result is division of the minimum number by itself + q = Self::conditionally_select( + &mut cs.ns(|| "one_result"), + &both_min, + &allocated_one, + &q, + )?; + Ok(Self::conditionally_select( &mut cs.ns(|| "self_or_quotient"), &self_is_zero, @@ -193,4 +243,4 @@ macro_rules! div_int_impl { )*) } -div_int_impl!(Int8 Int16 Int32 Int64 Int128); +div_int_impl!(Int8, Int16, Int32, Int64, Int128); diff --git a/gadgets/tests/signed_integer/i8.rs b/gadgets/tests/signed_integer/i8.rs index 3ba64d99d1..9801f98e15 100644 --- a/gadgets/tests/signed_integer/i8.rs +++ b/gadgets/tests/signed_integer/i8.rs @@ -282,12 +282,16 @@ fn test_int8_div_constants() { for _ in 0..1000 { let mut cs = TestConstraintSystem::::new(); - let a: i8 = rng.gen_range(-127i8, i8::MAX); - let b: i8 = rng.gen_range(-127i8, i8::MAX); + let a: i8 = rng.gen(); + let b: i8 = rng.gen(); + + if a.checked_neg().is_none() { + return; + } let expected = match a.checked_div(b) { Some(valid) => valid, - None => continue, + None => return, }; let a_bit = Int8::constant(a); @@ -308,12 +312,16 @@ fn test_int8_div() { for _ in 0..100 { let mut cs = TestConstraintSystem::::new(); - let a: i8 = rng.gen_range(-127i8, i8::MAX); - let b: i8 = rng.gen_range(-127i8, i8::MAX); + let a: i8 = rng.gen(); + let b: i8 = rng.gen(); + + if a.checked_neg().is_none() { + continue; + } let expected = match a.checked_div(b) { Some(valid) => valid, - None => continue, + None => return, }; let a_bit = Int8::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap(); diff --git a/leo/commands/init.rs b/leo/commands/init.rs index ee6d8c4b8a..edd5081119 100644 --- a/leo/commands/init.rs +++ b/leo/commands/init.rs @@ -76,10 +76,17 @@ impl CLI for InitCommand { // Verify the input file does not exist let input_file = InputFile::new(&package_name); if !input_file.exists_at(&path) { - // Create the input file in the input directory + // Create the input file in the inputs directory input_file.write_to(&path)?; } + // Verify the state file does not exist + let state_file = StateFile::new(&package_name); + if !state_file.exists_at(&path) { + // Create the state file in the inputs directory + state_file.write_to(&path)?; + } + // Verify the main file does not exist if !MainFile::exists_at(&path) { // Create the main file in the source directory diff --git a/leo/commands/new.rs b/leo/commands/new.rs index 70373c0da3..8eef13209c 100644 --- a/leo/commands/new.rs +++ b/leo/commands/new.rs @@ -90,9 +90,12 @@ impl CLI for NewCommand { // Create the input directory InputsDirectory::create(&path)?; - // Create the input file in the input directory + // Create the input file in the inputs directory InputFile::new(&package_name).write_to(&path)?; + // Create the state file in the inputs directory + StateFile::new(&package_name).write_to(&path)?; + // Create the main file in the source directory MainFile::new(&package_name).write_to(&path)?; } diff --git a/leo/commands/test.rs b/leo/commands/test.rs index f1526cf8af..458edfb241 100644 --- a/leo/commands/test.rs +++ b/leo/commands/test.rs @@ -12,7 +12,6 @@ use leo_package::{ }; use snarkos_curves::edwards_bls12::Fq; -use snarkos_models::gadgets::r1cs::TestConstraintSystem; use clap::ArgMatches; use std::{convert::TryFrom, env::current_dir}; @@ -81,9 +80,8 @@ impl CLI for TestCommand { // Generate the program on the constraint system and verify correctness { - let mut cs = TestConstraintSystem::::new(); let temporary_program = program.clone(); - let output = temporary_program.compile_test_constraints(&mut cs)?; + let output = temporary_program.compile_test_constraints()?; log::debug!("Compiled constraints - {:#?}", output); } diff --git a/package/src/inputs/input.rs b/package/src/inputs/input.rs index 524c12c5e8..ebd4d993e7 100644 --- a/package/src/inputs/input.rs +++ b/package/src/inputs/input.rs @@ -50,6 +50,9 @@ impl InputFile { [main] a: u32 = 1; b: u32 = 2; + +[registers] +r0: u32 = 0; "#, self.package_name )