1
0
Fork 0
mirror of synced 2025-09-23 04:08:33 +00:00

add tests, tweak out of range interpreter

This commit is contained in:
schaeff 2021-08-05 15:22:46 +02:00
parent 33c8fba1e1
commit 63be983d74
6 changed files with 151 additions and 38 deletions

1
Cargo.lock generated
View file

@ -2394,6 +2394,7 @@ dependencies = [
"zokrates_common",
"zokrates_embed",
"zokrates_field",
"zokrates_fs_resolver",
"zokrates_pest_ast",
]

View file

@ -59,6 +59,7 @@ sha2 = { version = "0.9.3", optional = true }
[dev-dependencies]
wasm-bindgen-test = "^0.3.0"
pretty_assertions = "0.6.1"
zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"}
[build-dependencies]
cc = { version = "1.0", features = ["parallel"], optional = true }

View file

@ -65,33 +65,27 @@ impl Interpreter {
}
}
},
Statement::Directive(ref d) => {
match (&d.solver, &d.inputs, self.should_try_out_of_range) {
(Solver::Bits(bitwidth), inputs, true)
if inputs[0].left.0.len() > 1
|| inputs[0].right.0.len() > 1
&& *bitwidth == T::get_required_bits() =>
{
Self::try_solve_out_of_range(&d, &mut witness)
}
_ => {
let inputs: Vec<_> = d
.inputs
.iter()
.map(|i| i.evaluate(&witness).unwrap())
.collect();
match self.execute_solver(&d.solver, &inputs) {
Ok(res) => {
for (i, o) in d.outputs.iter().enumerate() {
witness.insert(*o, res[i].clone());
}
continue;
}
Err(_) => return Err(Error::Solver),
};
}
Statement::Directive(ref d) => match (&d.solver, self.should_try_out_of_range) {
(Solver::Bits(bitwidth), true) if *bitwidth >= T::get_required_bits() => {
Self::try_solve_out_of_range(&d, &mut witness)
}
}
_ => {
let inputs: Vec<_> = d
.inputs
.iter()
.map(|i| i.evaluate(&witness).unwrap())
.collect();
match self.execute_solver(&d.solver, &inputs) {
Ok(res) => {
for (i, o) in d.outputs.iter().enumerate() {
witness.insert(*o, res[i].clone());
}
continue;
}
Err(_) => return Err(Error::Solver),
};
}
},
}
}
@ -104,7 +98,9 @@ impl Interpreter {
// we target the `2a - 2b` part of the `<` check by only returning out-of-range results
// when the input is not a single summand
let value = d.inputs[0].evaluate(&witness).unwrap();
let candidate = value.to_biguint() + T::max_value().to_biguint() + T::from(1).to_biguint();
let input = if candidate < T::from(2).to_biguint().pow(T::get_required_bits()) {
candidate
} else {
@ -123,6 +119,9 @@ impl Interpreter {
}
}
assert_eq!(num, T::zero().to_biguint());
println!("RES {:?}", res);
for (i, o) in d.outputs.iter().enumerate() {
witness.insert(*o, res[i].clone());
}

View file

@ -39,7 +39,7 @@ pub trait Analyse {
pub enum Error {
Reducer(self::reducer::Error),
Propagation(self::propagation::Error),
NonConstantShift(self::constant_argument_checker::Error),
NonConstantArgument(self::constant_argument_checker::Error),
}
impl From<reducer::Error> for Error {
@ -56,7 +56,7 @@ impl From<propagation::Error> for Error {
impl From<constant_argument_checker::Error> for Error {
fn from(e: constant_argument_checker::Error) -> Self {
Error::NonConstantShift(e)
Error::NonConstantArgument(e)
}
}
@ -65,7 +65,7 @@ impl fmt::Display for Error {
match self {
Error::Reducer(e) => write!(f, "{}", e),
Error::Propagation(e) => write!(f, "{}", e),
Error::NonConstantShift(e) => write!(f, "{}", e),
Error::NonConstantArgument(e) => write!(f, "{}", e),
}
}
}

View file

@ -10,9 +10,10 @@ use zokrates_core::{
ir::Interpreter,
};
use zokrates_field::Bn128Field;
use zokrates_fs_resolver::FileSystemResolver;
#[test]
fn out_of_range() {
fn lt_field() {
let source = r#"
def main(private field a, private field b) -> field:
field x = if a < b then 3333 else 4444 fi
@ -21,7 +22,7 @@ fn out_of_range() {
"#
.to_string();
// let's try to prove that "10000 < 5555" is true by exploiting
// let's try to prove that "10000f < 5555f" is true by exploiting
// the fact that `2*10000 - 2*5555` has two distinct bit decompositions
// we chose the one which is out of range, ie the sum check features an overflow
@ -42,3 +43,115 @@ fn out_of_range() {
)
.is_err());
}
#[test]
fn lt_uint() {
let source = r#"
def main(private u32 a, private u32 b):
field x = if a < b then 3333 else 4444 fi
assert(x == 3333)
return
"#
.to_string();
// let's try to prove that "10000u32 < 5555u32" is true by exploiting
// the fact that `2*10000 - 2*5555` has two distinct bit decompositions
// we chose the one which is out of range, ie the sum check features an overflow
let res: CompilationArtifacts<Bn128Field> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
&CompileConfig::default(),
)
.unwrap();
let interpreter = Interpreter::try_out_of_range();
assert!(interpreter
.execute(
&res.prog(),
&[Bn128Field::from(10000), Bn128Field::from(5555)]
)
.is_err());
}
#[test]
fn unpack256() {
let source = r#"
import "utils/pack/bool/unpack256"
def main(private field a):
bool[256] bits = unpack256(a)
assert(bits[255])
return
"#
.to_string();
// let's try to prove that the least significant bit of 0 is 1
// we exploit the fact that the bits of 0 are the bits of p, and p is even
// we want this to still fail
let stdlib_path = std::fs::canonicalize(
std::env::current_dir()
.unwrap()
.join("../zokrates_stdlib/stdlib"),
)
.unwrap();
let res: CompilationArtifacts<Bn128Field> = compile(
source,
"./path/to/file".into(),
Some(&FileSystemResolver::with_stdlib_root(
stdlib_path.to_str().unwrap(),
)),
&CompileConfig::default(),
)
.unwrap();
let interpreter = Interpreter::try_out_of_range();
assert!(interpreter
.execute(&res.prog(), &[Bn128Field::from(0)])
.is_err());
}
#[test]
fn unpack256_unchecked() {
let source = r#"
import "utils/pack/bool/nonStrictUnpack256"
def main(private field a):
bool[256] bits = nonStrictUnpack256(a)
assert(bits[255])
return
"#
.to_string();
// let's try to prove that the least significant bit of 0 is 1
// we exploit the fact that the bits of 0 are the bits of p, and p is even
// we want this to succeed as the non strict version does not enforce the bits to be in range
let stdlib_path = std::fs::canonicalize(
std::env::current_dir()
.unwrap()
.join("../zokrates_stdlib/stdlib"),
)
.unwrap();
let res: CompilationArtifacts<Bn128Field> = compile(
source,
"./path/to/file".into(),
Some(&FileSystemResolver::with_stdlib_root(
stdlib_path.to_str().unwrap(),
)),
&CompileConfig::default(),
)
.unwrap();
let interpreter = Interpreter::try_out_of_range();
assert!(interpreter
.execute(&res.prog(), &[Bn128Field::from(0)])
.is_ok());
}

View file

@ -26,17 +26,16 @@ impl<'a> Resolver<io::Error> for FileSystemResolver<'a> {
) -> Result<(String, PathBuf), io::Error> {
let source = Path::new(&import_location);
if !current_location.is_file() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("{} was expected to be a file", current_location.display()),
));
}
// paths starting with `./` or `../` are interpreted relative to the current file
// other paths `abc/def` are interpreted relative to the standard library root path
let base = match source.components().next() {
Some(Component::CurDir) | Some(Component::ParentDir) => {
if !current_location.is_file() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("{} was expected to be a file", current_location.display()),
));
}
current_location.parent().unwrap().into()
}
_ => PathBuf::from(self.stdlib_root_path.unwrap_or("")),