Skip to content

Commit bdfebdb

Browse files
authored
Merge pull request #3004 from ProvableHQ/feat/snark-verify
[Feature] Implement `snark.verify` opcode
2 parents 9acfa31 + 8332ee3 commit bdfebdb

File tree

37 files changed

+1980
-105
lines changed

37 files changed

+1980
-105
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ commands:
5858
5959
# Put cargo/rustup first in PATH for this step and subsequent steps
6060
$Env:Path = "$Env:USERPROFILE\.cargo\bin;$Env:Path"
61-
61+
6262
# Select the toolchain
6363
rustup default 1.88.0-x86_64-pc-windows-msvc
6464

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

algorithms/src/snark/varuna/mode.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515

1616
use core::fmt::Debug;
17+
use snarkvm_utilities::{FromBytes, ToBytes, io_error};
18+
use std::io;
1719

1820
/// A trait to specify the SNARK mode.
1921
pub trait SNARKMode: 'static + Copy + Clone + Debug + PartialEq + Eq + Sync + Send {
@@ -37,8 +39,26 @@ impl SNARKMode for VarunaNonHidingMode {
3739
}
3840

3941
/// The different Varuna Versions.
42+
#[repr(u8)]
4043
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
4144
pub enum VarunaVersion {
4245
V1 = 1,
4346
V2 = 2,
4447
}
48+
49+
impl ToBytes for VarunaVersion {
50+
fn write_le<W: io::Write>(&self, writer: W) -> io::Result<()> {
51+
(*self as u8).write_le(writer)
52+
}
53+
}
54+
55+
impl FromBytes for VarunaVersion {
56+
fn read_le<R: io::Read>(reader: R) -> io::Result<Self> {
57+
match u8::read_le(reader)? {
58+
0 => Err(io_error("Zero is not a valid Varuna version")),
59+
1 => Ok(Self::V1),
60+
2 => Ok(Self::V2),
61+
_ => Err(io_error("Invalid Varuna version")),
62+
}
63+
}
64+
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// limitations under the License.
1515

1616
use super::*;
17-
use snarkvm_circuit_types::U8;
17+
use snarkvm_circuit_types::{U8, U32};
1818

1919
impl<A: Aleo> ToBits for Future<A> {
2020
type Boolean = Boolean<A>;
@@ -39,7 +39,11 @@ impl<A: Aleo> ToBits for Future<A> {
3939
for argument in &self.arguments {
4040
let argument_bits = argument.to_bits_le();
4141
// Write the size of the argument.
42-
U16::constant(console::U16::new(argument_bits.len() as u16)).write_bits_le(vec);
42+
let argument_length = argument_bits.len();
43+
match argument_length <= u16::MAX as usize {
44+
true => U16::constant(console::U16::new(argument_length as u16)).write_bits_le(vec),
45+
false => U32::constant(console::U32::new(argument_length as u32)).write_bits_le(vec),
46+
}
4347
// Write the argument.
4448
vec.extend_from_slice(&argument_bits);
4549
}
@@ -65,7 +69,11 @@ impl<A: Aleo> ToBits for Future<A> {
6569
for argument in &self.arguments {
6670
let argument_bits = argument.to_bits_be();
6771
// Write the size of the argument.
68-
U16::constant(console::U16::new(argument_bits.len() as u16)).write_bits_be(vec);
72+
let argument_length = argument_bits.len();
73+
match argument_length <= u16::MAX as usize {
74+
true => U16::constant(console::U16::new(argument_length as u16)).write_bits_be(vec),
75+
false => U32::constant(console::U32::new(argument_length as u32)).write_bits_be(vec),
76+
}
6977
// Write the argument.
7078
vec.extend_from_slice(&argument_bits);
7179
}

console/network/src/consensus_heights.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ pub enum ConsensusVersion {
4949
V12 = 12,
5050
/// V13: Introduces external structs.
5151
V13 = 13,
52-
/// V14: Increase the program size limit to 512 kB and transaction size limit to 540 kB.
53-
/// Introduces `aleo::GENERATOR` and `aleo::GENERATOR_POWERS` opcodes.
52+
/// V14: Increase the program size limit to 512 kB, the transaction size limit to 540 kB,
53+
/// the array size limit to 2048, and the `Future` argument bit size to 32 bits.
54+
/// Introduces `aleo::GENERATOR`, `aleo::GENERATOR_POWERS`, and `snark.verify` opcodes.
5455
V14 = 14,
5556
}
5657

@@ -318,6 +319,11 @@ mod tests {
318319
assert!(*version > previous_version);
319320
previous_version = *version;
320321
}
322+
let mut previous_version = N::MAX_ARRAY_ELEMENTS.first().unwrap().0;
323+
for (version, _) in N::MAX_ARRAY_ELEMENTS.iter().skip(1) {
324+
assert!(*version > previous_version);
325+
previous_version = *version;
326+
}
321327
let mut previous_version = N::MAX_PROGRAM_SIZE.first().unwrap().0;
322328
for (version, _) in N::MAX_PROGRAM_SIZE.iter().skip(1) {
323329
assert!(*version > previous_version);
@@ -362,6 +368,12 @@ mod tests {
362368
// Double-check that consensus_config_value returns the correct value.
363369
assert_eq!(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, height).unwrap(), *value);
364370
}
371+
for (version, value) in N::MAX_ARRAY_ELEMENTS.iter() {
372+
// Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
373+
let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
374+
// Double-check that consensus_config_value returns the correct value.
375+
assert_eq!(consensus_config_value!(N, MAX_ARRAY_ELEMENTS, height).unwrap(), *value);
376+
}
365377
for (version, value) in N::MAX_PROGRAM_SIZE.iter() {
366378
// Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
367379
let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
@@ -387,6 +399,7 @@ mod tests {
387399
for (_, height) in N::CONSENSUS_VERSION_HEIGHTS().iter() {
388400
assert!(consensus_config_value!(N, MAX_CERTIFICATES, *height).is_some());
389401
assert!(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, *height).is_some());
402+
assert!(consensus_config_value!(N, MAX_ARRAY_ELEMENTS, *height).is_some());
390403
assert!(consensus_config_value!(N, MAX_PROGRAM_SIZE, *height).is_some());
391404
assert!(consensus_config_value!(N, MAX_TRANSACTION_SIZE, *height).is_some());
392405
assert!(consensus_config_value!(N, MAX_WRITES, *height).is_some());
@@ -403,6 +416,16 @@ mod tests {
403416
}
404417
}
405418

419+
/// Ensure that `MAX_ARRAY_ELEMENTS` increases and is correctly defined.
420+
/// See the constant declaration for an explanation why.
421+
fn max_array_elements_increasing<N: Network>() {
422+
let mut previous_value = N::MAX_ARRAY_ELEMENTS.first().unwrap().1;
423+
for (_, value) in N::MAX_ARRAY_ELEMENTS.iter().skip(1) {
424+
assert!(*value >= previous_value);
425+
previous_value = *value;
426+
}
427+
}
428+
406429
/// Ensure that `MAX_TRANSACTION_SIZE` is at least 28KB greater than `MAX_PROGRAM_SIZE` for all consensus versions.
407430
/// This overhead accounts for proofs, signatures, and other transaction metadata.
408431
fn transaction_size_exceeds_program_size<N: Network>() {
@@ -425,6 +448,7 @@ mod tests {
425448
let _ = [N1::CONSENSUS_VERSION_HEIGHTS, N2::CONSENSUS_VERSION_HEIGHTS, N3::CONSENSUS_VERSION_HEIGHTS];
426449
let _ = [N1::MAX_CERTIFICATES, N2::MAX_CERTIFICATES, N3::MAX_CERTIFICATES];
427450
let _ = [N1::TRANSACTION_SPEND_LIMIT, N2::TRANSACTION_SPEND_LIMIT, N3::TRANSACTION_SPEND_LIMIT];
451+
let _ = [N1::MAX_ARRAY_ELEMENTS, N2::MAX_ARRAY_ELEMENTS, N3::MAX_ARRAY_ELEMENTS];
428452
let _ = [N1::MAX_PROGRAM_SIZE, N2::MAX_PROGRAM_SIZE, N3::MAX_PROGRAM_SIZE];
429453
let _ = [N1::MAX_TRANSACTION_SIZE, N2::MAX_TRANSACTION_SIZE, N3::MAX_TRANSACTION_SIZE];
430454
let _ = [N1::MAX_WRITES, N2::MAX_WRITES, N3::MAX_WRITES];
@@ -470,6 +494,10 @@ mod tests {
470494
max_certificates_increasing::<TestnetV0>();
471495
max_certificates_increasing::<CanaryV0>();
472496

497+
max_array_elements_increasing::<MainnetV0>();
498+
max_array_elements_increasing::<TestnetV0>();
499+
max_array_elements_increasing::<CanaryV0>();
500+
473501
transaction_size_exceeds_program_size::<MainnetV0>();
474502
transaction_size_exceeds_program_size::<TestnetV0>();
475503
transaction_size_exceeds_program_size::<CanaryV0>();

console/network/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,9 @@ pub trait Network:
192192

193193
/// The minimum number of elements in an array.
194194
const MIN_ARRAY_ELEMENTS: usize = 1; // This ensures the array is not empty.
195-
/// The maximum number of elements in an array.
196-
const MAX_ARRAY_ELEMENTS: usize = 512;
195+
/// A list of (consensus_version, size) pairs indicating the maximum number of elements in an array.
196+
const MAX_ARRAY_ELEMENTS: [(ConsensusVersion, usize); 3] =
197+
[(ConsensusVersion::V1, 32), (ConsensusVersion::V11, 512), (ConsensusVersion::V14, 2048)];
197198

198199
/// The minimum number of entries in a record.
199200
const MIN_RECORD_ENTRIES: usize = 1; // This accounts for 'record.owner'.
@@ -315,6 +316,11 @@ pub trait Network:
315316
fn CONSENSUS_HEIGHT(version: ConsensusVersion) -> Result<u32> {
316317
Ok(Self::CONSENSUS_VERSION_HEIGHTS().get(version as usize - 1).ok_or(anyhow!("Invalid consensus version"))?.1)
317318
}
319+
/// Returns the last `MAX_ARRAY_ELEMENTS` value.
320+
#[allow(non_snake_case)]
321+
fn LATEST_MAX_ARRAY_ELEMENTS() -> usize {
322+
Self::MAX_ARRAY_ELEMENTS.last().expect("MAX_ARRAY_ELEMENTS must have at least one entry").1
323+
}
318324
/// Returns the last `MAX_CERTIFICATES` value.
319325
#[allow(non_snake_case)]
320326
fn LATEST_MAX_CERTIFICATES() -> u16 {

console/program/src/data/future/to_bits.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use super::*;
1717

1818
impl<N: Network> ToBits for Future<N> {
1919
/// Returns the future as a list of **little-endian** bits.
20+
// Note: Any updates to bit serialization will require updating `FinalizeType::future_size_in_bits`.
2021
#[inline]
2122
fn write_bits_le(&self, vec: &mut Vec<bool>) {
2223
// Write the bits for the program ID.
@@ -39,14 +40,23 @@ impl<N: Network> ToBits for Future<N> {
3940
let argument_bits = argument.to_bits_le();
4041

4142
// Write the size of the argument.
42-
u16::try_from(argument_bits.len()).or_halt_with::<N>("argument exceeds u16::MAX bits").write_bits_le(vec);
43+
let argument_length = argument_bits.len();
44+
match argument_length <= u16::MAX as usize {
45+
true => u16::try_from(argument_length)
46+
.or_halt_with::<N>("argument exceeds u16::MAX bits")
47+
.write_bits_le(vec),
48+
false => u32::try_from(argument_length)
49+
.or_halt_with::<N>("argument exceeds u32::MAX bits")
50+
.write_bits_le(vec),
51+
}
4352

4453
// Write the argument.
4554
vec.extend_from_slice(&argument_bits);
4655
}
4756
}
4857

4958
/// Returns the future as a list of **big-endian** bits.
59+
// Note: Any updates to bit serialization will require updating `FinalizeType::future_size_in_bits`.
5060
#[inline]
5161
fn write_bits_be(&self, vec: &mut Vec<bool>) {
5262
// Write the bits for the program ID.
@@ -69,7 +79,15 @@ impl<N: Network> ToBits for Future<N> {
6979
let argument_bits = argument.to_bits_be();
7080

7181
// Write the size of the argument.
72-
u16::try_from(argument_bits.len()).or_halt_with::<N>("argument exceeds u16::MAX bits").write_bits_be(vec);
82+
let argument_length = argument_bits.len();
83+
match argument_length <= u16::MAX as usize {
84+
true => u16::try_from(argument_length)
85+
.or_halt_with::<N>("argument exceeds u16::MAX bits")
86+
.write_bits_be(vec),
87+
false => u32::try_from(argument_length)
88+
.or_halt_with::<N>("argument exceeds u32::MAX bits")
89+
.write_bits_be(vec),
90+
}
7391

7492
// Write the argument.
7593
vec.extend_from_slice(&argument_bits);

console/program/src/data/plaintext/bytes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl<N: Network> Plaintext<N> {
6161
2 => {
6262
// Read the length of the array.
6363
let num_elements = u32::read_le(&mut reader)?;
64-
if num_elements as usize > N::MAX_ARRAY_ELEMENTS {
64+
if num_elements as usize > N::LATEST_MAX_ARRAY_ELEMENTS() {
6565
return Err(error("Failed to deserialize plaintext: Array exceeds maximum length"));
6666
}
6767
// Read the elements.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<N: Network> Plaintext<N> {
9393
// Array
9494
else if variant == PlaintextType::<N>::ARRAY_PREFIX_BITS {
9595
let num_elements = u32::from_bits_le(next_bits(32)?)?;
96-
if num_elements as usize > N::MAX_ARRAY_ELEMENTS {
96+
if num_elements as usize > N::LATEST_MAX_ARRAY_ELEMENTS() {
9797
bail!("Array exceeds maximum of elements.");
9898
}
9999

@@ -176,7 +176,7 @@ impl<N: Network> Plaintext<N> {
176176
// Array
177177
else if variant == PlaintextType::<N>::ARRAY_PREFIX_BITS {
178178
let num_elements = u32::from_bits_be(next_bits(32)?)?;
179-
if num_elements as usize > N::MAX_ARRAY_ELEMENTS {
179+
if num_elements as usize > N::LATEST_MAX_ARRAY_ELEMENTS() {
180180
bail!("Array exceeds maximum of elements.");
181181
}
182182

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,40 @@ impl<N: Network> Plaintext<N> {
7171
_ => bail!("Expected a bit array, found a non-array plaintext."),
7272
}
7373
}
74+
75+
/// Returns the `Plaintext` as a `Vec<u8>`, if it is a u8 array.
76+
pub fn as_byte_array(&self) -> Result<Vec<u8>> {
77+
match self {
78+
Self::Array(elements, _) => {
79+
let mut bytes = Vec::with_capacity(elements.len());
80+
for element in elements {
81+
match element {
82+
Self::Literal(Literal::U8(byte), _) => bytes.push(**byte),
83+
_ => bail!("Expected a u8 array, found a non-u8 element."),
84+
}
85+
}
86+
Ok(bytes)
87+
}
88+
_ => bail!("Expected a u8 array, found a non-array plaintext."),
89+
}
90+
}
91+
92+
/// Returns the `Plaintext` as a `Vec<N::Field>`, if it is a field array.
93+
pub fn as_field_array(&self) -> Result<Vec<Field<N>>> {
94+
match self {
95+
Self::Array(elements, _) => {
96+
let mut fields = Vec::with_capacity(elements.len());
97+
for element in elements {
98+
match element {
99+
Self::Literal(Literal::Field(field), _) => fields.push(*field),
100+
_ => bail!("Expected an array of fields, found a non-field element."),
101+
}
102+
}
103+
Ok(fields)
104+
}
105+
_ => bail!("Expected an array of fields, found a non-array plaintext."),
106+
}
107+
}
74108
}
75109

76110
impl<N: Network> From<Literal<N>> for Plaintext<N> {

0 commit comments

Comments
 (0)