Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
"arm_core",
"arm",
"arm_tests/arm_test_witness",
"arm_tests/arm_test_app",
Expand Down
1 change: 1 addition & 0 deletions arm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository = { workspace = true }

[dependencies]
# If you want to try (experimental) std support, add `features = [ "std" ]` to risc0-zkvm
arm-core = { package = "anoma-rm-core", path = "../arm_core" }
risc0-zkvm = { version = "3.0.3", features = [
"std",
"unstable",
Expand Down
141 changes: 65 additions & 76 deletions arm/src/action.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
//! An action represents a set of compliance units and logic verifiers.

pub use arm_core::action::Action;

use k256::ProjectivePoint;

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

/// An action consists of compliance units and logic verifier inputs.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Action {
/// The compliance units in this action.
pub compliance_units: Vec<ComplianceUnit>,
/// The logic verifier inputs in this action.
pub logic_verifier_inputs: Vec<LogicVerifierInputs>,
}

impl Action {
/// Extension methods for actions that require zkvm/k256 functionality.
pub trait ActionExt {
/// Creates a new Action from compliance units and logic verifiers.
pub fn new(
fn new(
compliance_units: Vec<ComplianceUnit>,
logic_verifiers: Vec<LogicVerifier>,
) -> Result<Self, ArmError>
where
Self: Sized;

/// Verifies all proofs and consistencies in the action.
fn verify(self) -> Result<(), ArmError>;

/// This function computes the delta of the action by summing up the deltas
/// of each compliance unit.
fn delta(&self) -> Result<ProjectivePoint, ArmError>;

/// Constructs logic verifiers from compliance units and logic verifier inputs.
fn get_logic_verifiers(&self) -> Result<Vec<LogicVerifier>, ArmError>;
}

impl ActionExt for Action {
fn new(
compliance_units: Vec<ComplianceUnit>,
logic_verifiers: Vec<LogicVerifier>,
) -> Result<Self, ArmError> {
Expand All @@ -36,40 +48,55 @@ impl Action {
})
}

/// Returns a reference to the compliance units.
pub fn get_compliance_units(&self) -> &Vec<ComplianceUnit> {
&self.compliance_units
fn verify(self) -> Result<(), ArmError> {
for unit in &self.compliance_units {
unit.verify()?;
}

let logic_verifiers = self.get_logic_verifiers()?;
for verifier in &logic_verifiers {
verifier.verify()?;
}

Ok(())
}

/// Returns a reference to the logic verifier inputs.
pub fn get_logic_verifier_inputs(&self) -> &Vec<LogicVerifierInputs> {
&self.logic_verifier_inputs
fn delta(&self) -> Result<ProjectivePoint, ArmError> {
self.compliance_units
.iter()
.try_fold(ProjectivePoint::IDENTITY, |acc, unit| {
Ok(acc + unit.delta()?)
})
}

/// 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> {
fn get_logic_verifiers(&self) -> Result<Vec<LogicVerifier>, ArmError> {
let mut logic_verifiers = Vec::new();

let compliance_intances = self
// Construct the action tree.
let tags: Vec<Digest> = self
.compliance_units
.iter()
.map(|unit| unit.get_instance())
.collect::<Result<Vec<ComplianceInstance>, ArmError>>()?;

// Construct the action tree
let tags: Vec<Digest> = compliance_intances
.iter()
.flat_map(|instance| vec![instance.consumed_nullifier, instance.created_commitment])
.flat_map(|unit| {
[
unit.instance.consumed_nullifier,
unit.instance.created_commitment,
]
})
.collect();
let logics = compliance_intances
let logics: Vec<Digest> = self
.compliance_units
.iter()
.flat_map(|instance| vec![instance.consumed_logic_ref, instance.created_logic_ref])
.collect::<Vec<_>>();
.flat_map(|unit| {
[
unit.instance.consumed_logic_ref,
unit.instance.created_logic_ref,
]
})
.collect();
let action_tree = MerkleTree::from(tags.clone());
let root = action_tree.root()?;

// Match logic verifier inputs with the tags in the action tree
// Match logic verifier inputs with the tags in the action tree.
if tags.len() != self.logic_verifier_inputs.len() {
return Err(ArmError::TagNotFound);
}
Expand All @@ -95,42 +122,4 @@ impl Action {

Ok(logic_verifiers)
}

/// Verifies all proofs and consistencies in the action.
pub fn verify(self) -> Result<(), ArmError> {
for unit in &self.compliance_units {
unit.verify()?;
}

let logic_verifiers = self.get_logic_verifiers()?;
for verifier in logic_verifiers.iter() {
verifier.verify()?;
}

Ok(())
}

/// This function computes the delta of the action by summing up the deltas
/// of each compliance unit.
pub fn delta(&self) -> Result<ProjectivePoint, ArmError> {
self.compliance_units
.iter()
.try_fold(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)
}
}
24 changes: 15 additions & 9 deletions arm/src/action_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

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

/// A Merkle tree structure.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -37,17 +37,17 @@ 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)
.map(|pair| hash_two(&pair[0], &pair[1]))
.map(|pair| hash_core(&pair[0], &pair[1]))
.collect();
}
Ok(cur_layer[0])
}

// Generate the merkle path for the current leave
// Generate the merkle path for the current leave.
/// Generates the Merkle path for a given leaf in the Merkle tree.
///
/// # Arguments
Expand All @@ -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 @@ -99,7 +99,7 @@ impl MerkleTree {

let prev_layer = cur_layer
.chunks(2)
.map(|pair| hash_two(&pair[0], &pair[1]))
.map(|pair| hash_core(&pair[0], &pair[1]))
.collect();

build_merkle_path_inner(prev_layer, position / 2, path);
Expand All @@ -123,3 +123,9 @@ impl From<Vec<Digest>> for MerkleTree {
MerkleTree::new(leaves)
}
}

fn hash_core(left: &Digest, right: &Digest) -> Digest {
let left = core_to_risc0_digest(left);
let right = core_to_risc0_digest(right);
risc0_to_core_digest(hash_two(&left, &right))
}
Loading
Loading