Skip to content
Merged
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
766 changes: 720 additions & 46 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ We have the following feature flags in arm lib:
| `composite_prover` | | Fastest option producing linear-size proofs, and supports compression via recursion |
| `groth16_prover` | | Generates groth16 proofs(requires x86_64 machines) |
| `nif` | | Enables Erlang/Elixir NIF (Native Implemented Function) bindings |
| `evm` | | Wrapped EVM data structures, used in resource logic |


### Usage Examples
Expand All @@ -103,6 +104,9 @@ arm = { version = "0.2.0", default-features = false, features = ["groth16_prover
# Logic-circuit-only usage
arm = { version = "0.2.0", default-features = false, features = ["logic_circuit"] }

# Logic-circuit-only, using evm data structures(e.g. ForwarderCalldata)
arm = { version = "0.2.0", default-features = false, features = ["logic_circuit", "evm"] }

# Elixir Anoma SDK
arm = { version = "0.2.0", features = ["nif"] }
```
Expand Down
5 changes: 4 additions & 1 deletion arm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ hex = "0.4"
lazy_static = "1.5.0"
rustler = { version = "0.36.2", optional = true }
bytemuck = { version = "1.12", features = ["derive"] }
alloy-primitives = { version = "1.0.23", optional = true }
alloy-sol-types = { version = "1.0.23", optional = true }

[features]
default = ["transaction"]
default = ["transaction", "evm"]
prove = ["risc0-zkvm/prove"]
bonsai = ["risc0-zkvm/bonsai"]
nif = ["dep:rustler"]
Expand All @@ -30,3 +32,4 @@ compliance_circuit = []
fast_prover = []
composite_prover = []
groth16_prover = []
evm = ["dep:alloy-primitives", "dep:alloy-sol-types"]
Binary file modified arm/elfs/compliance-guest.bin
Binary file not shown.
Binary file added arm/elfs/logic-test-guest.bin
Binary file not shown.
Binary file modified arm/elfs/trivial-logic-guest.bin
Binary file not shown.
54 changes: 25 additions & 29 deletions arm/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ use crate::{
compliance::{ComplianceInstance, ComplianceWitness},
compliance_unit::ComplianceUnit,
delta_proof::DeltaWitness,
logic_proof::{LogicProof, LogicProver},
logic_proof::{LogicProver, LogicVerifier, LogicVerifierInputs},
merkle_path::COMMITMENT_TREE_DEPTH,
nullifier_key::NullifierKey,
resource::Resource,
resource_logic::TrivialLogicWitness,
};
use k256::ProjectivePoint;
#[cfg(feature = "nif")]
Expand All @@ -19,29 +18,26 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "nif", module = "Anoma.Arm.Action")]
pub struct Action {
pub compliance_units: Vec<ComplianceUnit>,
pub logic_verifier_inputs: Vec<LogicProof>,
pub logic_verifier_inputs: Vec<LogicVerifierInputs>,
}

impl Action {
pub fn new(
compliance_units: Vec<ComplianceUnit>,
logic_verifier_inputs: Vec<LogicProof>,
) -> Self {
pub fn new(compliance_units: Vec<ComplianceUnit>, logic_verifiers: Vec<LogicVerifier>) -> Self {
Action {
compliance_units,
logic_verifier_inputs,
logic_verifier_inputs: logic_verifiers.into_iter().map(|lv| lv.into()).collect(),
}
}

pub fn get_compliance_units(&self) -> &Vec<ComplianceUnit> {
&self.compliance_units
}

pub fn get_logic_verifier_inputs(&self) -> &Vec<LogicProof> {
pub fn get_logic_verifier_inputs(&self) -> &Vec<LogicVerifierInputs> {
&self.logic_verifier_inputs
}

pub fn verify(&self) -> bool {
pub fn verify(self) -> bool {
for unit in &self.compliance_units {
if !unit.verify() {
return false;
Expand Down Expand Up @@ -76,22 +72,20 @@ impl Action {
let action_tree = MerkleTree::from(tags.clone());
let root = action_tree.root();

for proof in &self.logic_verifier_inputs {
let instance = proof.get_instance();

if root != instance.root {
return false;
}
for input in self.logic_verifier_inputs {
if let Some(index) = tags.iter().position(|tag| *tag == input.tag) {
if input.verifying_key != logics[index] {
// The verifying_key doesn't match the resource logic
return false;
}

if let Some(index) = tags.iter().position(|tag| *tag == instance.tag) {
if proof.verifying_key != logics[index] {
let is_comsumed = index % 2 == 0;
let verifier = input.to_logic_verifier(is_comsumed, root.clone());
if !verifier.verify() {
return false;
}
} else {
return false;
}

if !proof.verify() {
// Tag not found
return false;
}
}
Expand All @@ -118,11 +112,14 @@ impl Action {
}

pub fn create_an_action(nonce: u8) -> (Action, DeltaWitness) {
use crate::logic_proof::TestLogic;

let nf_key = NullifierKey::default();
let nf_key_cm = nf_key.commit();
let mut consumed_resource = Resource {
logic_ref: TrivialLogicWitness::verifying_key_as_bytes(),
logic_ref: TestLogic::verifying_key_as_bytes(),
nk_commitment: nf_key_cm,
quantity: 1,
..Default::default()
};
consumed_resource.nonce[0] = nonce;
Expand All @@ -143,23 +140,22 @@ pub fn create_an_action(nonce: u8) -> (Action, DeltaWitness) {
let consumed_resource_path = action_tree.generate_path(&consumed_resource_nf).unwrap();
let created_resource_path = action_tree.generate_path(&created_resource_cm).unwrap();

let consumed_logic_witness = TrivialLogicWitness::new(
let consumed_logic = TestLogic::new(
consumed_resource,
consumed_resource_path,
nf_key.clone(),
true,
);
let consumed_logic_proof = consumed_logic_witness.prove();
let consumed_logic_proof = consumed_logic.prove();

let created_logic_witness =
TrivialLogicWitness::new(created_resource, created_resource_path, nf_key, false);
let created_logic_proof = created_logic_witness.prove();
let created_logic = TestLogic::new(created_resource, created_resource_path, nf_key, false);
let created_logic_proof = created_logic.prove();

let compliance_units = vec![compliance_receipt];
let logic_verifier_inputs = vec![consumed_logic_proof, created_logic_proof];

let action = Action::new(compliance_units, logic_verifier_inputs);
assert!(action.verify());
assert!(action.clone().verify());

let delta_witness = DeltaWitness::from_bytes_vec(&[compliance_witness.rcv]);
(action, delta_witness)
Expand Down
13 changes: 10 additions & 3 deletions arm/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ use risc0_zkvm::Digest;
pub const COMPLIANCE_PK: &[u8] = include_bytes!("../elfs/compliance-guest.bin");
// Padding logic proving key / padding logic guest ELF binary
pub const PADDING_LOGIC_PK: &[u8] = include_bytes!("../elfs/trivial-logic-guest.bin");
// Test logic proving key / test logic guest ELF binary
pub const TEST_LOGIC_PK: &[u8] = include_bytes!("../elfs/logic-test-guest.bin");

lazy_static! {
// compliance verification key / compliance image id
pub static ref COMPLIANCE_VK: Digest =
Digest::from_hex("e04fd74c5f4ed1fc3ccf4412c358927907eec18891c87f157a4b0eede2e01adc")
Digest::from_hex("2c10d71e919b8b6359bfc167294c9994c1699e3eeb851d4b7775edb67b54a327")
.unwrap();

// compliance verification key / compliance image id
// padding logic verification key / compliance image id
pub static ref PADDING_LOGIC_VK: Digest =
Digest::from_hex("751068c8a7bf034fefac9ba4adb745ecb5a1345e40345b8f0a4bef380f06fbe2")
Digest::from_hex("f8047dc2cf6cbe45137a588a3f019814218e7d7199b1b86a57b51c310e04fae9")
.unwrap();

// test logic verification key / compliance image id
pub static ref TEST_LOGIC_VK: Digest =
Digest::from_hex("2805a3008e096f4047b661c7cdcd662a580ea2dd9d93eedc3b686a5677af3c6f")
.unwrap();
}
23 changes: 18 additions & 5 deletions arm/src/encryption.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::utils::{bytes_to_words, words_to_bytes};
use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit};
use k256::{
elliptic_curve::{group::GroupEncoding, Field},
Expand Down Expand Up @@ -28,12 +29,20 @@ impl SecretKey {
pub struct Ciphertext(Vec<u8>);

impl Ciphertext {
pub fn new(cipher: Vec<u8>) -> Self {
pub fn from_bytes(cipher: Vec<u8>) -> Self {
Ciphertext(cipher)
}

pub fn inner(&self) -> Vec<u8> {
self.0.clone()
pub fn from_words(words: &[u32]) -> Self {
Ciphertext(words_to_bytes(words).to_vec())
}

pub fn inner(&self) -> &[u8] {
&self.0
}

pub fn as_words(&self) -> Vec<u32> {
bytes_to_words(self.inner())
}

pub fn encrypt(
Expand Down Expand Up @@ -63,7 +72,7 @@ impl Ciphertext {
return Err(aes_gcm::Error);
}
let cipher: InnerCiphert =
bincode::deserialize(&self.inner()).expect("deserialization failure");
bincode::deserialize(self.inner()).expect("deserialization failure");
// Generate the secret key using Diffie-Hellman exchange
let inner_secret_key = InnerSecretKey::from_dh_exchange(&cipher.pk, sk.inner());

Expand Down Expand Up @@ -131,6 +140,10 @@ fn test_encryption() {

// Decryption
let decryption = cipher.decrypt(&receiver_sk).unwrap();

assert_eq!(message, decryption);

let cipher_words = cipher.as_words();
let cipher_from_words = Ciphertext::from_words(&cipher_words);
let decrypted_from_words = cipher_from_words.decrypt(&receiver_sk).unwrap();
assert_eq!(message, decrypted_from_words);
}
118 changes: 118 additions & 0 deletions arm/src/evm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use crate::resource::Resource as ArmResource;
use alloy_primitives::B256;
use alloy_sol_types::sol;
use alloy_sol_types::SolValue;
sol! {
struct Resource {
bytes32 logicRef;
bytes32 labelRef;
bytes32 valueRef;
bytes32 nullifierKeyCommitment;
bytes32 nonce;
bytes32 randSeed;
uint128 quantity;
bool ephemeral;
}
}

impl Resource {
pub fn encode(&self) -> Vec<u8> {
self.abi_encode()
}

pub fn decode(encoded: &[u8]) -> Option<Self> {
Self::abi_decode(encoded).ok()
}
}

impl From<ArmResource> for Resource {
fn from(r: ArmResource) -> Self {
Self {
logicRef: B256::from_slice(&r.logic_ref),
labelRef: B256::from_slice(&r.label_ref),
quantity: r.quantity,
valueRef: B256::from_slice(&r.value_ref),
ephemeral: r.is_ephemeral,
nonce: B256::from_slice(&r.nonce),
nullifierKeyCommitment: B256::from_slice(r.nk_commitment.inner()),
randSeed: B256::from_slice(&r.rand_seed),
}
}
}

sol! {
struct ForwarderCalldata {
address untrustedForwarder;
bytes input;
bytes output;
}
}

impl ForwarderCalldata {
pub fn new(untrusted_forwarder: &str, input: Vec<u8>, output: Vec<u8>) -> Self {
let untrusted_forwarder_addr = untrusted_forwarder.parse().expect("Invalid address string");
ForwarderCalldata {
untrustedForwarder: untrusted_forwarder_addr,
input: input.into(),
output: output.into(),
}
}

pub fn from_hex(untrusted_forwarder: &str, input: &str, output: &str) -> Self {
let untrusted_forwarder_addr = untrusted_forwarder.parse().expect("Invalid address string");
ForwarderCalldata {
untrustedForwarder: untrusted_forwarder_addr,
input: hex::decode(input).expect("Invalid hex input").into(),
output: hex::decode(output).expect("Invalid hex output").into(),
}
}

pub fn encode(&self) -> Vec<u8> {
self.abi_encode()
}

pub fn decode(encoded: &[u8]) -> Option<Self> {
Self::abi_decode(encoded).ok()
}
}

#[test]
fn forward_call_data_test() {
// Example data
let addr = "0x1111111111111111111111111111111111111111";
let input = hex::decode("1122").unwrap();
let output = hex::decode("aabbcc").unwrap();

// Create instance
let data = ForwarderCalldata::new(addr, input, output);
let data_from_hex = ForwarderCalldata::from_hex(addr, "1122", "aabbcc");
assert_eq!(data.input, data_from_hex.input);
assert_eq!(data.output, data_from_hex.output);

// abi encode
let encoded_data = data.encode();
let decoded_data = ForwarderCalldata::decode(&encoded_data).unwrap();

assert_eq!(data.untrustedForwarder, decoded_data.untrustedForwarder);
assert_eq!(data.input, decoded_data.input);
assert_eq!(data.output, decoded_data.output);
}

#[test]
fn evm_resource_test() {
let arm_resource = ArmResource::default();
let evm_resource: Resource = arm_resource.clone().into();
let encoded_resource = evm_resource.encode();
let decoded_resource = Resource::decode(&encoded_resource).unwrap();
assert_eq!(arm_resource.logic_ref, decoded_resource.logicRef.as_slice());
assert_eq!(arm_resource.label_ref, decoded_resource.labelRef.as_slice());
assert_eq!(arm_resource.value_ref, decoded_resource.valueRef.as_slice());
assert_eq!(arm_resource.nonce, decoded_resource.nonce.as_slice());
assert_eq!(arm_resource.rand_seed, decoded_resource.randSeed.as_slice());
assert_eq!(arm_resource.is_ephemeral, decoded_resource.ephemeral);
assert_eq!(arm_resource.quantity, decoded_resource.quantity);
assert_eq!(
arm_resource.nk_commitment.inner(),
decoded_resource.nullifierKeyCommitment.as_slice()
);
}
4 changes: 4 additions & 0 deletions arm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub mod constants;
#[cfg(feature = "transaction")]
pub mod delta_proof;
pub mod encryption;
#[cfg(feature = "evm")]
pub mod evm;
pub mod logic_instance;
#[cfg(feature = "transaction")]
pub mod logic_proof;
Expand All @@ -22,6 +24,8 @@ pub mod proving_system;
pub mod resource;
#[cfg(feature = "logic_circuit")]
pub mod resource_logic;
#[cfg(all(feature = "logic_circuit", feature = "evm"))]
pub mod test_logic;
#[cfg(feature = "transaction")]
pub mod transaction;
pub mod utils;
Loading
Loading