Skip to content

Commit b1dde7d

Browse files
authored
Merge pull request #2939 from ProvableHQ/feat/bytes-continued
[Feature] ECDSA and bit operations.
2 parents 90aea8b + 6f33a31 commit b1dde7d

88 files changed

Lines changed: 7801 additions & 382 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 205 additions & 50 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ version = "3"
448448
[workspace.dependencies.dotenvy]
449449
version = "0.15"
450450

451+
[workspace.dependencies.enum-iterator]
452+
version = "2.1"
453+
451454
[workspace.dependencies.expect-test]
452455
version = "1.4.1"
453456

circuit/environment/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pub mod prelude {
8585
Square as _,
8686
SquareRoot as _,
8787
ToBits as _,
88+
ToBitsRaw as _,
8889
string_parser,
8990
types::{
9091
integer_magnitude::Magnitude,

circuit/environment/src/traits/to.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ pub trait ToFields {
6565
fn to_fields(&self) -> Vec<Self::Field>;
6666
}
6767

68+
/// Unary operator for converting to a list of base fields.
69+
pub trait ToFieldsRaw: ToFields {
70+
/// Returns the circuit as a list of base field elements using the raw bits.
71+
fn to_fields_raw(&self) -> Vec<Self::Field>;
72+
}
73+
6874
/// Unary operator for converting to an affine group.
6975
pub trait ToGroup {
7076
type Group: GroupTrait<Self::Scalar>;

circuit/environment/src/traits/to_bits.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@ pub trait ToBits {
3838
fn write_bits_be(&self, vec: &mut Vec<Self::Boolean>);
3939
}
4040

41+
/// Unary operator for converting to raw bits.
42+
pub trait ToBitsRaw: ToBits {
43+
/// Returns the little-endian bits without variant or identifier bits of the circuit.
44+
fn to_bits_raw_le(&self) -> Vec<Self::Boolean> {
45+
let mut bits = vec![];
46+
self.write_bits_raw_le(&mut bits);
47+
bits
48+
}
49+
50+
fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>);
51+
52+
/// Returns the bits-endian bits without variant or identifier bits of the circuit.
53+
fn to_bits_raw_be(&self) -> Vec<Self::Boolean> {
54+
let mut bits = vec![];
55+
self.write_bits_raw_be(&mut bits);
56+
bits
57+
}
58+
59+
fn write_bits_raw_be(&self, vec: &mut Vec<Self::Boolean>);
60+
}
61+
4162
/********************/
4263
/****** Arrays ******/
4364
/********************/
@@ -100,6 +121,58 @@ impl<C: ToBits<Boolean = B>, B: BooleanTrait> ToBits for &[C] {
100121
}
101122
}
102123

124+
impl<C: ToBitsRaw<Boolean = B>, B: BooleanTrait> ToBitsRaw for Vec<C> {
125+
/// A helper method to return a concatenated list of little-endian bits without variant or identifier bits from the circuits.
126+
#[inline]
127+
fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>) {
128+
// The vector is order-preserving, meaning the first circuit in is the first circuit bits out.
129+
self.as_slice().write_bits_raw_le(vec);
130+
}
131+
132+
/// A helper method to return a concatenated list of bits-endian bits without variant or identifier bits from the circuits.
133+
#[inline]
134+
fn write_bits_raw_be(&self, vec: &mut Vec<Self::Boolean>) {
135+
// The vector is order-preserving, meaning the first circuit in is the first circuit bits out.
136+
self.as_slice().write_bits_raw_be(vec);
137+
}
138+
}
139+
140+
impl<C: ToBitsRaw<Boolean = B>, B: BooleanTrait, const N: usize> ToBitsRaw for [C; N] {
141+
/// A helper method to return a concatenated list of little-endian bits without variant or identifier bits from the circuits.
142+
#[inline]
143+
fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>) {
144+
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
145+
self.as_slice().write_bits_raw_le(vec);
146+
}
147+
148+
/// A helper method to return a concatenated list of bits-endian bits without variant or identifier bits from the circuits.
149+
#[inline]
150+
fn write_bits_raw_be(&self, vec: &mut Vec<Self::Boolean>) {
151+
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
152+
self.as_slice().write_bits_raw_be(vec);
153+
}
154+
}
155+
156+
impl<C: ToBitsRaw<Boolean = B>, B: BooleanTrait> ToBitsRaw for &[C] {
157+
/// A helper method to return a concatenated list of little-endian bits without variant or identifier bits from the circuits.
158+
#[inline]
159+
fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>) {
160+
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
161+
for elem in self.iter() {
162+
elem.write_bits_raw_le(vec);
163+
}
164+
}
165+
166+
/// A helper method to return a concatenated list of bits-endian bits without variant or identifier bits from the circuits.
167+
#[inline]
168+
fn write_bits_raw_be(&self, vec: &mut Vec<Self::Boolean>) {
169+
// The slice is order-preserving, meaning the first circuit in is the first circuit bits out.
170+
for elem in self.iter() {
171+
elem.write_bits_raw_be(vec);
172+
}
173+
}
174+
}
175+
103176
/********************/
104177
/****** Tuples ******/
105178
/********************/
@@ -144,6 +217,40 @@ macro_rules! to_bits_tuple {
144217
$(self.$idx.write_bits_be(vec);)+
145218
}
146219
}
220+
221+
impl<B: BooleanTrait, $t0: ToBitsRaw<Boolean = B>, $($ty: ToBitsRaw<Boolean = B>),+> ToBitsRaw for ($t0, $($ty),+) {
222+
/// A helper method to return a concatenated list of little-endian bits without variant or identifier bits from the circuits.
223+
#[inline]
224+
fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>) {
225+
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
226+
(&self).write_bits_raw_le(vec);
227+
}
228+
229+
/// A helper method to return a concatenated list of bits-endian bits without variant or identifier bits from the circuits.
230+
#[inline]
231+
fn write_bits_raw_be(&self, vec: &mut Vec<Self::Boolean>) {
232+
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
233+
(&self).write_bits_raw_be(vec);
234+
}
235+
}
236+
237+
impl<'a, B: BooleanTrait, $t0: ToBitsRaw<Boolean = B>, $($ty: ToBitsRaw<Boolean = B>),+> ToBitsRaw for &'a ($t0, $($ty),+) {
238+
/// A helper method to return a concatenated list of little-endian bits without variant or identifier bits from the circuits.
239+
#[inline]
240+
fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>) {
241+
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
242+
self.$i0.write_bits_raw_le(vec);
243+
$(self.$idx.write_bits_raw_le(vec);)+
244+
}
245+
246+
/// A helper method to return a concatenated list of bits-endian bits without variant or identifier bits from the circuits.
247+
#[inline]
248+
fn write_bits_raw_be(&self, vec: &mut Vec<Self::Boolean>) {
249+
// The tuple is order-preserving, meaning the first circuit in is the first circuit bits out.
250+
self.$i0.write_bits_raw_be(vec);
251+
$(self.$idx.write_bits_raw_be(vec);)+
252+
}
253+
}
147254
}
148255
}
149256

circuit/program/src/data/plaintext/from_bits.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<A: Aleo> Plaintext<A> {
6666
let variant = [variant1, variant2];
6767

6868
// Literal
69-
if variant == [false, false] {
69+
if variant == PlaintextType::<A::Network>::LITERAL_PREFIX_BITS {
7070
let literal_variant = U8::from_bits_le(next_bits(8));
7171
let literal_size = U16::from_bits_le(next_bits(16)).eject_value();
7272
let literal = Literal::from_bits_le(&literal_variant, next_bits(*literal_size as usize));
@@ -77,7 +77,7 @@ impl<A: Aleo> Plaintext<A> {
7777
Self::Literal(literal, cell)
7878
}
7979
// Struct
80-
else if variant == [false, true] {
80+
else if variant == PlaintextType::<A::Network>::STRUCT_PREFIX_BITS {
8181
let num_members = U8::from_bits_le(next_bits(8)).eject_value();
8282

8383
let mut members = IndexMap::with_capacity(*num_members as usize);
@@ -97,7 +97,7 @@ impl<A: Aleo> Plaintext<A> {
9797
Self::Struct(members, cell)
9898
}
9999
// Array
100-
else if variant == [true, false] {
100+
else if variant == PlaintextType::<A::Network>::ARRAY_PREFIX_BITS {
101101
let num_elements = U32::from_bits_le(next_bits(32)).eject_value();
102102

103103
let mut elements = Vec::with_capacity(*num_elements as usize);
@@ -155,7 +155,7 @@ impl<A: Aleo> Plaintext<A> {
155155
let variant = [variant1, variant2];
156156

157157
// Literal
158-
if variant == [false, false] {
158+
if variant == PlaintextType::<A::Network>::LITERAL_PREFIX_BITS {
159159
let literal_variant = U8::from_bits_be(next_bits(8));
160160
let literal_size = U16::from_bits_be(next_bits(16)).eject_value();
161161
let literal = Literal::from_bits_be(&literal_variant, next_bits(*literal_size as usize));
@@ -166,7 +166,7 @@ impl<A: Aleo> Plaintext<A> {
166166
Self::Literal(literal, cell)
167167
}
168168
// Struct
169-
else if variant == [false, true] {
169+
else if variant == PlaintextType::<A::Network>::STRUCT_PREFIX_BITS {
170170
let num_members = U8::from_bits_be(next_bits(8)).eject_value();
171171

172172
let mut members = IndexMap::with_capacity(*num_members as usize);
@@ -186,7 +186,7 @@ impl<A: Aleo> Plaintext<A> {
186186
Self::Struct(members, cell)
187187
}
188188
// Array
189-
else if variant == [true, false] {
189+
else if variant == PlaintextType::<A::Network>::ARRAY_PREFIX_BITS {
190190
let num_elements = U32::from_bits_be(next_bits(32)).eject_value();
191191

192192
let mut elements = Vec::with_capacity(*num_elements as usize);

circuit/program/src/data/plaintext/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ mod from_fields;
2626
mod num_randomizers;
2727
mod size_in_fields;
2828
mod to_bits;
29+
mod to_bits_raw;
2930
mod to_fields;
31+
mod to_fields_raw;
3032

3133
use crate::{Access, Ciphertext, Identifier, Literal, Visibility};
34+
use console::PlaintextType;
3235
use snarkvm_circuit_network::Aleo;
3336
use snarkvm_circuit_types::{Address, Boolean, Field, Scalar, U8, U16, U32, environment::prelude::*};
3437

@@ -42,6 +45,31 @@ pub enum Plaintext<A: Aleo> {
4245
Array(Vec<Plaintext<A>>, OnceCell<Vec<Boolean<A>>>),
4346
}
4447

48+
impl<A: Aleo> Plaintext<A> {
49+
/// Returns a new `Plaintext::Array` from `Vec<Boolean<A>>`, checking that the length is correct.
50+
pub fn from_bit_array(bits: Vec<Boolean<A>>, length: u32) -> Result<Self> {
51+
ensure!(bits.len() == length as usize, "Expected '{length}' bits, got '{}' bits", bits.len());
52+
Ok(Self::Array(bits.into_iter().map(|bit| Plaintext::from(Literal::Boolean(bit))).collect(), OnceCell::new()))
53+
}
54+
55+
/// Returns the `Plaintext` as a `Vec<Boolean<A>>`, if it is a bit array.
56+
pub fn as_bit_array(&self) -> Result<Vec<Boolean<A>>> {
57+
match self {
58+
Self::Array(elements, _) => {
59+
let mut bits = Vec::with_capacity(elements.len());
60+
for element in elements {
61+
match element {
62+
Self::Literal(Literal::Boolean(bit), _) => bits.push(bit.clone()),
63+
_ => bail!("Expected a bit array, found a non-boolean element."),
64+
}
65+
}
66+
Ok(bits)
67+
}
68+
_ => bail!("Expected a bit array, found a non-array plaintext."),
69+
}
70+
}
71+
}
72+
4573
impl<A: Aleo> Inject for Plaintext<A> {
4674
type Primitive = console::Plaintext<A::Network>;
4775

circuit/program/src/data/plaintext/to_bits.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
use super::*;
1717

18+
use console::PlaintextType;
19+
1820
impl<A: Aleo> ToBits for Plaintext<A> {
1921
type Boolean = Boolean<A>;
2022

@@ -25,7 +27,7 @@ impl<A: Aleo> ToBits for Plaintext<A> {
2527
// Compute the bits of the literal.
2628
let bits = bits_le.get_or_init(|| {
2729
let mut bits_le = Vec::new();
28-
bits_le.extend([Boolean::constant(false), Boolean::constant(false)]); // Variant bit.
30+
bits_le.extend(PlaintextType::<A::Network>::LITERAL_PREFIX_BITS.map(Boolean::constant)); // Variant bit.
2931
literal.variant().write_bits_le(&mut bits_le);
3032
literal.size_in_bits().write_bits_le(&mut bits_le);
3133
literal.write_bits_le(&mut bits_le);
@@ -39,7 +41,7 @@ impl<A: Aleo> ToBits for Plaintext<A> {
3941
// Compute the bits of the struct.
4042
let bits = bits_le.get_or_init(|| {
4143
let mut bits_le = Vec::new();
42-
bits_le.extend([Boolean::constant(false), Boolean::constant(true)]); // Variant bit.
44+
bits_le.extend(PlaintextType::<A::Network>::STRUCT_PREFIX_BITS.map(Boolean::constant)); // Variant bit.
4345
U8::constant(console::U8::new(members.len() as u8)).write_bits_le(&mut bits_le);
4446
for (identifier, value) in members {
4547
let value_bits = value.to_bits_le();
@@ -58,7 +60,7 @@ impl<A: Aleo> ToBits for Plaintext<A> {
5860
// Compute the bits of the array.
5961
let bits = bits_le.get_or_init(|| {
6062
let mut bits_le = Vec::new();
61-
bits_le.extend([Boolean::constant(true), Boolean::constant(false)]); // Variant bit.
63+
bits_le.extend(PlaintextType::<A::Network>::ARRAY_PREFIX_BITS.map(Boolean::constant)); // Variant bit.
6264
U32::constant(console::U32::new(elements.len() as u32)).write_bits_le(&mut bits_le);
6365
for value in elements {
6466
let value_bits = value.to_bits_le();
@@ -81,7 +83,7 @@ impl<A: Aleo> ToBits for Plaintext<A> {
8183
// Compute the bits of the literal.
8284
let bits = bits_be.get_or_init(|| {
8385
let mut bits_be = Vec::new();
84-
bits_be.extend([Boolean::constant(false), Boolean::constant(false)]); // Variant bit.
86+
bits_be.extend(PlaintextType::<A::Network>::LITERAL_PREFIX_BITS.map(Boolean::constant)); // Variant bit.
8587
literal.variant().write_bits_be(&mut bits_be);
8688
literal.size_in_bits().write_bits_be(&mut bits_be);
8789
literal.write_bits_be(&mut bits_be);
@@ -95,7 +97,7 @@ impl<A: Aleo> ToBits for Plaintext<A> {
9597
// Compute the bits of the struct.
9698
let bits = bits_be.get_or_init(|| {
9799
let mut bits_be = Vec::new();
98-
bits_be.extend([Boolean::constant(false), Boolean::constant(true)]); // Variant bit.
100+
bits_be.extend(PlaintextType::<A::Network>::STRUCT_PREFIX_BITS.map(Boolean::constant)); // Variant bit.
99101
U8::constant(console::U8::new(members.len() as u8)).write_bits_be(&mut bits_be);
100102
for (identifier, value) in members {
101103
let value_bits = value.to_bits_be();
@@ -114,7 +116,7 @@ impl<A: Aleo> ToBits for Plaintext<A> {
114116
// Compute the bits of the array.
115117
let bits = bits_be.get_or_init(|| {
116118
let mut bits_be = Vec::new();
117-
bits_be.extend([Boolean::constant(true), Boolean::constant(false)]); // Variant bit.
119+
bits_be.extend(PlaintextType::<A::Network>::ARRAY_PREFIX_BITS.map(Boolean::constant)); // Variant bit.
118120
U32::constant(console::U32::new(elements.len() as u32)).write_bits_be(&mut bits_be);
119121
for value in elements {
120122
let value_bits = value.to_bits_be();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2019-2025 Provable Inc.
2+
// This file is part of the snarkVM library.
3+
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at:
7+
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use super::*;
17+
18+
impl<A: Aleo> ToBitsRaw for Plaintext<A> {
19+
/// Returns this plaintext as a list of raw **little-endian** bits.
20+
fn write_bits_raw_le(&self, vec: &mut Vec<Boolean<A>>) {
21+
match self {
22+
Self::Literal(literal, _) => {
23+
// Extend the vector with the bits of the literal.
24+
vec.extend_from_slice(&literal.to_bits_le());
25+
}
26+
Self::Struct(members, _) => {
27+
// Compute the bits of the struct.
28+
for (_, value) in members {
29+
vec.extend(value.to_bits_raw_le());
30+
}
31+
}
32+
Self::Array(elements, _) => {
33+
// Compute the bits of the array.
34+
for value in elements {
35+
vec.extend(value.to_bits_raw_le());
36+
}
37+
}
38+
}
39+
}
40+
41+
/// Returns this plaintext as a list of raw **big-endian** bits.
42+
fn write_bits_raw_be(&self, vec: &mut Vec<Boolean<A>>) {
43+
match self {
44+
Self::Literal(literal, _) => {
45+
// Extend the vector with the bits of the literal.
46+
vec.extend_from_slice(&literal.to_bits_be());
47+
}
48+
Self::Struct(members, _) => {
49+
// Compute the bits of the struct.
50+
for (_, value) in members {
51+
vec.extend(value.to_bits_raw_be());
52+
}
53+
}
54+
Self::Array(elements, _) => {
55+
// Compute the bits of the array.
56+
for value in elements {
57+
vec.extend(value.to_bits_raw_be());
58+
}
59+
}
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)