Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 12 additions & 10 deletions arm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ repository = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# If you want to try (experimental) std support, add `features = [ "std" ]` to risc0-zkvm
risc0-zkvm = { version = "3.0.3", features = [
"std",
"unstable",
], default-features = false }
serde = { version = "1.0.197", default-features = false }
], default-features = false, optional = true }
serde = { version = "1.0.197", default-features = false, features = ["derive"] }
serde_with = "3.14.1"
k256 = { version = "=0.13.3", features = [
"arithmetic",
Expand All @@ -23,21 +22,24 @@ k256 = { version = "=0.13.3", features = [
"std",
"ecdsa",
"hash2curve",
], default-features = false }
], default-features = false, optional = true }
sha3 = { version = "0.10", optional = true }
rand = "0.8"
bincode = "1.3.3"
hex = "0.4"
lazy_static = "1.5.0"
hex-literal = "0.4"
lazy_static = { version = "1.5.0", optional = true }
bytemuck = { version = "1.12", features = ["derive"] }
thiserror = "2.0.6"

[features]
default = ["transaction", "prove"]
default = ["transaction", "prove", "k256"]
k256 = ["dep:k256"]
zkvm = ["dep:risc0-zkvm", "dep:lazy_static"]
transaction = ["compliance_circuit", "dep:sha3"]
compliance_circuit = []
prove = ["risc0-zkvm/prove"]
bonsai = ["risc0-zkvm/bonsai"]
cuda = ["risc0-zkvm/cuda"]
aggregation = ["aggregation_circuit", "transaction"]
prove = ["zkvm", "risc0-zkvm/prove", "k256"]
bonsai = ["zkvm", "risc0-zkvm/bonsai"]
cuda = ["zkvm", "risc0-zkvm/cuda"]
aggregation = ["aggregation_circuit", "transaction", "zkvm", "k256"]
aggregation_circuit = []
79 changes: 39 additions & 40 deletions arm/src/action.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
//! An action represents a set of compliance units and logic verifiers.

use crate::{
action_tree::MerkleTree,
compliance::ComplianceInstance,
compliance_unit::ComplianceUnit,
error::ArmError,
logic_proof::{LogicVerifier, LogicVerifierInputs},
};
use k256::ProjectivePoint;
use risc0_zkvm::Digest;
use crate::{compliance_unit::ComplianceUnit, logic_instance::LogicVerifierInputs};
use serde::{Deserialize, Serialize};

#[cfg(feature = "k256")]
use crate::error::ArmError;
#[cfg(all(feature = "zkvm", feature = "k256"))]
use crate::{action_tree::MerkleTree, logic_proof::LogicVerifier, Digest};

/// An action consists of compliance units and logic verifier inputs.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Action {
Expand All @@ -20,6 +17,29 @@ pub struct Action {
pub logic_verifier_inputs: Vec<LogicVerifierInputs>,
}

impl Action {
/// Returns a reference to the compliance units.
pub fn get_compliance_units(&self) -> &Vec<ComplianceUnit> {
&self.compliance_units
}

/// Returns a reference to the logic verifier inputs.
pub fn get_logic_verifier_inputs(&self) -> &Vec<LogicVerifierInputs> {
&self.logic_verifier_inputs
}

/// Constructs the delta message by concatenating the delta messages
/// of each compliance unit.
pub fn get_delta_msg(&self) -> Vec<u8> {
let mut msg = Vec::new();
for unit in &self.compliance_units {
msg.extend_from_slice(&unit.instance.delta_msg());
}
msg
}
}

#[cfg(all(feature = "zkvm", feature = "k256"))]
impl Action {
/// Creates a new Action from compliance units and logic verifiers.
pub fn new(
Expand All @@ -36,33 +56,23 @@ impl Action {
})
}

/// Returns a reference to the compliance units.
pub fn get_compliance_units(&self) -> &Vec<ComplianceUnit> {
&self.compliance_units
}

/// Returns a reference to the logic verifier inputs.
pub fn get_logic_verifier_inputs(&self) -> &Vec<LogicVerifierInputs> {
&self.logic_verifier_inputs
}

/// Constructs logic verifiers from the action's compliance units and logic verifier inputs.
/// It also checks consistency between compliance instances and logic verifier inputs.
pub(crate) fn get_logic_verifiers(&self) -> Result<Vec<LogicVerifier>, ArmError> {
let mut logic_verifiers = Vec::new();

let compliance_intances = self
let compliance_instances: Vec<_> = self
.compliance_units
.iter()
.map(|unit| unit.get_instance())
.collect::<Result<Vec<ComplianceInstance>, ArmError>>()?;
.map(|unit| &unit.instance)
.collect();

// Construct the action tree
let tags: Vec<Digest> = compliance_intances
let tags: Vec<Digest> = compliance_instances
.iter()
.flat_map(|instance| vec![instance.consumed_nullifier, instance.created_commitment])
.collect();
let logics = compliance_intances
let logics = compliance_instances
.iter()
.flat_map(|instance| vec![instance.consumed_logic_ref, instance.created_logic_ref])
.collect::<Vec<_>>();
Expand Down Expand Up @@ -109,28 +119,17 @@ impl Action {

Ok(())
}
}

#[cfg(feature = "k256")]
impl Action {
/// This function computes the delta of the action by summing up the deltas
/// of each compliance unit.
pub fn delta(&self) -> Result<ProjectivePoint, ArmError> {
pub fn delta(&self) -> Result<k256::ProjectivePoint, ArmError> {
self.compliance_units
.iter()
.try_fold(ProjectivePoint::IDENTITY, |acc, unit| {
.try_fold(k256::ProjectivePoint::IDENTITY, |acc, unit| {
Ok(acc + unit.delta()?)
})
}

/// Constructs the delta message by concatenating the delta messages
/// of each compliance unit.
pub fn get_delta_msg(&self) -> Result<Vec<u8>, ArmError> {
let mut msg = Vec::new();
for unit in &self.compliance_units {
if let Ok(instance) = unit.get_instance() {
msg.extend_from_slice(&instance.delta_msg());
} else {
return Err(ArmError::InvalidComplianceInstance);
}
}
Ok(msg)
}
}
12 changes: 6 additions & 6 deletions arm/src/action_tree.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Merkle tree implementation for the action tree.

use crate::Digest;
use crate::{
error::ArmError,
merkle_path::{MerklePath, PADDING_LEAF},
merkle_path::{padding_leaf, MerklePath},
utils::hash_two,
};
use risc0_zkvm::sha::Digest;

/// A Merkle tree structure.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -37,7 +37,7 @@ impl MerkleTree {
.checked_next_power_of_two()
.ok_or(ArmError::TreeTooLarge)?;
let mut cur_layer = self.leaves.clone();
cur_layer.resize(len, *PADDING_LEAF);
cur_layer.resize(len, padding_leaf());
while cur_layer.len() > 1 {
cur_layer = cur_layer
.chunks(2)
Expand Down Expand Up @@ -67,7 +67,7 @@ impl MerkleTree {
return Err(ArmError::EmptyTree);
}

if *cur_leave == *PADDING_LEAF {
if *cur_leave == padding_leaf() {
return Err(ArmError::InvalidLeaf);
}

Expand All @@ -77,7 +77,7 @@ impl MerkleTree {
.checked_next_power_of_two()
.ok_or(ArmError::TreeTooLarge)?;
let mut cur_layer = self.leaves.clone();
cur_layer.resize(len, *PADDING_LEAF);
cur_layer.resize(len, padding_leaf());
if let Some(position) = cur_layer.iter().position(|v| v == cur_leave) {
let mut merkle_path = Vec::new();
fn build_merkle_path_inner(
Expand All @@ -87,7 +87,7 @@ impl MerkleTree {
) {
if cur_layer.len() > 1 {
let sibling = {
let is_sibling_left = position % 2 != 0;
let is_sibling_left = !position.is_multiple_of(2);
let sibling_value = if is_sibling_left {
cur_layer[position - 1]
} else {
Expand Down
124 changes: 69 additions & 55 deletions arm/src/compliance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,28 @@
/// Size hard-coded to two resources per unit
const COMPLIANCE_INSTANCE_SIZE: usize = 56;

use crate::{
error::ArmError,
merkle_path::MerklePath,
nullifier_key::NullifierKey,
resource::Resource,
utils::{bytes_to_words, words_to_bytes},
};
use hex::FromHex;
use crate::error::ArmError;
use crate::utils::bytes_to_words;
use crate::{constants::EMPTY_HASH_BYTES, Digest};
use serde_with::serde_as;

#[cfg(feature = "zkvm")]
use crate::utils::words_to_bytes;
#[cfg(all(feature = "zkvm", feature = "k256"))]
use crate::{merkle_path::MerklePath, nullifier_key::NullifierKey, resource::Resource};
#[cfg(feature = "k256")]
use k256::{elliptic_curve::sec1::FromEncodedPoint, EncodedPoint, ProjectivePoint};
#[cfg(all(feature = "zkvm", feature = "k256"))]
use k256::{
elliptic_curve::{
sec1::{FromEncodedPoint, ToEncodedPoint},
Field, PrimeField,
},
EncodedPoint, ProjectivePoint, Scalar,
elliptic_curve::{sec1::ToEncodedPoint, Field, PrimeField},
Scalar,
};
use lazy_static::lazy_static;
#[cfg(all(feature = "zkvm", feature = "k256"))]
use rand::rngs::OsRng;
use risc0_zkvm::Digest;
use serde_with::serde_as;

lazy_static! {
/// The initial root of the empty commitment tree is the hash of an empty string.
pub static ref INITIAL_ROOT: Digest =
Digest::from_hex("cc1d2f838445db7aec431df9ee8a871f40e7aa5e064fc056633ef8c60fab7b06")
.unwrap();
/// Returns the initial root of the empty commitment tree.
pub fn initial_root() -> Digest {
Digest::try_from(EMPTY_HASH_BYTES.as_slice()).unwrap()
}

/// The compliance instance contains all public inputs to the compliance proof.
Expand Down Expand Up @@ -59,7 +56,54 @@ pub struct ComplianceInstanceWords {
pub u32_words: [u32; COMPLIANCE_INSTANCE_SIZE],
}

impl ComplianceInstance {
/// Retrieves the delta message used for signing.
pub fn delta_msg(&self) -> Vec<u8> {
let mut msg = Vec::new();
msg.extend_from_slice(self.consumed_nullifier.as_bytes());
msg.extend_from_slice(self.created_commitment.as_bytes());
msg
}
}

#[cfg(feature = "k256")]
impl ComplianceInstance {
/// Converts the delta commitment from affine coordinates to a ProjectivePoint.
pub fn delta_projective(&self) -> Result<ProjectivePoint, ArmError> {
use crate::utils::words_to_bytes;
let encoded_point = EncodedPoint::from_affine_coordinates(
words_to_bytes(&self.delta_x).into(),
words_to_bytes(&self.delta_y).into(),
false,
);
ProjectivePoint::from_encoded_point(&encoded_point)
.into_option()
.ok_or(ArmError::InvalidDelta)
}
}

#[cfg(feature = "zkvm")]
impl ComplianceInstance {
/// Serializes this instance to journal bytes (risc0 serde format).
pub fn to_journal(&self) -> Result<Vec<u8>, ArmError> {
let words =
risc0_zkvm::serde::to_vec(self).map_err(|_| ArmError::InstanceSerializationFailed)?;
Ok(words_to_bytes(&words).to_vec())
}
}

impl ComplianceInstanceWords {
/// Creates a ComplianceInstanceWords from a byte slice.
pub fn from_bytes(instance_bytes: &[u8]) -> Result<Self, ArmError> {
let u32_words: [u32; COMPLIANCE_INSTANCE_SIZE] = bytes_to_words(instance_bytes)
.try_into()
.map_err(|_| ArmError::InstanceSerializationFailed)?;
Ok(ComplianceInstanceWords { u32_words })
}
}

/// The compliance witness contains all private inputs to the compliance proof.
#[cfg(all(feature = "zkvm", feature = "k256"))]
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct ComplianceWitness {
/// The consumed resource
Expand All @@ -79,6 +123,7 @@ pub struct ComplianceWitness {
// pub output_resource_logic_cm_r: [u8; DATA_BYTES],
}

#[cfg(all(feature = "zkvm", feature = "k256"))]
impl ComplianceWitness {
/// Creates a new compliance witness from the given resources and latest
/// root when consuming an ephemeral resource.
Expand Down Expand Up @@ -112,7 +157,7 @@ impl ComplianceWitness {
merkle_path,
rcv: Scalar::random(&mut OsRng).to_bytes().to_vec(),
nf_key,
ephemeral_root: *INITIAL_ROOT,
ephemeral_root: initial_root(),
}
}

Expand Down Expand Up @@ -211,6 +256,7 @@ impl ComplianceWitness {
}
}

#[cfg(all(feature = "zkvm", feature = "k256"))]
impl Default for ComplianceWitness {
fn default() -> Self {
let nf_key = NullifierKey::default();
Expand Down Expand Up @@ -246,42 +292,10 @@ impl Default for ComplianceWitness {
ComplianceWitness {
consumed_resource,
created_resource,
ephemeral_root: *INITIAL_ROOT,
ephemeral_root: initial_root(),
merkle_path,
rcv,
nf_key,
}
}
}

impl ComplianceInstance {
/// Converts the delta commitment from affine coordinates to a ProjectivePoint.
pub fn delta_projective(&self) -> Result<ProjectivePoint, ArmError> {
let encoded_point = EncodedPoint::from_affine_coordinates(
words_to_bytes(&self.delta_x).into(),
words_to_bytes(&self.delta_y).into(),
false,
);
ProjectivePoint::from_encoded_point(&encoded_point)
.into_option()
.ok_or(ArmError::InvalidDelta)
}

/// Retrieves the delta message used for signing.
pub fn delta_msg(&self) -> Vec<u8> {
let mut msg = Vec::new();
msg.extend_from_slice(self.consumed_nullifier.as_bytes());
msg.extend_from_slice(self.created_commitment.as_bytes());
msg
}
}

impl ComplianceInstanceWords {
/// Creates a ComplianceInstanceWords from a byte slice.
pub fn from_bytes(instance_bytes: &[u8]) -> Result<Self, ArmError> {
let u32_words: [u32; COMPLIANCE_INSTANCE_SIZE] = bytes_to_words(instance_bytes)
.try_into()
.map_err(|_| ArmError::InstanceSerializationFailed)?;
Ok(ComplianceInstanceWords { u32_words })
}
}
Loading