Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

chore!: refactor ToRadix to ToRadixLe and ToRadixBe #58

Merged
merged 20 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions acir/src/circuit/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ pub enum Directive {
bit_size: u32,
},

//decomposition of a: a=\sum b[i]*radix^i where b is an array of witnesses < radix
//decomposition of a: a=\sum b[i]*radix^i where b is an array of witnesses < radix in either little endian or big endian form
ToRadix {
a: Expression,
b: Vec<Witness>,
radix: u32,
is_little_endian: bool,
},
}

Expand Down Expand Up @@ -108,13 +109,19 @@ impl Directive {
write_u32(&mut writer, r.witness_index())?;
write_u32(&mut writer, *bit_size)?;
}
Directive::ToRadix { a, b, radix } => {
Directive::ToRadix {
a,
b,
radix,
is_little_endian,
} => {
a.write(&mut writer)?;
write_u32(&mut writer, b.len() as u32)?;
for bit in b {
write_u32(&mut writer, bit.witness_index())?;
}
write_u32(&mut writer, *radix)?;
write_u32(&mut writer, *is_little_endian as u32)?;
}
};

Expand Down Expand Up @@ -175,8 +182,18 @@ impl Directive {
}

let radix = read_u32(&mut reader)?;
let endianess = read_u32(&mut reader)?;
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
let mut is_little_endian = true;
if endianess == 0 {
is_little_endian = false;
}

Ok(Directive::ToRadix { a, b, radix })
Ok(Directive::ToRadix {
a,
b,
radix,
is_little_endian,
})
}

_ => Err(std::io::ErrorKind::InvalidData.into()),
Expand Down Expand Up @@ -228,10 +245,18 @@ fn serialisation_roundtrip() {
bit_size: 32,
};

let to_radix = Directive::ToRadix {
let to_radix_le = Directive::ToRadix {
a: Expression::default(),
b: vec![Witness(1u32), Witness(2u32), Witness(3u32), Witness(4u32)],
radix: 4,
is_little_endian: true,
};

let to_radix_be = Directive::ToRadix {
a: Expression::default(),
b: vec![Witness(1u32), Witness(2u32), Witness(3u32), Witness(4u32)],
radix: 4,
is_little_endian: false,
};

let directives = vec![
Expand All @@ -240,7 +265,8 @@ fn serialisation_roundtrip() {
quotient_predicate,
truncate,
odd_range,
to_radix,
to_radix_le,
to_radix_be,
];

for directive in directives {
Expand Down
12 changes: 9 additions & 3 deletions acir/src/circuit/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,22 @@ impl std::fmt::Display for Opcode {
)
}
Opcode::BlackBoxFuncCall(g) => write!(f, "{g}"),
Opcode::Directive(Directive::ToRadix { a, b, radix: _ }) => {
write!(f, "DIR::TORADIX ")?;
Opcode::Directive(Directive::ToRadix {
a,
b,
radix: _,
is_little_endian,
}) => {
write!(f, "DIR::TORADIXLE ")?;
write!(
f,
// TODO (Note): this assumes that the decomposed bits have contiguous witness indices
// This should be the case, however, we can also have a function which checks this
"(_{}, [_{}..._{}])",
"(_{}, [_{}..._{}], endianess: {})",
a,
b.first().unwrap().witness_index(),
b.last().unwrap().witness_index(),
if *is_little_endian { "little" } else { "big" }
)
}
}
Expand Down
63 changes: 41 additions & 22 deletions acvm/src/pwg/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,33 +64,23 @@ pub fn solve_directives(

Ok(())
}
Directive::ToRadix { a, b, radix } => {
Directive::ToRadix {
a,
b,
radix,
is_little_endian,
} => {
let val_a = get_value(a, initial_witness)?;

let a_big = BigUint::from_bytes_be(&val_a.to_be_bytes());
let a_dec = a_big.to_radix_le(*radix);
if b.len() < a_dec.len() {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
let mut a_dec = a_big.to_radix_be(*radix);
if *is_little_endian {
a_dec = a_big.to_radix_le(*radix);
}
for i in 0..b.len() {
let v = if i < a_dec.len() {
FieldElement::from_be_bytes_reduce(&[a_dec[i]])
} else {
FieldElement::zero()
};
match initial_witness.entry(b[i]) {
std::collections::btree_map::Entry::Vacant(e) => {
e.insert(v);
}
std::collections::btree_map::Entry::Occupied(e) => {
if e.get() != &v {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}
}
}
match to_radix_outcome(b, &a_dec, initial_witness) {
Ok(()) => Ok(()),
Err(e) => Err(e),
}

Ok(())
}
Directive::OddRange { a, b, r, bit_size } => {
let val_a = witness_to_value(initial_witness, *a)?;
Expand All @@ -112,3 +102,32 @@ pub fn solve_directives(
}
}
}

fn to_radix_outcome(
b: &Vec<Witness>,
a_dec: &Vec<u8>,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
) -> Result<(), OpcodeResolutionError> {
if b.len() < a_dec.len() {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}
for i in 0..b.len() {
let v = if i < a_dec.len() {
FieldElement::from_be_bytes_reduce(&[a_dec[i]])
} else {
FieldElement::zero()
};
match initial_witness.entry(b[i]) {
std::collections::btree_map::Entry::Vacant(e) => {
e.insert(v);
}
std::collections::btree_map::Entry::Occupied(e) => {
if e.get() != &v {
return Err(OpcodeResolutionError::UnsatisfiedConstrain);
}
}
}
}

Ok(())
}
1 change: 1 addition & 0 deletions stdlib/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) fn bit_decomposition(
a: gate.clone(),
b: bit_vector.clone(),
radix: 2,
is_little_endian: true,
}));

// Now apply constraints to the bits such that they are the bit decomposition
Expand Down