diff --git a/arm/Cargo.toml b/arm/Cargo.toml index 65591ef1..3fd5467e 100644 --- a/arm/Cargo.toml +++ b/arm/Cargo.toml @@ -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", @@ -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 = [] diff --git a/arm/src/action.rs b/arm/src/action.rs index fd025263..6fced995 100644 --- a/arm/src/action.rs +++ b/arm/src/action.rs @@ -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 { @@ -20,6 +17,29 @@ pub struct Action { pub logic_verifier_inputs: Vec, } +impl Action { + /// Returns a reference to the compliance units. + pub fn get_compliance_units(&self) -> &Vec { + &self.compliance_units + } + + /// Returns a reference to the logic verifier inputs. + pub fn get_logic_verifier_inputs(&self) -> &Vec { + &self.logic_verifier_inputs + } + + /// Constructs the delta message by concatenating the delta messages + /// of each compliance unit. + pub fn get_delta_msg(&self) -> Vec { + 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( @@ -36,33 +56,23 @@ impl Action { }) } - /// Returns a reference to the compliance units. - pub fn get_compliance_units(&self) -> &Vec { - &self.compliance_units - } - - /// Returns a reference to the logic verifier inputs. - pub fn get_logic_verifier_inputs(&self) -> &Vec { - &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, 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::, ArmError>>()?; + .map(|unit| &unit.instance) + .collect(); // Construct the action tree - let tags: Vec = compliance_intances + let tags: Vec = 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::>(); @@ -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 { + pub fn delta(&self) -> Result { 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, 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) - } } diff --git a/arm/src/action_tree.rs b/arm/src/action_tree.rs index 6e0786f0..c6617262 100644 --- a/arm/src/action_tree.rs +++ b/arm/src/action_tree.rs @@ -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)] @@ -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) @@ -67,7 +67,7 @@ impl MerkleTree { return Err(ArmError::EmptyTree); } - if *cur_leave == *PADDING_LEAF { + if *cur_leave == padding_leaf() { return Err(ArmError::InvalidLeaf); } @@ -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( @@ -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 { diff --git a/arm/src/compliance.rs b/arm/src/compliance.rs index 3cbafe8f..0b70b6a2 100644 --- a/arm/src/compliance.rs +++ b/arm/src/compliance.rs @@ -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. @@ -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 { + 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 { + 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, 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 { + 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 @@ -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. @@ -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(), } } @@ -211,6 +256,7 @@ impl ComplianceWitness { } } +#[cfg(all(feature = "zkvm", feature = "k256"))] impl Default for ComplianceWitness { fn default() -> Self { let nf_key = NullifierKey::default(); @@ -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 { - 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 { - 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 { - let u32_words: [u32; COMPLIANCE_INSTANCE_SIZE] = bytes_to_words(instance_bytes) - .try_into() - .map_err(|_| ArmError::InstanceSerializationFailed)?; - Ok(ComplianceInstanceWords { u32_words }) - } -} diff --git a/arm/src/compliance_unit.rs b/arm/src/compliance_unit.rs index 3bf64007..706c8e77 100644 --- a/arm/src/compliance_unit.rs +++ b/arm/src/compliance_unit.rs @@ -1,20 +1,18 @@ //! Compliance unit module containing the compliance proof and instance. -use crate::{ - compliance::ComplianceInstance, - constants::COMPLIANCE_VK, - error::ArmError, - proving_system::{journal_to_instance, verify as verify_proof}, -}; -use k256::ProjectivePoint; -use risc0_zkvm::InnerReceipt; +use crate::compliance::ComplianceInstance; use serde::{Deserialize, Serialize}; +#[cfg(any(feature = "zkvm", feature = "k256"))] +use crate::error::ArmError; +#[cfg(feature = "zkvm")] +use crate::{constants::COMPLIANCE_VK, proving_system::verify as verify_proof}; + #[cfg(feature = "prove")] use crate::{ compliance::ComplianceWitness, constants::COMPLIANCE_PK, - proving_system::{prove, ProofType}, + proving_system::{journal_to_instance, prove, ProofType}, }; /// A compliance unit consists of a compliance proof and its corresponding instance. @@ -23,8 +21,8 @@ use crate::{ pub struct ComplianceUnit { /// The compliance proof (optional, would be absent when aggregation is enabled). pub proof: Option>, - /// The serialized compliance instance. - pub instance: Vec, + /// The structured compliance instance. + pub instance: ComplianceInstance, } impl ComplianceUnit { @@ -33,7 +31,8 @@ impl ComplianceUnit { /// them as parameters. Instance is generated by proving. #[cfg(feature = "prove")] pub fn create(witness: &ComplianceWitness, proof_type: ProofType) -> Result { - let (proof, instance) = prove(COMPLIANCE_PK, witness, proof_type)?; + let (proof, journal) = prove(COMPLIANCE_PK, witness, proof_type)?; + let instance: ComplianceInstance = journal_to_instance(&journal)?; Ok(ComplianceUnit { proof: Some(proof), instance, @@ -41,9 +40,11 @@ impl ComplianceUnit { } /// Verifies the compliance proof against the instance using the constant verifying key. + #[cfg(feature = "zkvm")] pub fn verify(&self) -> Result<(), ArmError> { if let Some(proof) = &self.proof { - verify_proof(&COMPLIANCE_VK, &self.instance, proof) + let journal = self.instance.to_journal()?; + verify_proof(&COMPLIANCE_VK, &journal, proof) } else { Err(ArmError::ProofVerificationFailed( "Missing compliance proof".into(), @@ -52,18 +53,15 @@ impl ComplianceUnit { } /// Obtains the delta from the compliance instance. - pub fn delta(&self) -> Result { - self.get_instance()?.delta_projective() - } - - /// Retrieves the compliance instance from the serialized instance data. - pub fn get_instance(&self) -> Result { - journal_to_instance(&self.instance) + #[cfg(feature = "k256")] + pub fn delta(&self) -> Result { + self.instance.delta_projective() } /// Retrieves the inner receipt from the compliance proof. - pub fn get_inner_receipt(&self) -> Result { - let inner: InnerReceipt = bincode::deserialize( + #[cfg(feature = "zkvm")] + pub fn get_inner_receipt(&self) -> Result { + let inner: risc0_zkvm::InnerReceipt = bincode::deserialize( self.proof .as_ref() .ok_or(ArmError::MissingField("Missing compliance proof"))?, diff --git a/arm/src/constants.rs b/arm/src/constants.rs index 0073fe8e..01641159 100644 --- a/arm/src/constants.rs +++ b/arm/src/constants.rs @@ -1,31 +1,48 @@ //! Constants for compliance and padding logic proving and verification keys. -use hex::FromHex; -use lazy_static::lazy_static; -use risc0_zkvm::Digest; +use hex_literal::hex; + +/// Compliance verification key bytes. +pub const COMPLIANCE_VK_BYTES: [u8; 32] = + hex!("919e13001cd3319be5a5a7cb189203be083674acb3fff23d05aae9c3ed86314d"); + +/// Padding logic verification key bytes. +pub const PADDING_LOGIC_VK_BYTES: [u8; 32] = + hex!("21fcc2fc2c07f9753405d3070f2488c67389f7d797b6f6e20a9f2529fe4a0bff"); -/// Compliance proving key / compliance guest ELF binary +/// Hash of the empty string (used for PADDING_LEAF and INITIAL_ROOT). +pub const EMPTY_HASH_BYTES: [u8; 32] = + hex!("cc1d2f838445db7aec431df9ee8a871f40e7aa5e064fc056633ef8c60fab7b06"); + +/// Compliance proving key / compliance guest ELF binary. +#[cfg(feature = "zkvm")] pub const COMPLIANCE_PK: &[u8] = include_bytes!("../elfs/compliance-guest.bin"); -/// Padding logic proving key / padding logic guest ELF binary + +/// Padding logic proving key / padding logic guest ELF binary. +#[cfg(feature = "zkvm")] pub const PADDING_LOGIC_PK: &[u8] = include_bytes!("../elfs/trivial-logic-guest.bin"); -/// Batch aggregation proving key / batch aggregation guest ELF binary + +/// Batch aggregation proving key / batch aggregation guest ELF binary. #[cfg(feature = "aggregation")] pub const BATCH_AGGREGATION_PK: &[u8] = include_bytes!("../elfs/batch-aggregation-guest.bin"); +#[cfg(feature = "zkvm")] +use lazy_static::lazy_static; + +#[cfg(feature = "zkvm")] lazy_static! { - /// compliance verification key / compliance image id - pub static ref COMPLIANCE_VK: Digest = - Digest::from_hex("919e13001cd3319be5a5a7cb189203be083674acb3fff23d05aae9c3ed86314d") - .unwrap(); - - /// padding logic verification key / compliance image id - pub static ref PADDING_LOGIC_VK: Digest = - Digest::from_hex("21fcc2fc2c07f9753405d3070f2488c67389f7d797b6f6e20a9f2529fe4a0bff") - .unwrap(); + /// Compliance verification key as a risc0 Digest. + pub static ref COMPLIANCE_VK: crate::Digest = + crate::Digest::try_from(COMPLIANCE_VK_BYTES.as_slice()).unwrap(); + + /// Padding logic verification key as a risc0 Digest. + pub static ref PADDING_LOGIC_VK: crate::Digest = + crate::Digest::try_from(PADDING_LOGIC_VK_BYTES.as_slice()).unwrap(); } #[cfg(feature = "aggregation")] lazy_static! { /// Batch aggregation verification key / Batch aggregation image id. - pub static ref BATCH_AGGREGATION_VK: Digest = Digest::from_hex("5ca0cbd4d5c267f42e0883b1ae7a28689d792230d9c4c61ca4f5df56aaf5fede").unwrap(); + pub static ref BATCH_AGGREGATION_VK: crate::Digest = + crate::Digest::try_from(hex!("5ca0cbd4d5c267f42e0883b1ae7a28689d792230d9c4c61ca4f5df56aaf5fede").as_slice()).unwrap(); } diff --git a/arm/src/delta_types.rs b/arm/src/delta_types.rs new file mode 100644 index 00000000..698e8afb --- /dev/null +++ b/arm/src/delta_types.rs @@ -0,0 +1,55 @@ +//! Feature-conditional delta witness and proof types. + +#[cfg(feature = "k256")] +pub use crate::delta_proof::{DeltaProof, DeltaWitness}; + +#[cfg(not(feature = "k256"))] +pub use self::placeholder::{DeltaProof, DeltaWitness}; + +/// Opaque placeholder types when k256 is unavailable. +#[cfg(not(feature = "k256"))] +mod placeholder { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Opaque 65-byte delta proof (signature + recovery ID). + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct DeltaProof(pub [u8; 65]); + + /// Opaque 32-byte delta witness (signing key). + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct DeltaWitness(pub [u8; 32]); + + impl DeltaWitness { + /// Panics - composition requires k256. + pub fn compose(&self, _other: &DeltaWitness) -> DeltaWitness { + panic!("DeltaWitness::compose requires k256 feature") + } + } + + impl Serialize for DeltaProof { + fn serialize(&self, s: S) -> Result { + s.serialize_bytes(&self.0) + } + } + + impl<'de> Deserialize<'de> for DeltaProof { + fn deserialize>(d: D) -> Result { + let bytes: Vec = Vec::deserialize(d)?; + bytes.try_into().map(DeltaProof).map_err(|v: Vec| { + serde::de::Error::custom(format!("expected 65 bytes, got {}", v.len())) + }) + } + } + + impl Serialize for DeltaWitness { + fn serialize(&self, s: S) -> Result { + s.serialize_bytes(&self.0) + } + } + + impl<'de> Deserialize<'de> for DeltaWitness { + fn deserialize>(d: D) -> Result { + <[u8; 32]>::deserialize(d).map(DeltaWitness) + } + } +} diff --git a/arm/src/digest.rs b/arm/src/digest.rs new file mode 100644 index 00000000..a95e537d --- /dev/null +++ b/arm/src/digest.rs @@ -0,0 +1,109 @@ +//! Standalone Digest type, wire-compatible with `risc0_zkvm::sha::Digest`. + +use serde::{Deserialize, Serialize}; + +/// Number of bytes in a digest. +pub const DIGEST_BYTES: usize = 32; +/// Number of u32 words in a digest. +pub const DIGEST_WORDS: usize = 8; + +/// A SHA-256 digest represented as 8 little-endian u32 words. +/// +/// This type is wire-compatible with `risc0_zkvm::sha::Digest`: +/// both store `[u32; 8]` and expose bytes via `bytemuck::cast_slice`. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Digest([u32; DIGEST_WORDS]); + +impl Digest { + /// Creates a new Digest from u32 words. + pub const fn new(words: [u32; DIGEST_WORDS]) -> Self { + Digest(words) + } + + /// Returns the digest as a slice of u32 words. + pub fn as_words(&self) -> &[u32; DIGEST_WORDS] { + &self.0 + } + + /// Returns the digest as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + bytemuck::cast_slice(&self.0) + } +} + +impl AsRef<[u8]> for Digest { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl TryFrom<&[u8]> for Digest { + type Error = &'static str; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != DIGEST_BYTES { + return Err("Invalid byte length for Digest"); + } + Ok(Digest(*bytemuck::from_bytes(bytes))) + } +} + +impl hex::FromHex for Digest { + type Error = hex::FromHexError; + + fn from_hex>(hex: T) -> Result { + let bytes = hex::decode(hex)?; + if bytes.len() != DIGEST_BYTES { + return Err(hex::FromHexError::InvalidStringLength); + } + Ok(Digest(*bytemuck::from_bytes(&bytes))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_digest_roundtrip_bytes() { + let hex_str = "cc1d2f838445db7aec431df9ee8a871f40e7aa5e064fc056633ef8c60fab7b06"; + let digest = ::from_hex(hex_str).unwrap(); + let bytes = digest.as_bytes(); + let digest2 = Digest::try_from(bytes).unwrap(); + assert_eq!(digest, digest2); + } + + #[test] + fn test_digest_from_hex_nonzero() { + let hex_str = "cc1d2f838445db7aec431df9ee8a871f40e7aa5e064fc056633ef8c60fab7b06"; + let digest = ::from_hex(hex_str).unwrap(); + assert_ne!(digest, Digest::default()); + } + + #[test] + fn test_digest_default_is_zero() { + let digest = Digest::default(); + assert_eq!(digest.as_words(), &[0u32; DIGEST_WORDS]); + assert_eq!(digest.as_bytes(), &[0u8; DIGEST_BYTES]); + } + + #[test] + fn test_digest_words_bytes_consistency() { + let words = [ + 0x01020304u32, + 0x05060708, + 0x090a0b0c, + 0x0d0e0f10, + 0x11121314, + 0x15161718, + 0x191a1b1c, + 0x1d1e1f20, + ]; + let digest = Digest::new(words); + assert_eq!(digest.as_words(), &words); + let bytes = digest.as_bytes(); + assert_eq!(bytes.len(), DIGEST_BYTES); + let roundtrip = Digest::try_from(bytes).unwrap(); + assert_eq!(roundtrip, digest); + } +} diff --git a/arm/src/error.rs b/arm/src/error.rs index 9c799267..e0a90154 100644 --- a/arm/src/error.rs +++ b/arm/src/error.rs @@ -50,8 +50,6 @@ pub enum ArmError { ProveFailed(String), #[error("Proof verification failed with return code {0}")] ProofVerificationFailed(String), - #[error("Invalid compliance instance")] - InvalidComplianceInstance, #[error("Delta proof generation failed")] DeltaProofGenerationFailed, #[error("Invalid Random Commitment Value")] diff --git a/arm/src/lib.rs b/arm/src/lib.rs index 5cdc9694..6f677df1 100644 --- a/arm/src/lib.rs +++ b/arm/src/lib.rs @@ -2,29 +2,41 @@ #![deny(missing_docs)] +#[cfg(not(feature = "zkvm"))] +mod digest; +#[cfg(not(feature = "zkvm"))] +pub use digest::{Digest, DIGEST_BYTES, DIGEST_WORDS}; + +#[cfg(feature = "zkvm")] +pub use risc0_zkvm::sha::{DIGEST_BYTES, DIGEST_WORDS}; +#[cfg(feature = "zkvm")] +pub use risc0_zkvm::Digest; + #[cfg(feature = "transaction")] pub mod action; +#[cfg(feature = "zkvm")] pub mod action_tree; #[cfg(any(feature = "compliance_circuit", feature = "aggregation_circuit"))] pub mod compliance; #[cfg(feature = "transaction")] pub mod compliance_unit; -#[cfg(feature = "transaction")] pub mod constants; -#[cfg(feature = "transaction")] +#[cfg(all(feature = "transaction", feature = "k256"))] pub mod delta_proof; +#[cfg(feature = "transaction")] +pub mod delta_types; pub mod error; pub mod logic_instance; -#[cfg(feature = "transaction")] +#[cfg(all(feature = "transaction", feature = "zkvm", feature = "k256"))] pub mod logic_proof; pub mod merkle_path; pub mod nullifier_key; -#[cfg(feature = "transaction")] +#[cfg(all(feature = "transaction", feature = "zkvm"))] pub mod proving_system; +#[cfg(all(feature = "zkvm", feature = "k256"))] pub mod resource; +#[cfg(all(feature = "zkvm", feature = "k256"))] pub mod resource_logic; #[cfg(feature = "transaction")] pub mod transaction; pub mod utils; - -pub use risc0_zkvm::Digest; diff --git a/arm/src/logic_instance.rs b/arm/src/logic_instance.rs index 100f3d02..51f561d3 100644 --- a/arm/src/logic_instance.rs +++ b/arm/src/logic_instance.rs @@ -1,6 +1,6 @@ //! Logic instance for ARM resource logic proofs. -use risc0_zkvm::Digest; +use crate::Digest; use serde::{Deserialize, Serialize}; /// Represents a logic instance with its associated data. @@ -69,3 +69,52 @@ impl AppData { self.application_payload.push(blob); } } + +#[cfg(feature = "zkvm")] +impl LogicInstance { + /// Serializes this instance to journal bytes (risc0 serde format). + pub fn to_journal(&self) -> Result, crate::error::ArmError> { + let words = risc0_zkvm::serde::to_vec(self) + .map_err(|_| crate::error::ArmError::InstanceSerializationFailed)?; + Ok(crate::utils::words_to_bytes(&words).to_vec()) + } +} + +/// Inputs required to create a logic verifier. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +pub struct LogicVerifierInputs { + /// The tag (either commitment or nullifier) for the logic instance. + pub tag: Digest, + /// The verifying key for the logic proof. + pub verifying_key: Digest, + /// The application data associated with the logic instance. + pub app_data: AppData, + /// The logic proof (optional, would be absent when aggregation is enabled). + pub proof: Option>, +} + +impl LogicVerifierInputs { + /// Converts the LogicVerifierInputs into a LogicInstance. + pub fn to_instance(&self, is_consumed: bool, root: Digest) -> LogicInstance { + LogicInstance { + tag: self.tag, + is_consumed, + root, + app_data: self.app_data.clone(), + } + } +} + +#[cfg(feature = "zkvm")] +impl LogicVerifierInputs { + /// Retrieves the inner receipt from the logic proof. + pub fn get_inner_receipt(&self) -> Result { + let inner: risc0_zkvm::InnerReceipt = bincode::deserialize( + self.proof + .as_ref() + .ok_or(crate::error::ArmError::MissingField("Missing logic proof"))?, + ) + .map_err(|_| crate::error::ArmError::InnerReceiptDeserializationError)?; + Ok(inner) + } +} diff --git a/arm/src/logic_proof.rs b/arm/src/logic_proof.rs index 298ff628..ee732a74 100644 --- a/arm/src/logic_proof.rs +++ b/arm/src/logic_proof.rs @@ -1,18 +1,18 @@ //! Logic proof structures and traits for proving and verifying logic statements. +pub use crate::logic_instance::LogicVerifierInputs; use crate::{ constants::{PADDING_LOGIC_PK, PADDING_LOGIC_VK}, error::ArmError, - logic_instance::{AppData, LogicInstance}, + logic_instance::LogicInstance, nullifier_key::{NullifierKey, NullifierKeyCommitment}, proving_system::{journal_to_instance, verify as verify_proof}, resource::Resource, resource_logic::TrivialLogicWitness, - utils::words_to_bytes, }; use rand::rngs::OsRng; use rand::Rng; -use risc0_zkvm::{serde::to_vec, sha::Digest, InnerReceipt}; +use risc0_zkvm::sha::Digest; use serde::{Deserialize, Serialize}; #[cfg(feature = "prove")] @@ -60,19 +60,6 @@ pub struct LogicVerifier { pub verifying_key: Digest, } -/// Inputs required to create a logic verifier. -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] -pub struct LogicVerifierInputs { - /// The tag (either commitment or nullifier) for the logic instance. - pub tag: Digest, - /// The verifying key for the logic proof. - pub verifying_key: Digest, - /// The application data associated with the logic instance. - pub app_data: AppData, - /// The logic proof (optional, would be absent when aggregation is enabled). - pub proof: Option>, -} - impl LogicVerifier { /// Verifies the logic proof against the instance using the provided verifying key. pub fn verify(&self) -> Result<(), ArmError> { @@ -99,35 +86,13 @@ impl LogicVerifierInputs { is_consumed: bool, root: Digest, ) -> Result { - let instance_words = to_vec(&self.to_instance(is_consumed, root)) - .map_err(|_| ArmError::InstanceSerializationFailed)?; + let instance = self.to_instance(is_consumed, root); Ok(LogicVerifier { proof: self.proof, - instance: words_to_bytes(&instance_words).to_vec(), + instance: instance.to_journal()?, verifying_key: self.verifying_key, }) } - - /// Converts the LogicVerifierInputs into a LogicInstance. - fn to_instance(&self, is_consumed: bool, root: Digest) -> LogicInstance { - LogicInstance { - tag: self.tag, - is_consumed, - root, - app_data: self.app_data.clone(), - } - } - - /// Retrieves the inner receipt from the logic proof. - pub fn get_inner_receipt(&self) -> Result { - let inner: InnerReceipt = bincode::deserialize( - self.proof - .as_ref() - .ok_or(ArmError::MissingField("Missing logic proof"))?, - ) - .map_err(|_| ArmError::InnerReceiptDeserializationError)?; - Ok(inner) - } } impl TryFrom for LogicVerifierInputs { diff --git a/arm/src/merkle_path.rs b/arm/src/merkle_path.rs index b7d4388a..d9ebf370 100644 --- a/arm/src/merkle_path.rs +++ b/arm/src/merkle_path.rs @@ -1,17 +1,12 @@ //! A Merkle path from a leaf to a root in a commitment/action tree. -use crate::utils::hash_two; -use hex::FromHex; -use lazy_static::lazy_static; -use risc0_zkvm::sha::Digest; +use crate::constants::EMPTY_HASH_BYTES; +use crate::Digest; use serde::{Deserialize, Serialize}; -lazy_static! { - /// A constant padding leaf used in Merkle trees. - /// This is the hash of an empty string. - pub static ref PADDING_LEAF: Digest = - Digest::from_hex("cc1d2f838445db7aec431df9ee8a871f40e7aa5e064fc056633ef8c60fab7b06") - .unwrap(); +/// Returns the padding leaf digest (hash of the empty string). +pub fn padding_leaf() -> Digest { + Digest::try_from(EMPTY_HASH_BYTES.as_slice()).unwrap() } /// A path from a position in a particular commitment tree to the root of that tree. @@ -25,7 +20,9 @@ impl MerklePath { } /// Returns the root of the tree corresponding to this path applied to `leaf`. + #[cfg(feature = "zkvm")] pub fn root(&self, leaf: &Digest) -> Digest { + use crate::utils::hash_two; self.0.iter().fold( *leaf, |root, (p, leaf_is_on_right)| match leaf_is_on_right { diff --git a/arm/src/nullifier_key.rs b/arm/src/nullifier_key.rs index 8da36b73..ed4ecac7 100644 --- a/arm/src/nullifier_key.rs +++ b/arm/src/nullifier_key.rs @@ -1,10 +1,14 @@ //! Nullifier key and its commitment use crate::error::ArmError; -use rand::{rngs::OsRng, Rng}; -use risc0_zkvm::sha::{Digest, Impl, Sha256, DIGEST_BYTES}; +use crate::{Digest, DIGEST_BYTES}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "zkvm")] +use rand::{rngs::OsRng, Rng}; +#[cfg(feature = "zkvm")] +use risc0_zkvm::sha::{Impl, Sha256}; + /// Nullifier key #[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct NullifierKey([u8; DIGEST_BYTES]); @@ -16,6 +20,7 @@ impl NullifierKey { } /// Compute the commitment to the nullifier key + #[cfg(feature = "zkvm")] pub fn commit(&self) -> NullifierKeyCommitment { NullifierKeyCommitment(*Impl::hash_bytes(self.inner())) } @@ -31,6 +36,7 @@ impl NullifierKey { } /// Generate a random nullifier key and its commitment + #[cfg(feature = "zkvm")] pub fn random_pair() -> (NullifierKey, NullifierKeyCommitment) { let rng_bytes: [u8; DIGEST_BYTES] = OsRng.gen(); let nf_key = NullifierKey::from_bytes(rng_bytes); @@ -68,6 +74,7 @@ impl NullifierKeyCommitment { } } +#[cfg(feature = "zkvm")] impl Default for NullifierKeyCommitment { fn default() -> Self { NullifierKey::default().commit() diff --git a/arm/src/transaction.rs b/arm/src/transaction.rs index 3d152399..21142408 100644 --- a/arm/src/transaction.rs +++ b/arm/src/transaction.rs @@ -1,5 +1,14 @@ //! Transaction structure and associated methods. +#[cfg(feature = "k256")] +use crate::delta_proof::DeltaInstance; +use crate::delta_types::{DeltaProof, DeltaWitness}; +use crate::{action::Action, compliance_unit::ComplianceUnit, error::ArmError}; +use serde::{Deserialize, Serialize}; + +#[cfg(all(feature = "zkvm", feature = "k256"))] +use crate::logic_proof::LogicVerifier; + #[cfg(feature = "aggregation")] use crate::{ compliance::ComplianceInstanceWords, @@ -9,16 +18,6 @@ use crate::{ }; #[cfg(feature = "aggregation")] use risc0_zkvm::{default_prover, ExecutorEnv, ProverOpts, Receipt, VerifierContext}; -use risc0_zkvm::{Digest, InnerReceipt}; - -use crate::{ - action::Action, - compliance_unit::ComplianceUnit, - delta_proof::{DeltaInstance, DeltaProof, DeltaWitness}, - error::ArmError, - logic_proof::LogicVerifier, -}; -use serde::{Deserialize, Serialize}; /// Represents a transaction consisting of actions, delta proof, expected balance, /// and optional aggregation proof. @@ -35,6 +34,10 @@ pub struct Transaction { } /// Represents either a delta witness for proving or a delta proof for verification. +/// +/// The inner types are feature-conditional: with `k256` enabled, they provide +/// full cryptographic operations. Without `k256`, they are opaque byte containers +/// that maintain wire compatibility. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum Delta { /// The delta witness used for proving the delta proof. @@ -45,8 +48,6 @@ pub enum Delta { impl Transaction { /// Create a new transaction with the given actions and delta. - /// Delta proof is a deterministic process, no proving key is needed. - /// Delta instance can be constructed from the actions. pub fn create(actions: Vec, delta: Delta) -> Self { Transaction { actions, @@ -56,11 +57,62 @@ impl Transaction { } } + /// Constructs the delta message by concatenating the delta messages + /// of each action. + pub fn get_delta_msg(&self) -> Vec { + let mut msg = Vec::new(); + for action in &self.actions { + msg.extend(action.get_delta_msg()); + } + msg + } + + /// Inner check for nullifier duplication across all compliance units + pub fn nf_duplication_check(&self) -> Result<(), ArmError> { + let mut seen_nullifiers = std::collections::HashSet::new(); + for action in &self.actions { + for cu in action.get_compliance_units() { + if !seen_nullifiers.insert(cu.instance.consumed_nullifier) { + return Err(ArmError::NullifierDuplication); + } + } + } + Ok(()) + } + + /// Returns all compliance units in the transaction. + pub fn get_compliance_units(&self) -> Vec<&ComplianceUnit> { + self.actions + .iter() + .flat_map(|a| a.get_compliance_units().iter()) + .collect() + } + + /// Returns `true` if any compliance or resource logic proof is `None`. + pub fn base_proofs_are_empty(&self) -> bool { + for a in self.actions.iter() { + if a.get_compliance_units().iter().any(|cu| cu.proof.is_none()) { + return true; + } + if a.get_logic_verifier_inputs() + .iter() + .any(|lp| lp.proof.is_none()) + { + return true; + } + } + + false + } +} + +#[cfg(feature = "k256")] +impl Transaction { /// Generates the delta proof for the transaction if it contains a delta witness. pub fn generate_delta_proof(self) -> Result { match self.delta_proof { Delta::Witness(ref witness) => { - let msg = self.get_delta_msg()?; + let msg = self.get_delta_msg(); let proof = DeltaProof::prove(&msg, witness)?; let delta_proof = Delta::Proof(proof); Ok(Transaction { @@ -74,11 +126,34 @@ impl Transaction { } } + /// Returns the DeltaInstance constructed from the sum of all actions' deltas. + pub fn delta(&self) -> Result { + let mut points = Vec::with_capacity(self.actions.len()); + for action in &self.actions { + points.push(action.delta()?); + } + DeltaInstance::from_deltas(&points) + } + + /// Composes two transactions by concatenating their actions and combining their delta witnesses. + pub fn compose(tx1: Transaction, tx2: Transaction) -> Transaction { + let mut actions = tx1.actions; + actions.extend(tx2.actions); + let delta = match (&tx1.delta_proof, &tx2.delta_proof) { + (Delta::Witness(witness1), Delta::Witness(witness2)) => { + Delta::Witness(witness1.compose(witness2)) + } + _ => panic!("Cannot compose transactions with different delta types"), + }; + Transaction::create(actions, delta) + } + /// Verifies all the proofs and corresponding checks in the transaction. + #[cfg(feature = "zkvm")] pub fn verify(self) -> Result<(), ArmError> { match &self.delta_proof { Delta::Proof(ref proof) => { - let msg = self.get_delta_msg()?; + let msg = self.get_delta_msg(); let instance = self.delta()?; DeltaProof::verify(&msg, proof, instance)?; @@ -104,80 +179,21 @@ impl Transaction { Delta::Witness(_) => Err(ArmError::ExpectedDeltaProof), } } +} - /// Inner check for nullifier duplication across all compliance units - pub fn nf_duplication_check(&self) -> Result<(), ArmError> { - let mut seen_nullifiers = std::collections::HashSet::new(); - for action in &self.actions { - for cu in action.get_compliance_units() { - let instance = cu.get_instance()?; - if !seen_nullifiers.insert(instance.consumed_nullifier) { - return Err(ArmError::NullifierDuplication); - } - } - } - Ok(()) - } - - /// Returns the DeltaInstance constructed from the sum of all actions' deltas. - pub fn delta(&self) -> Result { - let mut points = Vec::with_capacity(self.actions.len()); - for action in &self.actions { - points.push(action.delta()?); - } - DeltaInstance::from_deltas(&points) - } - - /// Constructs the delta message by concatenating the delta messages - /// of each action. - pub fn get_delta_msg(&self) -> Result, ArmError> { - let mut msg = Vec::new(); - for action in &self.actions { - msg.extend(action.get_delta_msg()?); - } - Ok(msg) - } - - /// Composes two transactions by concatenating their actions and combining their delta witnesses. - pub fn compose(tx1: Transaction, tx2: Transaction) -> Transaction { - let mut actions = tx1.actions; - actions.extend(tx2.actions); - let delta = match (&tx1.delta_proof, &tx2.delta_proof) { - (Delta::Witness(witness1), Delta::Witness(witness2)) => { - Delta::Witness(witness1.compose(witness2)) - } - _ => panic!("Cannot compose transactions with different delta types"), - }; - Transaction::create(actions, delta) - } - - /// Returns `true` if any compliance or resource logic proof is `None`. - pub fn base_proofs_are_empty(&self) -> bool { - for a in self.actions.iter() { - if a.get_compliance_units().iter().any(|cu| cu.proof.is_none()) { - return true; - } - if a.get_logic_verifier_inputs() - .iter() - .any(|lp| lp.proof.is_none()) - { - return true; - } +#[cfg(feature = "zkvm")] +impl Transaction { + /// Returns all compliance instances as serialized journal bytes. + pub fn get_compliance_instances(&self) -> Result>, ArmError> { + let mut result = Vec::new(); + for cu in self.get_compliance_units() { + result.push(cu.instance.to_journal()?); } - - false - } - - /// Returns all compliance units in the transaction. - pub fn get_compliance_units(&self) -> Vec<&ComplianceUnit> { - self.actions - .iter() - .flat_map(|a| a.get_compliance_units().iter()) - .collect() + Ok(result) } /// Returns all compliance inner receipts in the transaction. - pub fn get_compliance_inner_receipts(&self) -> Result, ArmError> { + pub fn get_compliance_inner_receipts(&self) -> Result, ArmError> { let mut compliance_inner_receipts = Vec::new(); for cu in self.get_compliance_units() { let inner_receipt = cu.get_inner_receipt()?; @@ -187,7 +203,7 @@ impl Transaction { } /// Returns all logic inner receipts in the transaction. - pub fn get_logic_inner_receipts(&self) -> Result, ArmError> { + pub fn get_logic_inner_receipts(&self) -> Result, ArmError> { let mut logic_inner_receipts = Vec::new(); for action in self.actions.iter() { let logic_inputs = action.get_logic_verifier_inputs(); @@ -198,16 +214,10 @@ impl Transaction { } Ok(logic_inner_receipts) } +} - /// Returns all compliance instances in the transaction. - pub fn get_compliance_instances(&self) -> Vec> { - let mut result = Vec::new(); - for cu in self.get_compliance_units() { - result.push(cu.instance.clone()); - } - result - } - +#[cfg(all(feature = "zkvm", feature = "k256"))] +impl Transaction { /// Returns all logic verifiers in the transaction. pub fn get_logic_verifiers(&self) -> Result, ArmError> { let mut result = Vec::new(); @@ -219,7 +229,9 @@ impl Transaction { } /// Returns all logic verifying keys and instances in the transaction. - pub fn get_logic_vks_and_instances(&self) -> Result<(Vec, Vec>), ArmError> { + pub fn get_logic_vks_and_instances( + &self, + ) -> Result<(Vec, Vec>), ArmError> { let mut vks = Vec::new(); let mut instances = Vec::new(); for lp in self.get_logic_verifiers()? { @@ -247,7 +259,7 @@ impl Transaction { let compliance_inner_receipts = self.get_compliance_inner_receipts()?; let logic_inner_receipts = self.get_logic_inner_receipts()?; let compliance_instances_u32: Vec = self - .get_compliance_instances() + .get_compliance_instances()? .iter() .map(|instance_bytes| ComplianceInstanceWords::from_bytes(instance_bytes)) .collect::, ArmError>>()?; @@ -268,7 +280,7 @@ impl Transaction { } // Write instances and keys to guest input. - let compliance_key: Digest = *COMPLIANCE_VK; + let compliance_key: crate::Digest = *COMPLIANCE_VK; let env = env_builder .write(&compliance_instances_u32) .map_err(|_| ArmError::WriteWitnessFailed)? @@ -316,7 +328,7 @@ impl Transaction { if let Some(proof) = &self.aggregation_proof { let instance = self.construct_aggregation_instance()?; - let inner_receipt: InnerReceipt = bincode::deserialize(proof) + let inner_receipt: risc0_zkvm::InnerReceipt = bincode::deserialize(proof) .map_err(|_| ArmError::InnerReceiptDeserializationError)?; // Verify proof on the batch instance. @@ -335,7 +347,7 @@ impl Transaction { /// Constructs the aggregation instance by serializing all compliance and logic instances. pub fn construct_aggregation_instance(&self) -> Result, ArmError> { let compliance_instances_u32: Vec = self - .get_compliance_instances() + .get_compliance_instances()? .iter() .map(|instance_bytes| ComplianceInstanceWords::from_bytes(instance_bytes)) .collect::, ArmError>>()?; diff --git a/arm/src/utils.rs b/arm/src/utils.rs index ec1d8dc6..8b5b6eb7 100644 --- a/arm/src/utils.rs +++ b/arm/src/utils.rs @@ -1,7 +1,5 @@ //! Utility functions for byte and word conversions and hashing. -use risc0_zkvm::sha::{Digest, Impl, Sha256, DIGEST_WORDS}; - /// Converts a byte slice to a vector of u32 words. pub fn bytes_to_words(bytes: &[u8]) -> Vec { let mut words = Vec::new(); @@ -33,7 +31,9 @@ pub fn words_to_bytes(words: &[u32]) -> &[u8] { } /// Hashes two digests together using SHA-256. -pub fn hash_two(left: &Digest, right: &Digest) -> Digest { +#[cfg(feature = "zkvm")] +pub fn hash_two(left: &crate::Digest, right: &crate::Digest) -> crate::Digest { + use risc0_zkvm::sha::{Impl, Sha256, DIGEST_WORDS}; let mut words = Vec::with_capacity(2 * DIGEST_WORDS); words.extend_from_slice(left.as_words()); words.extend_from_slice(right.as_words()); @@ -41,7 +41,9 @@ pub fn hash_two(left: &Digest, right: &Digest) -> Digest { } /// Hashes arbitrary bytes using SHA-256. -pub fn hash_bytes(bytes: &[u8]) -> Digest { +#[cfg(feature = "zkvm")] +pub fn hash_bytes(bytes: &[u8]) -> crate::Digest { + use risc0_zkvm::sha::{Impl, Sha256}; *Impl::hash_bytes(bytes) } diff --git a/arm_circuits/compliance/Cargo.toml b/arm_circuits/compliance/Cargo.toml index cbfe24f0..312033a3 100644 --- a/arm_circuits/compliance/Cargo.toml +++ b/arm_circuits/compliance/Cargo.toml @@ -11,6 +11,8 @@ compliance-methods = { path = "methods" } risc0-zkvm = "3.0.3" anoma-rm-risc0 = { path = "../../arm", features = [ "compliance_circuit", + "zkvm", + "k256", ], default-features = false } [features] diff --git a/arm_circuits/compliance/methods/guest/Cargo.toml b/arm_circuits/compliance/methods/guest/Cargo.toml index 9277ca86..00f5e28a 100644 --- a/arm_circuits/compliance/methods/guest/Cargo.toml +++ b/arm_circuits/compliance/methods/guest/Cargo.toml @@ -13,6 +13,8 @@ risc0-zkvm = { version = "3.0.3", features = [ ], default-features = false } anoma-rm-risc0 = { path = "../../../../arm", features = [ "compliance_circuit", + "zkvm", + "k256", ], default-features = false } [patch.crates-io] diff --git a/arm_circuits/logic_test/methods/guest/Cargo.toml b/arm_circuits/logic_test/methods/guest/Cargo.toml index 82a78a4a..5a3e7857 100644 --- a/arm_circuits/logic_test/methods/guest/Cargo.toml +++ b/arm_circuits/logic_test/methods/guest/Cargo.toml @@ -8,7 +8,10 @@ edition = "2021" [dependencies] # If you want to try (experimental) std support, add `features = [ "std" ]` to risc0-zkvm risc0-zkvm = { version = "=3.0.3", features = ["std", "unstable"] } -anoma-rm-risc0 = { path = "../../../../arm", default-features = false } +anoma-rm-risc0 = { path = "../../../../arm", default-features = false, features = [ + "zkvm", + "k256", +] } anoma-rm-risc0-test-witness = { path = "../../../../arm_tests/arm_test_witness" } [patch.crates-io] diff --git a/arm_circuits/trivial_logic/Cargo.toml b/arm_circuits/trivial_logic/Cargo.toml index 01847571..e884be01 100644 --- a/arm_circuits/trivial_logic/Cargo.toml +++ b/arm_circuits/trivial_logic/Cargo.toml @@ -6,4 +6,7 @@ edition = "2021" [dependencies] trivial-logic-methods = { path = "methods" } risc0-zkvm = "3.0.3" -anoma-rm-risc0 = { path = "../../arm", default-features = false } +anoma-rm-risc0 = { path = "../../arm", default-features = false, features = [ + "zkvm", + "k256", +] } diff --git a/arm_circuits/trivial_logic/methods/guest/Cargo.toml b/arm_circuits/trivial_logic/methods/guest/Cargo.toml index d0052225..f4d02d0b 100644 --- a/arm_circuits/trivial_logic/methods/guest/Cargo.toml +++ b/arm_circuits/trivial_logic/methods/guest/Cargo.toml @@ -8,7 +8,10 @@ edition = "2021" [dependencies] # If you want to try (experimental) std support, add `features = [ "std" ]` to risc0-zkvm risc0-zkvm = { version = "3.0.3", features = ["std", "unstable"] } -anoma-rm-risc0 = { path = "../../../../arm", default-features = false } +anoma-rm-risc0 = { path = "../../../../arm", default-features = false, features = [ + "zkvm", + "k256", +] } [patch.crates-io] # Placing this patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 accelerator diff --git a/arm_gadgets/Cargo.toml b/arm_gadgets/Cargo.toml index 182f57c7..959b2300 100644 --- a/arm_gadgets/Cargo.toml +++ b/arm_gadgets/Cargo.toml @@ -9,7 +9,10 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anoma-rm-risc0 = { path = "../arm", default-features = false } +anoma-rm-risc0 = { path = "../arm", default-features = false, features = [ + "zkvm", + "k256", +] } alloy-primitives = "1.0.23" alloy-sol-types = "1.0.23" serde = { version = "1.0.197", default-features = false } diff --git a/arm_tests/arm_test_app/src/lib.rs b/arm_tests/arm_test_app/src/lib.rs index 6681a21e..4492d2af 100644 --- a/arm_tests/arm_test_app/src/lib.rs +++ b/arm_tests/arm_test_app/src/lib.rs @@ -5,7 +5,7 @@ use anoma_rm_risc0::{ action::Action, action_tree::MerkleTree, - compliance::{ComplianceWitness, INITIAL_ROOT}, + compliance::{initial_root, ComplianceWitness}, compliance_unit::ComplianceUnit, delta_proof::DeltaWitness, logic_proof::LogicProver, @@ -103,7 +103,7 @@ pub fn create_an_action_with_multiple_compliances( let compliance_witness = ComplianceWitness { consumed_resource: consumed_resources[i], merkle_path: MerklePath::default(), // dummy path for test - ephemeral_root: *INITIAL_ROOT, + ephemeral_root: initial_root(), nf_key: nf_key.clone(), created_resource: created_resources[i], rcv: Scalar::ONE.to_bytes().to_vec(), // fixed rcv for test diff --git a/arm_tests/arm_test_witness/Cargo.toml b/arm_tests/arm_test_witness/Cargo.toml index 2e7cab34..66f4397f 100644 --- a/arm_tests/arm_test_witness/Cargo.toml +++ b/arm_tests/arm_test_witness/Cargo.toml @@ -7,7 +7,10 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -anoma-rm-risc0 = { path = "../../arm", default-features = false } +anoma-rm-risc0 = { path = "../../arm", default-features = false, features = [ + "zkvm", + "k256", +] } anoma-rm-risc0-gadgets = { path = "../../arm_gadgets" } serde = { version = "1.0.197", default-features = false } k256 = { version = "=0.13.3", features = [