Skip to main content

anoma_rm_risc0_test_app/
lib.rs

1// This module contains a trivial test circuit and tx tests for the ARM crate.
2// The functions here are also used in the elixir sdk and binding libraries to
3// ensure that the ARM crate's transaction functionalities work as expected.
4
5use anoma_rm_risc0::{
6    action::Action,
7    action_tree::MerkleTree,
8    compliance::{ComplianceWitness, INITIAL_ROOT},
9    compliance_unit::ComplianceUnit,
10    delta_proof::DeltaWitness,
11    logic_proof::LogicProver,
12    merkle_path::MerklePath,
13    nullifier_key::NullifierKey,
14    proving_system::ProofType,
15    resource::Resource,
16    transaction::{Delta, Transaction},
17    Digest,
18};
19use anoma_rm_risc0_test_witness::TestLogicWitness;
20use hex::FromHex;
21use k256::Scalar;
22use lazy_static::lazy_static;
23use serde::{Deserialize, Serialize};
24
25// Test logic proving key / test logic guest ELF binary
26pub const TEST_LOGIC_PK: &[u8] = include_bytes!("../elf/logic-test-guest.bin");
27
28lazy_static! {
29    // test logic verification key / compliance image id
30    pub static ref TEST_LOGIC_VK: Digest =
31        Digest::from_hex("73167841dd698323eb04209f89e6c19c5559e83841277621ab538feb8a715dfe")
32            .unwrap();
33}
34
35#[derive(Clone, Default, Deserialize, Serialize)]
36pub struct TestLogic {
37    witness: TestLogicWitness,
38}
39
40impl TestLogic {
41    pub fn new(
42        resource: Resource,
43        receive_existence_path: MerklePath,
44        nf_key: NullifierKey,
45        is_consumed: bool,
46    ) -> Self {
47        let witness = TestLogicWitness {
48            resource,
49            receive_existence_path,
50            is_consumed,
51            nf_key,
52        };
53        TestLogic { witness }
54    }
55}
56
57impl LogicProver for TestLogic {
58    type Witness = TestLogicWitness;
59
60    fn proving_key() -> &'static [u8] {
61        TEST_LOGIC_PK
62    }
63
64    fn verifying_key() -> Digest {
65        *TEST_LOGIC_VK
66    }
67
68    fn witness(&self) -> &Self::Witness {
69        &self.witness
70    }
71}
72
73pub fn create_an_action_with_multiple_compliances(
74    compliance_num: usize,
75    nonce: u8,
76    proof_type: ProofType,
77) -> (Action, DeltaWitness) {
78    let nf_key = NullifierKey::default();
79    let nf_key_cm = nf_key.commit();
80
81    // Generate multiple consumed and created resources
82    let (consumed_resources, created_resources): (Vec<_>, Vec<_>) = (0..compliance_num)
83        .map(|i| {
84            let mut consumed_resource = Resource {
85                logic_ref: TestLogic::verifying_key(),
86                nk_commitment: nf_key_cm,
87                quantity: 1,
88                ..Default::default()
89            };
90            consumed_resource.nonce = [[nonce; 16], [i as u8; 16]].concat().try_into().unwrap();
91            let consumed_resource_nf = consumed_resource.nullifier(&nf_key).unwrap();
92
93            let mut created_resource = consumed_resource;
94            created_resource.set_nonce(consumed_resource_nf);
95            (consumed_resource, created_resource)
96        })
97        .unzip();
98
99    let mut compliance_units = Vec::new();
100    let mut rcvs = Vec::new();
101    let mut action_tree = MerkleTree::new(vec![]);
102    for i in 0..compliance_num {
103        let compliance_witness = ComplianceWitness {
104            consumed_resource: consumed_resources[i],
105            merkle_path: MerklePath::default(), // dummy path for test
106            ephemeral_root: *INITIAL_ROOT,
107            nf_key: nf_key.clone(),
108            created_resource: created_resources[i],
109            rcv: Scalar::ONE.to_bytes().to_vec(), // fixed rcv for test
110        };
111
112        let compliance_receipt = ComplianceUnit::create(&compliance_witness, proof_type).unwrap();
113
114        let consumed_resource_nf = consumed_resources[i].nullifier(&nf_key).unwrap();
115        let created_resource_cm = created_resources[i].commitment();
116        action_tree.insert(consumed_resource_nf);
117        action_tree.insert(created_resource_cm);
118
119        compliance_units.push(compliance_receipt);
120        rcvs.push(compliance_witness.rcv);
121    }
122
123    let logic_verifier_inputs = (0..compliance_num)
124        .flat_map(|i| {
125            let consumed_resource_nf = consumed_resources[i].nullifier(&nf_key).unwrap();
126            let created_resource_cm = created_resources[i].commitment();
127            let consumed_resource_path = action_tree.generate_path(&consumed_resource_nf).unwrap();
128            let created_resource_path = action_tree.generate_path(&created_resource_cm).unwrap();
129
130            let consumed_logic = TestLogic::new(
131                consumed_resources[i],
132                consumed_resource_path,
133                nf_key.clone(),
134                true,
135            );
136            let consumed_logic_proof = consumed_logic.prove(proof_type).unwrap();
137
138            let created_logic = TestLogic::new(
139                created_resources[i],
140                created_resource_path,
141                nf_key.clone(),
142                false,
143            );
144            let created_logic_proof = created_logic.prove(proof_type).unwrap();
145
146            vec![consumed_logic_proof, created_logic_proof]
147        })
148        .collect();
149
150    let action = Action::new(compliance_units, logic_verifier_inputs).unwrap();
151    action.clone().verify().unwrap();
152
153    let delta_witness = DeltaWitness::from_bytes_vec(&rcvs).unwrap();
154    (action, delta_witness)
155}
156
157pub fn create_multiple_actions(
158    action_num: usize,
159    compliance_num: usize,
160    proof_type: ProofType,
161) -> (Vec<Action>, DeltaWitness) {
162    let mut actions = Vec::new();
163    let mut delta_witnesses = Vec::new();
164    for i in 0..action_num {
165        let (action, delta_witness) =
166            create_an_action_with_multiple_compliances(compliance_num, i as u8, proof_type);
167        actions.push(action);
168        delta_witnesses.push(delta_witness);
169    }
170    (actions, DeltaWitness::compress(&delta_witnesses))
171}
172
173// Create a test transaction with n_actions actions, each with compliance_num compliance units
174pub fn generate_test_transaction(
175    n_actions: usize,
176    compliance_num: usize,
177    proof_type: ProofType,
178) -> Transaction {
179    let (actions, delta_witness) = create_multiple_actions(n_actions, compliance_num, proof_type);
180    let tx = Transaction::create(actions, Delta::Witness(delta_witness));
181    let balanced_tx = tx.generate_delta_proof().unwrap();
182    balanced_tx.clone().verify().unwrap();
183    balanced_tx
184}
185
186#[test]
187fn test_logic_prover() {
188    let proof_type = ProofType::Succinct;
189    let test_logic = TestLogic::default();
190    let proof = test_logic.prove(proof_type).unwrap();
191    proof.verify().unwrap();
192}
193
194#[test]
195fn test_action() {
196    let _ = create_an_action_with_multiple_compliances(2, 1, ProofType::Succinct);
197}
198
199#[test]
200fn test_unmatched_logic_verifier_inputs_in_action() {
201    let (actions, _) = create_multiple_actions(2, 1, ProofType::Succinct);
202    // swap logic verifier inputs to cause mismatch in action0
203    let mut action0 = actions[0].clone();
204    action0.logic_verifier_inputs = actions[1].logic_verifier_inputs.clone();
205    assert!(action0.verify().is_err());
206
207    // empty logic verifier inputs in action1
208    let mut action1 = actions[1].clone();
209    action1.logic_verifier_inputs = vec![];
210    assert!(action1.verify().is_err());
211}
212
213#[test]
214fn test_nullifier_duplication_check() {
215    let mut tx = generate_test_transaction(2, 1, ProofType::Succinct);
216    assert!(tx.nf_duplication_check().is_ok());
217
218    // Introduce a duplicate nullifier
219    tx.actions[1] = tx.actions[0].clone();
220
221    assert!(tx.nf_duplication_check().is_err());
222}
223
224#[test]
225fn test_transaction() {
226    let _ = generate_test_transaction(2, 2, ProofType::Succinct);
227}
228
229#[test]
230#[ignore]
231fn test_transaction_groth16() {
232    let _ = generate_test_transaction(2, 2, ProofType::Groth16);
233}
234
235#[test]
236fn test_aggregation_works() {
237    let tx = generate_test_transaction(2, 2, ProofType::Succinct);
238    let mut tx_str = tx.clone();
239    assert!(tx_str.aggregate(ProofType::Succinct).is_ok());
240    assert!(tx_str.aggregation_proof.is_some());
241    assert!(tx_str.verify_aggregation().is_ok());
242}
243
244#[test]
245#[ignore]
246fn test_aggregation_works_groth16() {
247    let tx = generate_test_transaction(2, 2, ProofType::Succinct);
248    let mut tx_str = tx.clone();
249    assert!(tx_str.aggregate(ProofType::Groth16).is_ok());
250    assert!(tx_str.aggregation_proof.is_some());
251    assert!(tx_str.verify_aggregation().is_ok());
252}
253
254#[test]
255fn test_verify_aggregation_fails_for_incorrect_instances() {
256    let tx = generate_test_transaction(2, 2, ProofType::Succinct);
257    let mut tx_str = tx.clone();
258    assert!(tx_str.aggregate(ProofType::Succinct).is_ok());
259
260    tx_str.actions[0].logic_verifier_inputs.pop();
261
262    assert!(tx_str.verify_aggregation().is_err());
263}
264
265#[test]
266fn test_cannot_aggregate_invalid_proofs() {
267    use anoma_rm_risc0::logic_proof::LogicVerifierInputs;
268
269    let tx = generate_test_transaction(2, 2, ProofType::Succinct);
270
271    // Create a transaction with one invalid proof.
272    let bad_lproof = LogicVerifierInputs {
273        proof: tx.actions[0].logic_verifier_inputs[0].clone().proof,
274        verifying_key: Digest::from_bytes([66u8; 32]), //vec![666u32; 8], // Bad key.
275        tag: tx.actions[0].logic_verifier_inputs[0].tag,
276        app_data: tx.actions[0].logic_verifier_inputs[0].app_data.clone(),
277    };
278
279    let bad_action = Action {
280        compliance_units: tx.actions[0].clone().compliance_units,
281        logic_verifier_inputs: vec![bad_lproof],
282    };
283    let bad_tx = Transaction::create(vec![bad_action, tx.actions[1].clone()], tx.delta_proof);
284
285    let mut bad_tx_str = bad_tx.clone();
286    assert!(bad_tx_str.aggregate(ProofType::Succinct).is_err());
287    assert!(bad_tx_str.aggregation_proof.is_none());
288}