Skip to content

Commit

Permalink
Merge pull request #1877 from ljedrz/wholesale_extend_to_bits
Browse files Browse the repository at this point in the history
Optimize the implementations of ToBits across the codebase
  • Loading branch information
howardwu authored Aug 13, 2023
2 parents 4d48ef0 + 7f19dca commit a6c5cf5
Show file tree
Hide file tree
Showing 67 changed files with 1,044 additions and 1,107 deletions.
20 changes: 14 additions & 6 deletions circuit/algorithms/src/bhp/hash_uncompressed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,35 @@ impl<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> HashUncompres
// The maximum number of input bits per iteration.
let max_input_bits_per_iteration = num_hasher_bits - num_data_bits;

debug_assert!(num_data_bits < num_hasher_bits);
debug_assert_eq!(num_data_bits - 64, self.domain.len());

// Initialize a variable to store the hash from the current iteration.
let mut digest = Group::zero();

// Compute the hash of the input.
for (i, input_bits) in input.chunks(max_input_bits_per_iteration).enumerate() {
// Initialize a vector for the hash preimage.
let mut preimage = Vec::with_capacity(num_hasher_bits);
// Determine if this is the first iteration.
match i == 0 {
let preimage = match i == 0 {
// Construct the first iteration as: [ 0...0 || DOMAIN || LENGTH(INPUT) || INPUT[0..BLOCK_SIZE] ].
true => {
// Initialize a vector for the hash preimage.
let mut preimage = Vec::with_capacity(num_hasher_bits);
preimage.extend(self.domain.clone());
preimage.extend(U64::constant(console::U64::new(input.len() as u64)).to_bits_le());
U64::constant(console::U64::new(input.len() as u64)).write_bits_le(&mut preimage);
preimage.extend_from_slice(input_bits);
preimage
}
// Construct the subsequent iterations as: [ PREVIOUS_HASH[0..DATA_BITS] || INPUT[I * BLOCK_SIZE..(I + 1) * BLOCK_SIZE] ].
false => {
preimage.extend(digest.to_x_coordinate().to_bits_le().into_iter().take(num_data_bits));
// Initialize a vector for the hash preimage.
let mut preimage = Vec::with_capacity(num_hasher_bits);
digest.to_x_coordinate().write_bits_le(&mut preimage);
preimage.truncate(num_data_bits);
preimage.extend_from_slice(input_bits);
preimage
}
}
};
// Hash the preimage for this iteration.
digest = self.hasher.hash_uncompressed(&preimage);
}
Expand Down
4 changes: 2 additions & 2 deletions circuit/collections/src/merkle_tree/helpers/path_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ impl<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> PathHash<E> f
fn hash_children(&self, left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
// Prepend the nodes with a `true` bit.
let mut input = vec![Boolean::constant(true)];
input.extend(left.to_bits_le());
input.extend(right.to_bits_le());
left.write_bits_le(&mut input);
right.write_bits_le(&mut input);
// Hash the input.
Hash::hash(self, &input)
}
Expand Down
70 changes: 40 additions & 30 deletions circuit/environment/src/traits/to_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ pub trait ToBits {
type Boolean: BooleanTrait;

/// Returns the little-endian bits of the circuit.
fn to_bits_le(&self) -> Vec<Self::Boolean>;
fn to_bits_le(&self) -> Vec<Self::Boolean> {
let mut bits = vec![];
self.write_bits_le(&mut bits);
bits
}

fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>);

/// Returns the big-endian bits of the circuit.
fn to_bits_be(&self) -> Vec<Self::Boolean>;
fn to_bits_be(&self) -> Vec<Self::Boolean> {
let mut bits = vec![];
self.write_bits_be(&mut bits);
bits
}

fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>);
}

/********************/
Expand All @@ -34,16 +46,16 @@ impl<C: ToBits<Boolean = B>, B: BooleanTrait> ToBits for Vec<C> {

/// A helper method to return a concatenated list of little-endian bits from the circuits.
#[inline]
fn to_bits_le(&self) -> Vec<Self::Boolean> {
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
// The vector is order-preserving, meaning the first circuit in is the first circuit bits out.
self.as_slice().to_bits_le()
self.as_slice().write_bits_le(vec);
}

/// A helper method to return a concatenated list of big-endian bits from the circuits.
#[inline]
fn to_bits_be(&self) -> Vec<Self::Boolean> {
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
// The vector is order-preserving, meaning the first circuit in is the first circuit bits out.
self.as_slice().to_bits_be()
self.as_slice().write_bits_be(vec);
}
}

Expand All @@ -52,16 +64,16 @@ impl<C: ToBits<Boolean = B>, B: BooleanTrait, const N: usize> ToBits for [C; N]

/// A helper method to return a concatenated list of little-endian bits from the circuits.
#[inline]
fn to_bits_le(&self) -> Vec<Self::Boolean> {
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
self.as_slice().to_bits_le()
self.as_slice().write_bits_le(vec);
}

/// A helper method to return a concatenated list of big-endian bits from the circuits.
#[inline]
fn to_bits_be(&self) -> Vec<Self::Boolean> {
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
self.as_slice().to_bits_be()
self.as_slice().write_bits_be(vec);
}
}

Expand All @@ -70,16 +82,20 @@ impl<C: ToBits<Boolean = B>, B: BooleanTrait> ToBits for &[C] {

/// A helper method to return a concatenated list of little-endian bits from the circuits.
#[inline]
fn to_bits_le(&self) -> Vec<Self::Boolean> {
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
self.iter().flat_map(|c| c.to_bits_le()).collect()
for elem in self.iter() {
elem.write_bits_le(vec);
}
}

/// A helper method to return a concatenated list of big-endian bits from the circuits.
#[inline]
fn to_bits_be(&self) -> Vec<Self::Boolean> {
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
self.iter().flat_map(|c| c.to_bits_be()).collect()
for elem in self.iter() {
elem.write_bits_be(vec);
}
}
}

Expand All @@ -95,20 +111,16 @@ macro_rules! to_bits_tuple {

/// A helper method to return a concatenated list of little-endian bits from the circuits.
#[inline]
fn to_bits_le(&self) -> Vec<Self::Boolean> {
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
self.$i0.to_bits_le().into_iter()
$(.chain(self.$idx.to_bits_le().into_iter()))+
.collect()
(&self).write_bits_le(vec);
}

/// A helper method to return a concatenated list of big-endian bits from the circuits.
#[inline]
fn to_bits_be(&self) -> Vec<Self::Boolean> {
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
self.$i0.to_bits_be().into_iter()
$(.chain(self.$idx.to_bits_be().into_iter()))+
.collect()
(&self).write_bits_be(vec);
}
}

Expand All @@ -117,20 +129,18 @@ macro_rules! to_bits_tuple {

/// A helper method to return a concatenated list of little-endian bits from the circuits.
#[inline]
fn to_bits_le(&self) -> Vec<Self::Boolean> {
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
self.$i0.to_bits_le().into_iter()
$(.chain(self.$idx.to_bits_le().into_iter()))+
.collect()
self.$i0.write_bits_le(vec);
$(self.$idx.write_bits_le(vec);)+
}

/// A helper method to return a concatenated list of big-endian bits from the circuits.
#[inline]
fn to_bits_be(&self) -> Vec<Self::Boolean> {
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
self.$i0.to_bits_be().into_iter()
$(.chain(self.$idx.to_bits_be().into_iter()))+
.collect()
self.$i0.write_bits_be(vec);
$(self.$idx.write_bits_be(vec);)+
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions circuit/program/src/data/ciphertext/to_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ impl<A: Aleo> ToBits for Ciphertext<A> {
type Boolean = Boolean<A>;

/// Returns this ciphertext as a list of **little-endian** bits.
fn to_bits_le(&self) -> Vec<Self::Boolean> {
let bits_le = self.0.to_bits_le();
assert_eq!(self.0.len() * A::BaseField::size_in_bits(), bits_le.len());
bits_le
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
let initial_len = vec.len();
self.0.write_bits_le(vec);
assert_eq!(self.0.len() * A::BaseField::size_in_bits(), vec.len() - initial_len);
}

/// Returns this ciphertext as a list of **big-endian** bits.
fn to_bits_be(&self) -> Vec<Self::Boolean> {
let bits_be = self.0.to_bits_be();
assert_eq!(self.0.len() * A::BaseField::size_in_bits(), bits_be.len());
bits_be
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
let initial_len = vec.len();
self.0.write_bits_be(vec);
assert_eq!(self.0.len() * A::BaseField::size_in_bits(), vec.len() - initial_len);
}
}
24 changes: 12 additions & 12 deletions circuit/program/src/data/identifier/to_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,31 @@ impl<A: Aleo> ToBits for Identifier<A> {
type Boolean = Boolean<A>;

/// Returns the little-endian bits of the identifier.
fn to_bits_le(&self) -> Vec<Self::Boolean> {
self.0.to_bits_le()[..8 * self.1 as usize].to_vec()
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
(&self).write_bits_le(vec);
}

/// Returns the big-endian bits of the identifier.
fn to_bits_be(&self) -> Vec<Self::Boolean> {
let mut bits = self.to_bits_le();
bits.reverse();
bits
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
(&self).write_bits_be(vec);
}
}

impl<A: Aleo> ToBits for &Identifier<A> {
type Boolean = Boolean<A>;

/// Returns the little-endian bits of the identifier.
fn to_bits_le(&self) -> Vec<Self::Boolean> {
self.0.to_bits_le()[..8 * self.1 as usize].to_vec()
fn write_bits_le(&self, vec: &mut Vec<Self::Boolean>) {
let initial_len = vec.len();
self.0.write_bits_le(vec);
vec.truncate(initial_len + 8 * self.1 as usize);
}

/// Returns the big-endian bits of the identifier.
fn to_bits_be(&self) -> Vec<Self::Boolean> {
let mut bits = self.to_bits_le();
bits.reverse();
bits
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>) {
let initial_len = vec.len();
self.write_bits_le(vec);
vec[initial_len..].reverse();
}
}

Expand Down
76 changes: 38 additions & 38 deletions circuit/program/src/data/literal/to_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,60 +18,60 @@ impl<A: Aleo> ToBits for Literal<A> {
type Boolean = Boolean<A>;

/// Returns the little-endian bits of the literal.
fn to_bits_le(&self) -> Vec<Boolean<A>> {
(&self).to_bits_le()
fn write_bits_le(&self, vec: &mut Vec<Boolean<A>>) {
(&self).write_bits_le(vec);
}

/// Returns the big-endian bits of the literal.
fn to_bits_be(&self) -> Vec<Boolean<A>> {
(&self).to_bits_be()
fn write_bits_be(&self, vec: &mut Vec<Boolean<A>>) {
(&self).write_bits_be(vec);
}
}

impl<A: Aleo> ToBits for &Literal<A> {
type Boolean = Boolean<A>;

/// Returns the little-endian bits of the literal.
fn to_bits_le(&self) -> Vec<Boolean<A>> {
fn write_bits_le(&self, vec: &mut Vec<Boolean<A>>) {
match self {
Literal::Address(literal) => literal.to_bits_le(),
Literal::Boolean(literal) => literal.to_bits_le(),
Literal::Field(literal) => literal.to_bits_le(),
Literal::Group(literal) => literal.to_bits_le(),
Literal::I8(literal) => literal.to_bits_le(),
Literal::I16(literal) => literal.to_bits_le(),
Literal::I32(literal) => literal.to_bits_le(),
Literal::I64(literal) => literal.to_bits_le(),
Literal::I128(literal) => literal.to_bits_le(),
Literal::U8(literal) => literal.to_bits_le(),
Literal::U16(literal) => literal.to_bits_le(),
Literal::U32(literal) => literal.to_bits_le(),
Literal::U64(literal) => literal.to_bits_le(),
Literal::U128(literal) => literal.to_bits_le(),
Literal::Scalar(literal) => literal.to_bits_le(),
Literal::String(literal) => literal.to_bits_le(),
Literal::Address(literal) => literal.write_bits_le(vec),
Literal::Boolean(literal) => literal.write_bits_le(vec),
Literal::Field(literal) => literal.write_bits_le(vec),
Literal::Group(literal) => literal.write_bits_le(vec),
Literal::I8(literal) => literal.write_bits_le(vec),
Literal::I16(literal) => literal.write_bits_le(vec),
Literal::I32(literal) => literal.write_bits_le(vec),
Literal::I64(literal) => literal.write_bits_le(vec),
Literal::I128(literal) => literal.write_bits_le(vec),
Literal::U8(literal) => literal.write_bits_le(vec),
Literal::U16(literal) => literal.write_bits_le(vec),
Literal::U32(literal) => literal.write_bits_le(vec),
Literal::U64(literal) => literal.write_bits_le(vec),
Literal::U128(literal) => literal.write_bits_le(vec),
Literal::Scalar(literal) => literal.write_bits_le(vec),
Literal::String(literal) => literal.write_bits_le(vec),
}
}

/// Returns the big-endian bits of the literal.
fn to_bits_be(&self) -> Vec<Boolean<A>> {
fn write_bits_be(&self, vec: &mut Vec<Boolean<A>>) {
match self {
Literal::Address(literal) => literal.to_bits_be(),
Literal::Boolean(literal) => literal.to_bits_be(),
Literal::Field(literal) => literal.to_bits_be(),
Literal::Group(literal) => literal.to_bits_be(),
Literal::I8(literal) => literal.to_bits_be(),
Literal::I16(literal) => literal.to_bits_be(),
Literal::I32(literal) => literal.to_bits_be(),
Literal::I64(literal) => literal.to_bits_be(),
Literal::I128(literal) => literal.to_bits_be(),
Literal::U8(literal) => literal.to_bits_be(),
Literal::U16(literal) => literal.to_bits_be(),
Literal::U32(literal) => literal.to_bits_be(),
Literal::U64(literal) => literal.to_bits_be(),
Literal::U128(literal) => literal.to_bits_be(),
Literal::Scalar(literal) => literal.to_bits_be(),
Literal::String(literal) => literal.to_bits_be(),
Literal::Address(literal) => literal.write_bits_be(vec),
Literal::Boolean(literal) => literal.write_bits_be(vec),
Literal::Field(literal) => literal.write_bits_be(vec),
Literal::Group(literal) => literal.write_bits_be(vec),
Literal::I8(literal) => literal.write_bits_be(vec),
Literal::I16(literal) => literal.write_bits_be(vec),
Literal::I32(literal) => literal.write_bits_be(vec),
Literal::I64(literal) => literal.write_bits_be(vec),
Literal::I128(literal) => literal.write_bits_be(vec),
Literal::U8(literal) => literal.write_bits_be(vec),
Literal::U16(literal) => literal.write_bits_be(vec),
Literal::U32(literal) => literal.write_bits_be(vec),
Literal::U64(literal) => literal.write_bits_be(vec),
Literal::U128(literal) => literal.write_bits_be(vec),
Literal::Scalar(literal) => literal.write_bits_be(vec),
Literal::String(literal) => literal.write_bits_be(vec),
}
}
}
6 changes: 4 additions & 2 deletions circuit/program/src/data/plaintext/from_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ impl<A: Aleo> FromFields for Plaintext<A> {
}

// Unpack the field elements into little-endian bits, and reverse the list for popping the terminus bit off.
let mut bits_le =
fields.iter().flat_map(|field| field.to_bits_le()[..A::BaseField::size_in_data_bits()].to_vec()).rev();
let mut bits_le = fields
.iter()
.flat_map(|field| field.to_bits_le().into_iter().take(A::BaseField::size_in_data_bits()))
.rev();
// Remove the terminus bit that was added during encoding.
for boolean in bits_le.by_ref() {
// Drop all extraneous `0` bits, in addition to the final `1` bit.
Expand Down
Loading

0 comments on commit a6c5cf5

Please sign in to comment.