diff --git a/taiga_halo2/Cargo.toml b/taiga_halo2/Cargo.toml index 4ba7da56..7cdca7e8 100644 --- a/taiga_halo2/Cargo.toml +++ b/taiga_halo2/Cargo.toml @@ -18,7 +18,7 @@ subtle = { version = "2.3", default-features = false } dyn-clone = "1.0" reddsa = { git = "https://github.com/heliaxdev/reddsa.git", branch = "taiga" } vamp-ir = { git = "https://github.com/anoma/vamp-ir.git", rev = "6d401f8a479951727586ef0c44c42edab3139090" } -bincode = "2.0.0-rc.3" +bincode = "1.3.3" byteorder = "1.4" num-bigint = "0.4" @@ -47,7 +47,7 @@ name = "tx_examples" required-features = ["examples"] [features] -default = [] +default = ["serde"] nif = ["dep:rustler", "borsh", "pasta_curves/repr-erlang"] serde = ["dep:serde", "pasta_curves/serde"] borsh = ["dep:borsh"] diff --git a/taiga_halo2/benches/resource_logic_proof.rs b/taiga_halo2/benches/resource_logic_proof.rs index 0de26f3e..53113f6b 100644 --- a/taiga_halo2/benches/resource_logic_proof.rs +++ b/taiga_halo2/benches/resource_logic_proof.rs @@ -1,77 +1,19 @@ use criterion::{criterion_group, criterion_main, Criterion}; use halo2_proofs::plonk::{keygen_pk, keygen_vk}; - -use halo2_proofs::arithmetic::Field; -use pasta_curves::pallas; use rand::rngs::OsRng; -use rand::Rng; use taiga_halo2::{ circuit::{ resource_logic_circuit::ResourceLogicCircuit, resource_logic_examples::TrivialResourceLogicCircuit, }, - constant::{NUM_RESOURCE, RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, SETUP_PARAMS_MAP}, - nullifier::{Nullifier, NullifierKeyContainer}, + constant::{RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, SETUP_PARAMS_MAP}, proof::Proof, - resource::{Resource, ResourceKind}, }; fn bench_resource_logic_proof(name: &str, c: &mut Criterion) { let mut rng = OsRng; - let resource_logic_circuit = { - let input_resources = [(); NUM_RESOURCE].map(|_| { - let nonce = Nullifier::from(pallas::Base::random(&mut rng)); - let nk = NullifierKeyContainer::from_key(pallas::Base::random(&mut rng)); - let kind = { - let logic = pallas::Base::random(&mut rng); - let label = pallas::Base::random(&mut rng); - ResourceKind::new(logic, label) - }; - let value = pallas::Base::random(&mut rng); - let quantity: u64 = rng.gen(); - let rseed = pallas::Base::random(&mut rng); - Resource { - kind, - value, - quantity, - nk_container: nk, - is_ephemeral: false, - nonce, - rseed, - } - }); - let output_resources = input_resources - .iter() - .map(|input| { - let nonce = input.get_nf().unwrap(); - let npk = NullifierKeyContainer::from_npk(pallas::Base::random(&mut rng)); - let kind = { - let logic = pallas::Base::random(&mut rng); - let label = pallas::Base::random(&mut rng); - ResourceKind::new(logic, label) - }; - let value = pallas::Base::random(&mut rng); - let quantity: u64 = rng.gen(); - let rseed = pallas::Base::random(&mut rng); - Resource { - kind, - value, - quantity, - nk_container: npk, - is_ephemeral: false, - nonce, - rseed, - } - }) - .collect::>(); - let owned_resource_id = input_resources[0].get_nf().unwrap().inner(); - TrivialResourceLogicCircuit::new( - owned_resource_id, - input_resources, - output_resources.try_into().unwrap(), - ) - }; + let resource_logic_circuit = TrivialResourceLogicCircuit::default(); let params = SETUP_PARAMS_MAP .get(&RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE) .unwrap(); diff --git a/taiga_halo2/deprecated/simple_sudoku/resource_logic.rs b/taiga_halo2/deprecated/simple_sudoku/resource_logic.rs index 0da249b4..b990e8a0 100644 --- a/taiga_halo2/deprecated/simple_sudoku/resource_logic.rs +++ b/taiga_halo2/deprecated/simple_sudoku/resource_logic.rs @@ -67,7 +67,7 @@ impl ResourceLogicInfo for SudokuResourceLogic { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { + fn get_self_resource_id(&self) -> pallas::Base { pallas::Base::zero() } } diff --git a/taiga_halo2/deprecated/taiga_sudoku/app_resource_logic.rs b/taiga_halo2/deprecated/taiga_sudoku/app_resource_logic.rs index cbaa1a4f..c2d9aa6f 100644 --- a/taiga_halo2/deprecated/taiga_sudoku/app_resource_logic.rs +++ b/taiga_halo2/deprecated/taiga_sudoku/app_resource_logic.rs @@ -107,7 +107,7 @@ impl Default for SudokuState { #[derive(Clone, Debug, Default)] struct SudokuAppResourceLogicCircuit { - owned_resource_id: pallas::Base, + self_resource_id: pallas::Base, input_resources: [Resource; NUM_RESOURCE], output_resources: [Resource; NUM_RESOURCE], // Initial puzzle encoded in a single field @@ -453,8 +453,8 @@ impl ResourceLogicInfo for SudokuAppResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource_id(&self) -> pallas::Base { + self.self_resource_id } } @@ -467,11 +467,11 @@ impl ResourceLogicCircuit for SudokuAppResourceLogicCircuit { mut layouter: impl Layouter, basic_variables: BasicResourceLogicVariables, ) -> Result<(), Error> { - let owned_resource_id = basic_variables.get_owned_resource_id(); + let self_resource_id = basic_variables.get_self_resource_id(); let is_input_resource = get_is_input_resource_flag( config.get_is_input_resource_flag_config, layouter.namespace(|| "get is_input_resource_flag"), - &owned_resource_id, + &self_resource_id, &basic_variables.get_input_resource_nfs(), &basic_variables.get_output_resource_cms(), )?; @@ -666,9 +666,9 @@ fn test_halo2_sudoku_app_resource_logic_circuit_init() { output_resources[0].kind.app_data_static = poseidon_hash(encoded_init_state, current_state.encode()); output_resources[0].quantity = 1u64; - let owned_resource_id = output_resources[0].commitment().inner(); + let self_resource_id = output_resources[0].commitment().inner(); SudokuAppResourceLogicCircuit { - owned_resource_id, + self_resource_id, input_resources, output_resources: output_resources.try_into().unwrap(), encoded_init_state, @@ -745,7 +745,7 @@ fn test_halo2_sudoku_app_resource_logic_circuit_update() { output_resources[0].quantity = 1u64; output_resources[0].kind.app_vk = input_resources[0].kind.app_vk; SudokuAppResourceLogicCircuit { - owned_resource_id: input_resources[0].get_nf().unwrap().inner(), + self_resource_id: input_resources[0].get_nf().unwrap().inner(), input_resources, output_resources: output_resources.try_into().unwrap(), encoded_init_state, @@ -821,7 +821,7 @@ fn halo2_sudoku_app_resource_logic_circuit_final() { output_resources[0].quantity = 0u64; output_resources[0].kind.app_vk = input_resources[0].kind.app_vk; SudokuAppResourceLogicCircuit { - owned_resource_id: input_resources[0].get_nf().unwrap().inner(), + self_resource_id: input_resources[0].get_nf().unwrap().inner(), input_resources, output_resources: output_resources.try_into().unwrap(), encoded_init_state, diff --git a/taiga_halo2/deprecated/taiga_sudoku/dealer_intent_app_resource_logc.rs b/taiga_halo2/deprecated/taiga_sudoku/dealer_intent_app_resource_logc.rs index 048b750a..a1b0d966 100644 --- a/taiga_halo2/deprecated/taiga_sudoku/dealer_intent_app_resource_logc.rs +++ b/taiga_halo2/deprecated/taiga_sudoku/dealer_intent_app_resource_logc.rs @@ -37,7 +37,7 @@ use taiga_halo2::{ #[derive(Clone, Debug, Default)] struct DealerIntentResourceLogicCircuit { - owned_resource_id: pallas::Base, + self_resource_id: pallas::Base, input_resources: [Resource; NUM_RESOURCE], output_resources: [Resource; NUM_RESOURCE], encoded_puzzle: pallas::Base, @@ -153,8 +153,8 @@ impl ResourceLogicInfo for DealerIntentResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource_id(&self) -> pallas::Base { + self.self_resource_id } } @@ -167,11 +167,11 @@ impl ResourceLogicCircuit for DealerIntentResourceLogicCircuit { mut layouter: impl Layouter, basic_variables: BasicResourceLogicVariables, ) -> Result<(), Error> { - let owned_resource_id = basic_variables.get_owned_resource_id(); + let self_resource_id = basic_variables.get_self_resource_id(); let is_input_resource = get_is_input_resource_flag( config.get_is_input_resource_flag_config, layouter.namespace(|| "get is_input_resource_flag"), - &owned_resource_id, + &self_resource_id, &basic_variables.get_input_resource_nfs(), &basic_variables.get_output_resource_cms(), )?; @@ -180,7 +180,7 @@ impl ResourceLogicCircuit for DealerIntentResourceLogicCircuit { let app_data_static = get_owned_resource_variable( config.get_owned_resource_variable_config, layouter.namespace(|| "get owned resource app_static_data"), - &owned_resource_id, + &self_resource_id, &basic_variables.get_app_data_static_searchable_pairs(), )?; @@ -372,9 +372,9 @@ fn test_halo2_dealer_intent_resource_logic_circuit() { sudoku_app_vk, ); let encoded_solution = pallas::Base::random(&mut rng); - let owned_resource_id = output_resources[0].commitment().inner(); + let self_resource_id = output_resources[0].commitment().inner(); DealerIntentResourceLogicCircuit { - owned_resource_id, + self_resource_id, input_resources, output_resources: output_resources.try_into().unwrap(), encoded_puzzle, diff --git a/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs b/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs deleted file mode 100644 index c1204b96..00000000 --- a/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs +++ /dev/null @@ -1,248 +0,0 @@ -/// The example shows how to cascade the partial transactions by intents. -/// Alice wants to spend 1 "BTC", 2 "ETH" and 3 "XAN" simultaneously -/// -use halo2_proofs::arithmetic::Field; -use pasta_curves::pallas; -use rand::{CryptoRng, RngCore}; -use taiga_halo2::{ - circuit::resource_logic_examples::{ - cascade_intent::{create_intent_resource, CascadeIntentResourceLogicCircuit}, - signature_verification::COMPRESSED_TOKEN_AUTH_VK, - token::{Token, TokenAuthorization}, - }, - compliance::ComplianceInfo, - constant::TAIGA_COMMITMENT_TREE_DEPTH, - merkle_tree::{Anchor, MerklePath}, - resource::ResourceLogics, - shielded_ptx::ShieldedPartialTransaction, - transaction::{ShieldedPartialTxBundle, Transaction, TransparentPartialTxBundle}, -}; - -pub fn create_transaction(mut rng: R) -> Transaction { - let alice_auth_sk = pallas::Scalar::random(&mut rng); - let alice_auth = TokenAuthorization::from_sk_vk(&alice_auth_sk, &COMPRESSED_TOKEN_AUTH_VK); - let alice_nk = pallas::Base::random(&mut rng); - - let bob_auth = TokenAuthorization::random(&mut rng); - let bob_npk = pallas::Base::random(&mut rng); - - let input_token_1 = Token::new("btc".to_string(), 1u64); - let input_resource_1 = - input_token_1.create_random_input_token_resource(&mut rng, alice_nk, &alice_auth); - let output_token_1 = Token::new("btc".to_string(), 1u64); - let mut output_resource_1 = - output_token_1.create_random_output_token_resource(&mut rng, bob_npk, &bob_auth); - let input_token_2 = Token::new("eth".to_string(), 2u64); - let input_resource_2 = - input_token_2.create_random_input_token_resource(&mut rng, alice_nk, &alice_auth); - - let input_token_3 = Token::new("xan".to_string(), 3u64); - let input_resource_3 = - input_token_3.create_random_input_token_resource(&mut rng, alice_nk, &alice_auth); - let mut cascade_intent_resource = - create_intent_resource(&mut rng, input_resource_3.commitment().inner(), alice_nk); - let output_token_2 = Token::new("eth".to_string(), 2u64); - let mut output_resource_2 = - output_token_2.create_random_output_token_resource(&mut rng, bob_npk, &bob_auth); - let output_token_3 = Token::new("xan".to_string(), 3u64); - let mut output_resource_3 = - output_token_3.create_random_output_token_resource(&mut rng, bob_npk, &bob_auth); - - let merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); - - // Fetch a valid anchor for dummy resources - let anchor = Anchor::from(pallas::Base::random(&mut rng)); - - // The first partial transaction: - // Alice consumes 1 "BTC" and 2 "ETH". - // Alice creates a cascade intent resource and 1 "BTC" to Bob. - let ptx_1 = { - // Create compliance pairs - let compliances = { - let compliance_1 = ComplianceInfo::new( - *input_resource_1.resource(), - merkle_path.clone(), - None, - &mut output_resource_1.resource, - &mut rng, - ); - - let compliance_2 = ComplianceInfo::new( - *input_resource_2.resource(), - merkle_path.clone(), - None, - &mut cascade_intent_resource, - &mut rng, - ); - vec![compliance_1, compliance_2] - }; - - // Create resource logics - let (input_resource_logics, output_resource_logics) = { - let input_resources = [*input_resource_1.resource(), *input_resource_2.resource()]; - let output_resources = [*output_resource_1.resource(), cascade_intent_resource]; - - // Create resource logics for the input resource_1 - let input_resource_1_resource_logics = input_resource_1 - .generate_input_token_resource_logics( - &mut rng, - alice_auth, - alice_auth_sk, - input_resources, - output_resources, - ); - - // Create resource logics for the input resource_2 - let input_resource_2_resource_logics = input_resource_2 - .generate_input_token_resource_logics( - &mut rng, - alice_auth, - alice_auth_sk, - input_resources, - output_resources, - ); - - // Create resource logics for the output resource_1 - let output_resource_1_resource_logics = output_resource_1 - .generate_output_token_resource_logics( - &mut rng, - bob_auth, - input_resources, - output_resources, - ); - - // Create resource logics for the intent - let intent_resource_logics = { - let intent_resource_logic = CascadeIntentResourceLogicCircuit { - owned_resource_id: cascade_intent_resource.commitment().inner(), - input_resources, - output_resources, - cascade_resource_cm: cascade_intent_resource.get_label(), - }; - - ResourceLogics::new(Box::new(intent_resource_logic), vec![]) - }; - - ( - vec![ - input_resource_1_resource_logics, - input_resource_2_resource_logics, - ], - vec![output_resource_1_resource_logics, intent_resource_logics], - ) - }; - - // Create shielded partial tx - ShieldedPartialTransaction::build( - compliances, - input_resource_logics, - output_resource_logics, - vec![], - &mut rng, - ) - .unwrap() - }; - - // The second partial transaction: - // Alice consumes the intent resource and 3 "XAN"; - // Alice creates 2 "ETH" and 3 "XAN" to Bob - let ptx_2 = { - // Create compliance pairs - let compliances = { - let compliance_1 = ComplianceInfo::new( - cascade_intent_resource, - merkle_path.clone(), - Some(anchor), - &mut output_resource_2.resource, - &mut rng, - ); - - let compliance_2 = ComplianceInfo::new( - *input_resource_3.resource(), - merkle_path, - None, - &mut output_resource_3.resource, - &mut rng, - ); - vec![compliance_1, compliance_2] - }; - - // Create resource logics - let (input_resource_logics, output_resource_logics) = { - let input_resources = [cascade_intent_resource, *input_resource_3.resource()]; - let output_resources = [*output_resource_2.resource(), *output_resource_3.resource()]; - - // Create resource_logics for the intent - let intent_resource_logics = { - let intent_resource_logic = CascadeIntentResourceLogicCircuit { - owned_resource_id: cascade_intent_resource.get_nf().unwrap().inner(), - input_resources, - output_resources, - cascade_resource_cm: cascade_intent_resource.get_label(), - }; - - ResourceLogics::new(Box::new(intent_resource_logic), vec![]) - }; - - // Create resource logics for the input resource_3 - let input_resource_3_resource_logics = input_resource_3 - .generate_input_token_resource_logics( - &mut rng, - alice_auth, - alice_auth_sk, - input_resources, - output_resources, - ); - - // Create resource_logics for the output resource_2 - let output_resource_2_resource_logics = output_resource_2 - .generate_output_token_resource_logics( - &mut rng, - bob_auth, - input_resources, - output_resources, - ); - - // Create resource_logics for the output resource_3 - let output_resource_3_resource_logics = output_resource_3 - .generate_output_token_resource_logics( - &mut rng, - bob_auth, - input_resources, - output_resources, - ); - - ( - vec![intent_resource_logics, input_resource_3_resource_logics], - vec![ - output_resource_2_resource_logics, - output_resource_3_resource_logics, - ], - ) - }; - - // Create shielded partial tx - ShieldedPartialTransaction::build( - compliances, - input_resource_logics, - output_resource_logics, - vec![], - &mut rng, - ) - .unwrap() - }; - - // Create the final transaction - let shielded_tx_bundle = ShieldedPartialTxBundle::new(vec![ptx_1, ptx_2]); - let transparent_ptx_bundle = TransparentPartialTxBundle::default(); - Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle).unwrap() -} - -#[test] -fn test_cascade_ptxs() { - use rand::rngs::OsRng; - - let mut rng = OsRng; - let tx = create_transaction(&mut rng); - tx.execute().unwrap(); -} diff --git a/taiga_halo2/examples/tx_examples/main.rs b/taiga_halo2/examples/tx_examples/main.rs index cbb805f9..e0cf8fb7 100644 --- a/taiga_halo2/examples/tx_examples/main.rs +++ b/taiga_halo2/examples/tx_examples/main.rs @@ -1,4 +1,3 @@ -mod cascaded_partial_transactions; mod partial_fulfillment_token_swap; mod token; mod token_swap_with_intent; @@ -15,7 +14,4 @@ fn main() { let tx = partial_fulfillment_token_swap::create_token_swap_transaction(rng); tx.execute().unwrap(); - - let tx = cascaded_partial_transactions::create_transaction(rng); - tx.execute().unwrap(); } diff --git a/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs b/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs index 1e6adcbc..ec111dba 100644 --- a/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs +++ b/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs @@ -19,6 +19,7 @@ use taiga_halo2::{ merkle_tree::{Anchor, MerklePath}, nullifier::NullifierKeyContainer, resource::{Resource, ResourceLogics}, + resource_tree::{ResourceExistenceWitness, ResourceMerkleTreeLeaves}, shielded_ptx::ShieldedPartialTransaction, transaction::{ShieldedPartialTxBundle, Transaction, TransparentPartialTxBundle}, }; @@ -33,9 +34,6 @@ pub fn create_token_intent_ptx( let swap = Swap::random(&mut rng, sell, buy, input_auth); let mut intent_resource = swap.create_intent_resource(&mut rng); - // padding the zero resources - let padding_input_resource = Resource::random_padding_resource(&mut rng); - let mut padding_output_resource = Resource::random_padding_resource(&mut rng); let merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); // Create compliance pairs @@ -48,70 +46,54 @@ pub fn create_token_intent_ptx( &mut rng, ); - // Fetch a valid anchor for dummy resources - let anchor = Anchor::from(pallas::Base::random(&mut rng)); - let compliance_2 = ComplianceInfo::new( - padding_input_resource, - merkle_path, - Some(anchor), - &mut padding_output_resource, - &mut rng, - ); - vec![compliance_1, compliance_2] + vec![compliance_1] }; - // Create resource logics - let (input_resource_logics, output_resource_logics) = { - let input_resources = [*swap.sell.resource(), padding_input_resource]; - let output_resources = [intent_resource, padding_output_resource]; - // Create resource_logics for the input token - let input_token_resource_logics = swap.sell.generate_input_token_resource_logics( - &mut rng, - input_auth, - input_auth_sk, - input_resources, - output_resources, - ); + // Collect resource merkle leaves + let input_resource_nf = swap.sell.resource().get_nf().unwrap().inner(); + let output_resource_cm = intent_resource.commitment().inner(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![input_resource_nf, output_resource_cm]); + + // Create input resource logics + let input_merkle_path = resource_merkle_tree + .generate_path(input_resource_nf) + .unwrap(); + let input_resource_logics = swap.sell.generate_input_token_resource_logics( + &mut rng, + input_auth, + input_auth_sk, + input_merkle_path, + ); - // Create resource_logics for the intent - let intent_resource_logics = { - let intent_resource_logic = PartialFulfillmentIntentResourceLogicCircuit { - owned_resource_id: intent_resource.commitment().inner(), - input_resources, - output_resources, - swap: swap.clone(), - }; + // Create intent resource logics + let intent_resource_logics = { + let sell_resource_witness = + ResourceExistenceWitness::new(*swap.sell.resource(), input_merkle_path); - ResourceLogics::new(Box::new(intent_resource_logic), vec![]) + let intent_resource_witness = { + let merkle_path = resource_merkle_tree + .generate_path(output_resource_cm) + .unwrap(); + ResourceExistenceWitness::new(intent_resource, merkle_path) }; - // Create resource_logics for the padding input - let padding_input_resource_logics = - ResourceLogics::create_input_padding_resource_resource_logics( - &padding_input_resource, - input_resources, - output_resources, - ); - - // Create resource_logics the padding output - let padding_output_resource_logics = - ResourceLogics::create_output_padding_resource_resource_logics( - &padding_output_resource, - input_resources, - output_resources, - ); + let intent_circuit = PartialFulfillmentIntentResourceLogicCircuit { + self_resource: intent_resource_witness, + sell_resource: sell_resource_witness, + offer_resource: ResourceExistenceWitness::default(), // a dummy resource + returned_resource: ResourceExistenceWitness::default(), // a dummy resource + swap: swap.clone(), + }; - ( - vec![input_token_resource_logics, padding_input_resource_logics], - vec![intent_resource_logics, padding_output_resource_logics], - ) + ResourceLogics::new(Box::new(intent_circuit), vec![]) }; // Create shielded partial tx let ptx = ShieldedPartialTransaction::build( compliances, - input_resource_logics, - output_resource_logics, + vec![input_resource_logics], + vec![intent_resource_logics], vec![], &mut rng, ) @@ -128,9 +110,8 @@ pub fn consume_token_intent_ptx( offer: Token, output_auth_pk: pallas::Point, ) -> ShieldedPartialTransaction { - let (input_resources, [mut bought_resource, mut returned_resource]) = - swap.fill(&mut rng, intent_resource, offer); - let [intent_resource, padding_input_resource] = input_resources; + let (mut offer_resource, mut returned_resource) = swap.fill(&mut rng, offer); + let padding_input_resource = Resource::random_padding_resource(&mut rng); // output resources let output_auth = TokenAuthorization::new(output_auth_pk, *COMPRESSED_TOKEN_AUTH_VK); @@ -145,7 +126,7 @@ pub fn consume_token_intent_ptx( intent_resource, merkle_path.clone(), Some(anchor), - &mut bought_resource, + &mut offer_resource, &mut rng, ); @@ -159,40 +140,64 @@ pub fn consume_token_intent_ptx( vec![compliance_1, compliance_2] }; + let intent_nf = intent_resource.get_nf().unwrap().inner(); + let offer_cm = offer_resource.commitment().inner(); + let padding_nf = padding_input_resource.get_nf().unwrap().inner(); + let returned_cm = returned_resource.commitment().inner(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![intent_nf, offer_cm, padding_nf, returned_cm]); + // Create resource logics let (input_resource_logics, output_resource_logics) = { - let output_resources = [bought_resource, returned_resource]; + let intent_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(intent_nf).unwrap(); + ResourceExistenceWitness::new(intent_resource, merkle_path) + }; + + let offer_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(offer_cm).unwrap(); + ResourceExistenceWitness::new(offer_resource, merkle_path) + }; + + let padding_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(padding_nf).unwrap(); + ResourceExistenceWitness::new(padding_input_resource, merkle_path) + }; + + let returned_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(returned_cm).unwrap(); + ResourceExistenceWitness::new(returned_resource, merkle_path) + }; + // Create resource_logics for the intent let intent_resource_logics = { let intent_resource_logic = PartialFulfillmentIntentResourceLogicCircuit { - owned_resource_id: intent_resource.get_nf().unwrap().inner(), - input_resources, - output_resources, + self_resource: intent_resource_witness, + sell_resource: padding_resource_witness, // a dummy one + offer_resource: offer_resource_witness, + returned_resource: returned_resource_witness, swap: swap.clone(), }; ResourceLogics::new(Box::new(intent_resource_logic), vec![]) }; - // Create resource_logics for the bought_resource + // Create resource_logics for the offer_resource let bought_resource_resource_logics = TokenResource { token_name: swap.buy.name().clone(), - resource: bought_resource, + resource: offer_resource, } .generate_output_token_resource_logics( &mut rng, output_auth, - input_resources, - output_resources, + offer_resource_witness.get_path(), ); // Create resource_logics for the padding input - let padding_input_resource_logics = - ResourceLogics::create_input_padding_resource_resource_logics( - &padding_input_resource, - input_resources, - output_resources, - ); + let padding_input_resource_logics = ResourceLogics::create_padding_resource_resource_logics( + padding_input_resource, + padding_resource_witness.get_path(), + ); // Create resource_logics for the returned_resource let returned_resource_resource_logics = TokenResource { @@ -202,8 +207,7 @@ pub fn consume_token_intent_ptx( .generate_output_token_resource_logics( &mut rng, output_auth, - input_resources, - output_resources, + returned_resource_witness.get_path(), ); ( diff --git a/taiga_halo2/examples/tx_examples/token.rs b/taiga_halo2/examples/tx_examples/token.rs index 5a6c74bd..9352c8e1 100644 --- a/taiga_halo2/examples/tx_examples/token.rs +++ b/taiga_halo2/examples/tx_examples/token.rs @@ -1,5 +1,3 @@ -use halo2_proofs::arithmetic::Field; - use pasta_curves::pallas; use rand::RngCore; @@ -10,8 +8,8 @@ use taiga_halo2::{ }, compliance::ComplianceInfo, constant::TAIGA_COMMITMENT_TREE_DEPTH, - merkle_tree::{Anchor, MerklePath}, - resource::{Resource, ResourceLogics}, + merkle_tree::MerklePath, + resource_tree::ResourceMerkleTreeLeaves, shielded_ptx::ShieldedPartialTransaction, }; @@ -36,10 +34,6 @@ pub fn create_token_swap_ptx( let mut output_resource = output_token.create_random_output_token_resource(&mut rng, output_npk, &output_auth); - // padding the zero resources - let padding_input_resource = Resource::random_padding_resource(&mut rng); - let mut padding_output_resource = Resource::random_padding_resource(&mut rng); - // Generate proving info let merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); @@ -53,66 +47,42 @@ pub fn create_token_swap_ptx( &mut rng, ); - // Fetch a valid anchor for padding input resources - let anchor = Anchor::from(pallas::Base::random(&mut rng)); - let compliance_2 = ComplianceInfo::new( - padding_input_resource, - merkle_path, - Some(anchor), - &mut padding_output_resource, - &mut rng, - ); - vec![compliance_1, compliance_2] + vec![compliance_1] }; - // Create resource logics - let (input_resource_logics, output_resource_logics) = { - let input_resources = [*input_resource.resource(), padding_input_resource]; - let output_resources = [*output_resource.resource(), padding_output_resource]; - // Create resource_logics for the input token - let input_token_resource_logics = input_resource.generate_input_token_resource_logics( + // Collect resource merkle leaves + let input_resource_nf_1 = input_resource.get_nf().unwrap().inner(); + let output_resource_cm_1 = output_resource.commitment().inner(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![input_resource_nf_1, output_resource_cm_1]); + + // Create resource logics for the input resource + + let input_token_resource_logics = { + let merkle_path = resource_merkle_tree + .generate_path(input_resource_nf_1) + .unwrap(); + input_resource.generate_input_token_resource_logics( &mut rng, input_auth, input_auth_sk, - input_resources, - output_resources, - ); - - // Create resource logics for the output token - let output_token_resource_logics = output_resource.generate_output_token_resource_logics( - &mut rng, - output_auth, - input_resources, - output_resources, - ); - - // Create resource logics for the padding input - let padding_input_resource_logics = - ResourceLogics::create_input_padding_resource_resource_logics( - &padding_input_resource, - input_resources, - output_resources, - ); - - // Create resource logics for the padding output - let padding_output_resource_logics = - ResourceLogics::create_output_padding_resource_resource_logics( - &padding_output_resource, - input_resources, - output_resources, - ); - - ( - vec![input_token_resource_logics, padding_input_resource_logics], - vec![output_token_resource_logics, padding_output_resource_logics], + merkle_path, ) }; + // Create resource logics for the output resource + let output_token_resource_logics = { + let merkle_path = resource_merkle_tree + .generate_path(output_resource_cm_1) + .unwrap(); + output_resource.generate_output_token_resource_logics(&mut rng, output_auth, merkle_path) + }; + // Create shielded partial tx ShieldedPartialTransaction::build( compliances, - input_resource_logics, - output_resource_logics, + vec![input_token_resource_logics], + vec![output_token_resource_logics], vec![], &mut rng, ) diff --git a/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs b/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs index f7dbcaac..4fc92f56 100644 --- a/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs +++ b/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs @@ -18,7 +18,8 @@ use taiga_halo2::{ constant::TAIGA_COMMITMENT_TREE_DEPTH, merkle_tree::{Anchor, MerklePath}, nullifier::NullifierKeyContainer, - resource::{Resource, ResourceLogics}, + resource::ResourceLogics, + resource_tree::{ResourceExistenceWitness, ResourceMerkleTreeLeaves}, shielded_ptx::ShieldedPartialTransaction, transaction::{ShieldedPartialTxBundle, Transaction, TransparentPartialTxBundle}, }; @@ -53,10 +54,6 @@ pub fn create_token_intent_ptx( input_nk, ); - // padding the zero resources - let padding_input_resource = Resource::random_padding_resource(&mut rng); - let mut padding_output_resource = Resource::random_padding_resource(&mut rng); - let merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); // Create compliance pairs @@ -69,79 +66,51 @@ pub fn create_token_intent_ptx( &mut rng, ); - // Fetch a valid anchor for padding input resources - let anchor = Anchor::from(pallas::Base::random(&mut rng)); - let compliance_2 = ComplianceInfo::new( - padding_input_resource, - merkle_path, - Some(anchor), - &mut padding_output_resource, - &mut rng, - ); - vec![compliance_1, compliance_2] + vec![compliance_1] }; - // Create resource logics - let (input_resource_logics, output_resource_logics) = { - let input_resources = [*input_resource.resource(), padding_input_resource]; - let output_resources = [intent_resource, padding_output_resource]; - // Create resource_logics for the input resource - let input_resource_resource_logics = input_resource.generate_input_token_resource_logics( + // Collect resource merkle leaves + let input_resource_nf = input_resource.get_nf().unwrap().inner(); + let output_resource_cm = intent_resource.commitment().inner(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![input_resource_nf, output_resource_cm]); + + // Create resource logics for the input resource + let input_resource_resource_logics = { + let merkle_path = resource_merkle_tree + .generate_path(input_resource_nf) + .unwrap(); + input_resource.generate_input_token_resource_logics( &mut rng, input_auth, input_auth_sk, - input_resources, - output_resources, - ); - - // Create resource logics for the intent resource - let intent_resource_resource_logics = { - let intent_resource_logic = OrRelationIntentResourceLogicCircuit { - owned_resource_id: intent_resource.commitment().inner(), - input_resources, - output_resources, - token_1, - token_2, - receiver_npk: input_resource_npk, - receiver_value: input_resource.value, - }; + merkle_path, + ) + }; - ResourceLogics::new(Box::new(intent_resource_logic), vec![]) + // Create resource logics for the intent(output) resource + let output_resource_resource_logics = { + let merkle_path = resource_merkle_tree + .generate_path(output_resource_cm) + .unwrap(); + let intent_resource_witness = ResourceExistenceWitness::new(intent_resource, merkle_path); + let circuit = OrRelationIntentResourceLogicCircuit { + self_resource: intent_resource_witness, + // the desired resource won't be checked. + desired_resource: intent_resource_witness, + token_1, + token_2, + receiver_npk: input_resource_npk, + receiver_value: input_resource.value, }; - - // Create resource logics for the padding input - let padding_input_resource_logics = - ResourceLogics::create_input_padding_resource_resource_logics( - &padding_input_resource, - input_resources, - output_resources, - ); - - // Create resource_logics for the padding output - let padding_output_resource_logics = - ResourceLogics::create_output_padding_resource_resource_logics( - &padding_output_resource, - input_resources, - output_resources, - ); - - ( - vec![ - input_resource_resource_logics, - padding_input_resource_logics, - ], - vec![ - intent_resource_resource_logics, - padding_output_resource_logics, - ], - ) + ResourceLogics::new(Box::new(circuit), vec![]) }; // Create shielded partial tx let ptx = ShieldedPartialTransaction::build( compliances, - input_resource_logics, - output_resource_logics, + vec![input_resource_resource_logics], + vec![output_resource_resource_logics], vec![], &mut rng, ) @@ -172,95 +141,65 @@ pub fn consume_token_intent_ptx( ); // output resource - let input_resource_nf = intent_resource.get_nf().unwrap(); let output_auth = TokenAuthorization::new(output_auth_pk, *COMPRESSED_TOKEN_AUTH_VK); let output_npk = NullifierKeyContainer::from_key(input_nk).get_npk(); let mut output_resource = output_token.create_random_output_token_resource(&mut rng, output_npk, &output_auth); - // padding the zero resources - let padding_input_resource = Resource::random_padding_resource(&mut rng); - let mut padding_output_resource = Resource::random_padding_resource(&mut rng); - let merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); // Fetch a valid anchor for dummy resources let anchor = Anchor::from(pallas::Base::random(&mut rng)); - // Create compliance pairs - let compliances = { - let compliance_1 = ComplianceInfo::new( - intent_resource, - merkle_path.clone(), - Some(anchor), - &mut output_resource.resource, - &mut rng, - ); - - let compliance_2 = ComplianceInfo::new( - padding_input_resource, - merkle_path, - Some(anchor), - &mut padding_output_resource, - &mut rng, - ); - vec![compliance_1, compliance_2] - }; - - // Create resource logics - let (input_resource_logics, output_resource_logics) = { - let input_resources = [intent_resource, padding_input_resource]; - let output_resources = [*output_resource.resource(), padding_output_resource]; - // Create resource_logics for the intent - let intent_resource_logics = { - let intent_resource_logic = OrRelationIntentResourceLogicCircuit { - owned_resource_id: input_resource_nf.inner(), - input_resources, - output_resources, - token_1, - token_2, - receiver_npk, - receiver_value, - }; + // Create compliance proof + let compliance = ComplianceInfo::new( + intent_resource, + merkle_path.clone(), + Some(anchor), + &mut output_resource.resource, + &mut rng, + ); - ResourceLogics::new(Box::new(intent_resource_logic), vec![]) + let input_resource_nf = intent_resource.get_nf().unwrap().inner(); + let output_resource_cm = output_resource.commitment().inner(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![input_resource_nf, output_resource_cm]); + + let output_merkle_path = resource_merkle_tree + .generate_path(output_resource_cm) + .unwrap(); + + // Create resource logics for the intent(input) resource + let intent_resource_logics = { + let merkle_path = resource_merkle_tree + .generate_path(input_resource_nf) + .unwrap(); + let intent_resource_logic = OrRelationIntentResourceLogicCircuit { + self_resource: ResourceExistenceWitness::new(intent_resource, merkle_path), + desired_resource: ResourceExistenceWitness::new( + output_resource.resource, + output_merkle_path, + ), + token_1, + token_2, + receiver_npk, + receiver_value, }; - // Create resource logics for the output token resource - let output_token_resource_logics = output_resource.generate_output_token_resource_logics( - &mut rng, - output_auth, - input_resources, - output_resources, - ); - - // Create resource_logics for the padding input - let padding_input_resource_logics = - ResourceLogics::create_input_padding_resource_resource_logics( - &padding_input_resource, - input_resources, - output_resources, - ); - - // Create resource_logics for the padding output - let padding_output_resource_logics = - ResourceLogics::create_output_padding_resource_resource_logics( - &padding_output_resource, - input_resources, - output_resources, - ); - - ( - vec![intent_resource_logics, padding_input_resource_logics], - vec![output_token_resource_logics, padding_output_resource_logics], - ) + ResourceLogics::new(Box::new(intent_resource_logic), vec![]) }; + let output_resource_logics = output_resource.generate_output_token_resource_logics( + &mut rng, + output_auth, + output_merkle_path, + ); + // Create shielded partial tx ShieldedPartialTransaction::build( - compliances, - input_resource_logics, - output_resource_logics, + vec![compliance], + vec![intent_resource_logics], + vec![output_resource_logics], vec![], &mut rng, ) diff --git a/taiga_halo2/src/circuit/compliance_circuit.rs b/taiga_halo2/src/circuit/compliance_circuit.rs index 3af45f1c..cdca0849 100644 --- a/taiga_halo2/src/circuit/compliance_circuit.rs +++ b/taiga_halo2/src/circuit/compliance_circuit.rs @@ -241,9 +241,9 @@ impl Circuit for ComplianceCircuit { input_resource_variables.resource_variables.logic.clone(), input_resource_variables.resource_variables.label.clone(), input_resource_variables.resource_variables.quantity.clone(), - output_resource_vars.resource_variables.logic.clone(), - output_resource_vars.resource_variables.label.clone(), - output_resource_vars.resource_variables.quantity, + output_resource_vars.logic.clone(), + output_resource_vars.label.clone(), + output_resource_vars.quantity, self.rcv, )?; layouter.constrain_instance( @@ -309,7 +309,7 @@ impl Circuit for ComplianceCircuit { let output_resource_logic_commitment = resource_logic_commitment_gadget( &mut layouter, &blake2s_chip, - output_resource_vars.resource_variables.logic.clone(), + output_resource_vars.logic.clone(), output_resource_logic_cm_r, )?; layouter.constrain_instance( diff --git a/taiga_halo2/src/circuit/gadgets.rs b/taiga_halo2/src/circuit/gadgets.rs index d386314c..996f7ad8 100644 --- a/taiga_halo2/src/circuit/gadgets.rs +++ b/taiga_halo2/src/circuit/gadgets.rs @@ -11,7 +11,6 @@ pub mod extended_or_relation; pub mod mul; pub mod poseidon_hash; pub mod sub; -pub mod target_resource_variable; pub mod triple_mul; pub fn assign_free_advice( diff --git a/taiga_halo2/src/circuit/gadgets/target_resource_variable.rs b/taiga_halo2/src/circuit/gadgets/target_resource_variable.rs deleted file mode 100644 index 1fef6b2f..00000000 --- a/taiga_halo2/src/circuit/gadgets/target_resource_variable.rs +++ /dev/null @@ -1,350 +0,0 @@ -use crate::circuit::resource_logic_circuit::ResourceSearchableVariablePair; -use crate::constant::NUM_RESOURCE; -use halo2_gadgets::utilities::bool_check; -use halo2_proofs::{ - arithmetic::Field, - circuit::{AssignedCell, Layouter, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector}, - poly::Rotation, -}; -use pasta_curves::pallas; - -// Search and get owned resource variable -pub fn get_owned_resource_variable( - config: GetOwnedResourceVariableConfig, - mut layouter: impl Layouter, - // The owned_resource_id is the input_resource_nf or the output_resource_cm_x - owned_resource_id: &AssignedCell, - // NUM_RESOURCE pairs are from input resources, the other NUM_RESOURCE are from output resources - resource_variable_pairs: &[ResourceSearchableVariablePair; NUM_RESOURCE * 2], -) -> Result, Error> { - layouter.assign_region( - || "get owned_resource_variable", - |mut region| { - config.assign_region(owned_resource_id, resource_variable_pairs, 0, &mut region) - }, - ) -} - -// Search and get is_input_resource_flag variable -pub fn get_is_input_resource_flag( - config: GetIsInputResourceFlagConfig, - mut layouter: impl Layouter, - // The owned_resource_id is the input_resource_nf or the output_resource_cm_x - owned_resource_id: &AssignedCell, - input_resource_nfs: &[AssignedCell; NUM_RESOURCE], - output_resource_cms: &[AssignedCell; NUM_RESOURCE], -) -> Result, Error> { - layouter.assign_region( - || "get is_input_resource_flag", - |mut region| { - config.assign_region( - owned_resource_id, - input_resource_nfs, - output_resource_cms, - 0, - &mut region, - ) - }, - ) -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct GetOwnedResourceVariableConfig { - q_get_owned_resource_variable: Selector, - owned_resource_id: Column, - resource_variable_pairs: [Column; NUM_RESOURCE * 2], -} - -impl GetOwnedResourceVariableConfig { - #[allow(clippy::too_many_arguments)] - pub fn configure( - meta: &mut ConstraintSystem, - owned_resource_id: Column, - resource_variable_pairs: [Column; NUM_RESOURCE * 2], - ) -> Self { - let config = Self { - q_get_owned_resource_variable: meta.selector(), - owned_resource_id, - resource_variable_pairs, - }; - - config.create_gate(meta); - - config - } - - fn create_gate(&self, meta: &mut ConstraintSystem) { - meta.create_gate("get owned resource variable", |meta| { - let q_get_owned_resource_variable = - meta.query_selector(self.q_get_owned_resource_variable); - let owned_resource_id = meta.query_advice(self.owned_resource_id, Rotation::cur()); - let owned_resource_variable = - meta.query_advice(self.owned_resource_id, Rotation::next()); - let nf_or_cm_vec: Vec> = self - .resource_variable_pairs - .into_iter() - .map(|column| meta.query_advice(column, Rotation::cur())) - .collect(); - let target_variable_vec: Vec> = self - .resource_variable_pairs - .into_iter() - .map(|column| meta.query_advice(column, Rotation::next())) - .collect(); - let inv_vec: Vec> = self - .resource_variable_pairs - .into_iter() - .map(|column| meta.query_advice(column, Rotation::prev())) - .collect(); - let nf_or_cm_minus_owned_resource_id_vec: Vec> = - nf_or_cm_vec - .into_iter() - .map(|nf_or_cm| nf_or_cm - owned_resource_id.clone()) - .collect(); - let one = Expression::Constant(pallas::Base::one()); - let nf_or_cm_minus_owned_resource_id_is_zero_vec: Vec> = - nf_or_cm_minus_owned_resource_id_vec - .clone() - .into_iter() - .zip(inv_vec) - .map(|(nf_or_cm_minus_owned_resource_id, inv)| { - one.clone() - nf_or_cm_minus_owned_resource_id * inv - }) - .collect(); - let poly_vec: Vec> = nf_or_cm_minus_owned_resource_id_vec - .into_iter() - .zip(nf_or_cm_minus_owned_resource_id_is_zero_vec.clone()) - .map(|(nf_or_cm_minus_owned_resource_id, is_zero)| { - nf_or_cm_minus_owned_resource_id * is_zero - }) - .collect(); - - Constraints::with_selector( - q_get_owned_resource_variable, - [ - ( - "nf_or_cm_minus_owned_resource_id_is_zero check0", - poly_vec[0].clone(), - ), - ( - "nf_or_cm_minus_owned_resource_id_is_zero check1", - poly_vec[1].clone(), - ), - ( - "nf_or_cm_minus_owned_resource_id_is_zero check2", - poly_vec[2].clone(), - ), - ( - "nf_or_cm_minus_owned_resource_id_is_zero check3", - poly_vec[3].clone(), - ), - ( - "owned_resource_variable check0", - nf_or_cm_minus_owned_resource_id_is_zero_vec[0].clone() - * (owned_resource_variable.clone() - target_variable_vec[0].clone()), - ), - ( - "owned_resource_variable check1", - nf_or_cm_minus_owned_resource_id_is_zero_vec[1].clone() - * (owned_resource_variable.clone() - target_variable_vec[1].clone()), - ), - ( - "owned_resource_variable check2", - nf_or_cm_minus_owned_resource_id_is_zero_vec[2].clone() - * (owned_resource_variable.clone() - target_variable_vec[2].clone()), - ), - ( - "owned_resource_variable check3", - nf_or_cm_minus_owned_resource_id_is_zero_vec[3].clone() - * (owned_resource_variable - target_variable_vec[3].clone()), - ), - ( - "owned_resource_id exists in the resources", - nf_or_cm_minus_owned_resource_id_is_zero_vec[0].clone() - * nf_or_cm_minus_owned_resource_id_is_zero_vec[1].clone() - * nf_or_cm_minus_owned_resource_id_is_zero_vec[2].clone() - * nf_or_cm_minus_owned_resource_id_is_zero_vec[3].clone(), - ), - ], - ) - }); - } - - pub fn assign_region( - &self, - owned_resource_id: &AssignedCell, - resource_variable_pairs: &[ResourceSearchableVariablePair; NUM_RESOURCE * 2], - offset: usize, - region: &mut Region<'_, pallas::Base>, - ) -> Result, Error> { - // Enable `q_get_owned_resource_variable` selector - self.q_get_owned_resource_variable - .enable(region, offset + 1)?; - - // copy owned_resource_id, resource_variable_pairs into the advice columns - owned_resource_id.copy_advice( - || "owned_resource_id", - region, - self.owned_resource_id, - offset + 1, - )?; - - let mut ret = Value::known(pallas::Base::zero()); - for (pair, column) in resource_variable_pairs - .iter() - .zip(self.resource_variable_pairs) - { - pair.src_variable - .copy_advice(|| "nf or cm", region, column, offset + 1)?; - pair.target_variable - .copy_advice(|| "target_variable", region, column, offset + 2)?; - let inv = pair - .src_variable - .value() - .zip(owned_resource_id.value()) - .map(|(nf_or_cm, owned_resource_id)| { - let inv = (nf_or_cm - owned_resource_id) - .invert() - .unwrap_or(pallas::Base::zero()); - - // Find the target variable - if inv == pallas::Base::zero() { - ret = pair.target_variable.value().copied(); - } - inv - }); - region.assign_advice(|| "inv", column, offset, || inv)?; - } - region.assign_advice(|| "ret", self.owned_resource_id, offset + 2, || ret) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct GetIsInputResourceFlagConfig { - q_get_is_input_resource_flag: Selector, - owned_resource_id: Column, - input_resource_nf: Column, - output_resource_cm: Column, -} - -impl GetIsInputResourceFlagConfig { - #[allow(clippy::too_many_arguments)] - pub fn configure( - meta: &mut ConstraintSystem, - owned_resource_id: Column, - input_resource_nf: Column, - output_resource_cm: Column, - ) -> Self { - meta.enable_equality(owned_resource_id); - meta.enable_equality(input_resource_nf); - meta.enable_equality(output_resource_cm); - - let config = Self { - q_get_is_input_resource_flag: meta.selector(), - owned_resource_id, - input_resource_nf, - output_resource_cm, - }; - - config.create_gate(meta); - - config - } - - fn create_gate(&self, meta: &mut ConstraintSystem) { - meta.create_gate("get is_input_resource_flag", |meta| { - let q_get_is_input_resource_flag = meta.query_selector(self.q_get_is_input_resource_flag); - let owned_resource_id = meta.query_advice(self.owned_resource_id, Rotation::cur()); - let is_input_resource_flag = meta.query_advice(self.owned_resource_id, Rotation::next()); - let input_resource_nf_1 = meta.query_advice(self.input_resource_nf, Rotation::cur()); - let input_resource_nf_2 = meta.query_advice(self.input_resource_nf, Rotation::next()); - let output_resource_cm_1 = - meta.query_advice(self.output_resource_cm, Rotation::cur()); - let output_resource_cm_2 = - meta.query_advice(self.output_resource_cm, Rotation::next()); - let one = Expression::Constant(pallas::Base::one()); - - Constraints::with_selector( - q_get_is_input_resource_flag, - [ - ("bool_check is_input_resource_flag", bool_check(is_input_resource_flag.clone())), - ( - "if is_input_resource_flag, then owned_resource_id == input_resource_nf_1 or owned_resource_id == input_resource_nf_2", - is_input_resource_flag.clone() * (owned_resource_id.clone() - input_resource_nf_1) * (owned_resource_id.clone() - input_resource_nf_2), - ), - ( - "if not is_input_resource_flag, then owned_resource_id == output_resource_cm_1 or owned_resource_id == output_resource_cm_2", - (is_input_resource_flag - one) * (owned_resource_id.clone() - output_resource_cm_1) * (owned_resource_id - output_resource_cm_2), - ), - ], - ) - }); - } - - pub fn assign_region( - &self, - owned_resource_id: &AssignedCell, - input_resource_nfs: &[AssignedCell; NUM_RESOURCE], - output_resource_cms: &[AssignedCell; NUM_RESOURCE], - offset: usize, - region: &mut Region<'_, pallas::Base>, - ) -> Result, Error> { - // Enable `q_get_is_input_resource_flag` selector - self.q_get_is_input_resource_flag.enable(region, offset)?; - - // copy owned_resource_id, input_resource_nfs and output_resource_cms into the advice columns - owned_resource_id.copy_advice( - || "owned_resource_id", - region, - self.owned_resource_id, - offset, - )?; - input_resource_nfs[0].copy_advice( - || "input_resource_nf 1", - region, - self.input_resource_nf, - offset, - )?; - input_resource_nfs[1].copy_advice( - || "input_resource_nf 2", - region, - self.input_resource_nf, - offset + 1, - )?; - output_resource_cms[0].copy_advice( - || "output_resource_cm 1", - region, - self.output_resource_cm, - offset, - )?; - output_resource_cms[1].copy_advice( - || "output_resource_cm 2", - region, - self.output_resource_cm, - offset + 1, - )?; - - // compute the is_input_resource_flag - let is_input_resource_flag = owned_resource_id - .value() - .zip(input_resource_nfs[0].value()) - .zip(input_resource_nfs[1].value()) - .map( - |((&owned_resource_id, &input_resource_nf_1), &input_resource_nf_2)| { - if owned_resource_id == input_resource_nf_1 - || owned_resource_id == input_resource_nf_2 - { - pallas::Base::one() - } else { - pallas::Base::zero() - } - }, - ); - region.assign_advice( - || "is_input_resource_flag", - self.owned_resource_id, - offset + 1, - || is_input_resource_flag, - ) - } -} diff --git a/taiga_halo2/src/circuit/integrity.rs b/taiga_halo2/src/circuit/integrity.rs index a40da119..ad11343d 100644 --- a/taiga_halo2/src/circuit/integrity.rs +++ b/taiga_halo2/src/circuit/integrity.rs @@ -1,14 +1,19 @@ use crate::circuit::{ - gadgets::{assign_free_advice, assign_free_constant, poseidon_hash::poseidon_hash_gadget}, + gadgets::{ + assign_free_advice, assign_free_constant, conditional_select::ConditionalSelectConfig, + poseidon_hash::poseidon_hash_gadget, + }, hash_to_curve::{hash_to_curve_circuit, HashToCurveConfig}, + merkle_circuit::{merkle_poseidon_gadget, MerklePoseidonChip}, resource_commitment::{resource_commit, ResourceCommitChip}, - resource_logic_circuit::{InputResourceVariables, OutputResourceVariables, ResourceVariables}, + resource_logic_circuit::{InputResourceVariables, ResourceStatus, ResourceVariables}, }; use crate::constant::{ TaigaFixedBases, TaigaFixedBasesFull, POSEIDON_TO_CURVE_INPUT_LEN, PRF_EXPAND_PERSONALIZATION_TO_FIELD, PRF_EXPAND_PSI, PRF_EXPAND_RCM, }; use crate::resource::Resource; +use crate::resource_tree::ResourceExistenceWitness; use crate::utils::poseidon_to_curve; use halo2_gadgets::{ ecc::{chip::EccChip, FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarVar}, @@ -193,7 +198,7 @@ pub fn check_output_resource( output_resource: Resource, old_nf: AssignedCell, cm_row_idx: usize, -) -> Result { +) -> Result { // Witness npk let npk = assign_free_advice( layouter.namespace(|| "witness npk"), @@ -307,7 +312,7 @@ pub fn check_output_resource( // Public cm layouter.constrain_instance(cm.cell(), instances, cm_row_idx)?; - let resource_variables = ResourceVariables { + Ok(ResourceVariables { logic, label, quantity, @@ -316,11 +321,193 @@ pub fn check_output_resource( nonce: old_nf, npk, rseed, + }) +} + +#[allow(clippy::too_many_arguments)] +pub fn load_resource( + mut layouter: impl Layouter, + advices: [Column; 10], + resource_commit_chip: ResourceCommitChip, + conditional_select_config: ConditionalSelectConfig, + merkle_chip: MerklePoseidonChip, + resource_witness: &ResourceExistenceWitness, +) -> Result { + let resource = resource_witness.get_resource(); + let merkle_path = resource_witness.get_path(); + let is_input = resource_witness.is_input(); + + // Witness is_input + let is_input_var = assign_free_advice( + layouter.namespace(|| "witness is_input"), + advices[0], + Value::known(pallas::Base::from(is_input)), + )?; + + // Witness nk or npk + let nk_or_npk = if is_input { + resource.get_nk().unwrap() + } else { + resource.get_npk() }; - Ok(OutputResourceVariables { - resource_variables, - cm, + let nk_or_npk_var = assign_free_advice( + layouter.namespace(|| "witness nk_or_npk"), + advices[0], + Value::known(nk_or_npk), + )?; + + let zero_constant = assign_free_constant( + layouter.namespace(|| "constant zero"), + advices[0], + pallas::Base::zero(), + )?; + + // npk = Com_r(nk, zero) + let input_npk = poseidon_hash_gadget( + resource_commit_chip.get_poseidon_config(), + layouter.namespace(|| "npk encoding"), + [nk_or_npk_var.clone(), zero_constant], + )?; + + let npk = layouter.assign_region( + || "conditional select: npk", + |mut region| { + conditional_select_config.assign_region( + &is_input_var, + &input_npk, + &nk_or_npk_var, + 0, + &mut region, + ) + }, + )?; + + // Witness value + let value = assign_free_advice( + layouter.namespace(|| "witness value"), + advices[0], + Value::known(resource.value), + )?; + + // Witness logic + let logic = assign_free_advice( + layouter.namespace(|| "witness logic"), + advices[0], + Value::known(resource.get_logic()), + )?; + + // Witness label + let label = assign_free_advice( + layouter.namespace(|| "witness label"), + advices[0], + Value::known(resource.get_label()), + )?; + + // Witness and range check the quantity(u64) + let quantity = quantity_range_check( + layouter.namespace(|| "quantity range check"), + resource_commit_chip.get_lookup_config(), + resource.quantity, + )?; + + // Witness nonce + let nonce = assign_free_advice( + layouter.namespace(|| "witness nonce"), + advices[0], + Value::known(resource.nonce.inner()), + )?; + + // Witness rseed + let rseed = assign_free_advice( + layouter.namespace(|| "witness rseed"), + advices[0], + Value::known(resource.rseed), + )?; + + // We don't need the constraints on psi and rcm derivation for input resource. + // If the psi and rcm are not correct, the existence checking would fail. + // Witness psi + let psi = assign_free_advice( + layouter.namespace(|| "witness psi_input"), + advices[0], + Value::known(resource.get_psi()), + )?; + + // Witness rcm + let rcm = assign_free_advice( + layouter.namespace(|| "witness rcm"), + advices[0], + Value::known(resource.get_rcm()), + )?; + + // Witness is_ephemeral + // is_ephemeral will be boolean-constrained in the resource_commit. + let is_ephemeral = assign_free_advice( + layouter.namespace(|| "witness is_ephemeral"), + advices[0], + Value::known(pallas::Base::from(resource.is_ephemeral)), + )?; + + // Check resource commitment + let cm = resource_commit( + layouter.namespace(|| "resource commitment"), + resource_commit_chip.clone(), + logic.clone(), + label.clone(), + value.clone(), + npk.clone(), + nonce.clone(), + psi.clone(), + quantity.clone(), + is_ephemeral.clone(), + rcm.clone(), + )?; + + // Generate the nullifier if the resource is an input + let nf = nullifier_circuit( + layouter.namespace(|| "Generate nullifier"), + resource_commit_chip.get_poseidon_config(), + nk_or_npk_var, + nonce.clone(), + psi.clone(), + cm.clone(), + )?; + + // The self_id is the nullifier if the resource is an input, otherwise it's + // the commitment + let self_id = layouter.assign_region( + || "conditional select: nullifier or commitment", + |mut region| { + conditional_select_config.assign_region(&is_input_var, &nf, &cm, 0, &mut region) + }, + )?; + + // Check resource existence(merkle path) + // TODO: constrain the first LR(is_input) + let root = merkle_poseidon_gadget( + layouter.namespace(|| "poseidon merkle"), + merkle_chip, + self_id.clone(), + &merkle_path, + )?; + + let resource_variables = ResourceVariables { + logic, + quantity, + label, + is_ephemeral, + value, + nonce, + npk, + rseed, + }; + + Ok(ResourceStatus { + resource_merkle_root: root, + is_input: is_input_var, + identity: self_id, + resource: resource_variables, }) } diff --git a/taiga_halo2/src/circuit/merkle_circuit.rs b/taiga_halo2/src/circuit/merkle_circuit.rs index 8c4e7428..bd5ac064 100644 --- a/taiga_halo2/src/circuit/merkle_circuit.rs +++ b/taiga_halo2/src/circuit/merkle_circuit.rs @@ -1,5 +1,5 @@ use crate::circuit::gadgets::poseidon_hash::poseidon_hash_gadget; -use crate::merkle_tree::{is_left, LR}; +use crate::merkle_tree::LR; use halo2_gadgets::{ poseidon::Pow5Config as PoseidonConfig, utilities::cond_swap::{CondSwapChip, CondSwapConfig, CondSwapInstructions}, @@ -91,7 +91,7 @@ pub fn merkle_poseidon_gadget( &chip, layouter.namespace(|| "merkle swap"), pair, - Value::known(is_left(e.1)), + Value::known(e.1.is_left()), )? }; @@ -184,7 +184,7 @@ fn test_halo2_merkle_circuit() { layouter.namespace(|| "poseidon merkle"), merkle_chip, leaf, - &self.merkle_path.get_path(), + &self.merkle_path.inner(), )?; let expected_root = { diff --git a/taiga_halo2/src/circuit/resource_logic_bytecode.rs b/taiga_halo2/src/circuit/resource_logic_bytecode.rs index d8201921..c5a3bb26 100644 --- a/taiga_halo2/src/circuit/resource_logic_bytecode.rs +++ b/taiga_halo2/src/circuit/resource_logic_bytecode.rs @@ -2,7 +2,6 @@ use crate::circuit::resource_logic_examples::TrivialResourceLogicCircuit; #[cfg(feature = "examples")] use crate::circuit::resource_logic_examples::{ - cascade_intent::CascadeIntentResourceLogicCircuit, or_relation_intent::OrRelationIntentResourceLogicCircuit, partial_fulfillment_intent::PartialFulfillmentIntentResourceLogicCircuit, receiver_resource_logic::ReceiverResourceLogicCircuit, @@ -16,14 +15,9 @@ use crate::{ ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, VampIRResourceLogicCircuit, }, constant::{ - RESOURCE_LOGIC_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_OWNED_RESOURCE_ID_PUBLIC_INPUT_IDX, + RESOURCE_LOGIC_CIRCUIT_RESOURCE_MERKLE_ROOT_IDX, + RESOURCE_LOGIC_CIRCUIT_SELF_RESOURCE_ID_IDX, }, - nullifier::Nullifier, - resource::ResourceCommitment, }; #[cfg(feature = "borsh")] @@ -118,21 +112,15 @@ impl ResourceLogicByteCode { let resource_logic = OrRelationIntentResourceLogicCircuit::from_bytes(&self.inputs); Ok(resource_logic.get_verifying_info()) } - #[cfg(feature = "examples")] - ResourceLogicRepresentation::CascadeIntent => { - let resource_logic = CascadeIntentResourceLogicCircuit::from_bytes(&self.inputs); - Ok(resource_logic.get_verifying_info()) - } #[allow(unreachable_patterns)] _ => Err(TransactionError::InvalidResourceLogicRepresentation), } } - // Verify resource_logic circuit transparently and return owned resource PubID for further checking + // Verify resource_logic circuit transparently and return self resource id for further checking pub fn verify_transparently( &self, - compliance_nfs: &[Nullifier], - compliance_cms: &[ResourceCommitment], + compliance_resource_merkle_root: &pallas::Base, ) -> Result { // check resource logic transparently let public_inputs = match &self.circuit { @@ -180,45 +168,19 @@ impl ResourceLogicByteCode { let resource_logic = OrRelationIntentResourceLogicCircuit::from_bytes(&self.inputs); resource_logic.verify_transparently()? } - #[cfg(feature = "examples")] - ResourceLogicRepresentation::CascadeIntent => { - let resource_logic = CascadeIntentResourceLogicCircuit::from_bytes(&self.inputs); - resource_logic.verify_transparently()? - } #[allow(unreachable_patterns)] _ => return Err(TransactionError::InvalidResourceLogicRepresentation), }; - // check nullifiers - // Check the resource_logic actually uses the input resources from compliance circuits. - let resource_logic_nfs = [ - public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX), - public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX), - ]; - - if !((compliance_nfs[0].inner() == resource_logic_nfs[0] - && compliance_nfs[1].inner() == resource_logic_nfs[1]) - || (compliance_nfs[0].inner() == resource_logic_nfs[1] - && compliance_nfs[1].inner() == resource_logic_nfs[0])) - { - return Err(TransactionError::InconsistentNullifier); - } + // check resource merkle root + let logic_resource_merkle_root = + public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_RESOURCE_MERKLE_ROOT_IDX); - // check resource_commitments - // Check the resource_logic actually uses the output resources from compliance circuits. - let resource_logic_cms = [ - public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX), - public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX), - ]; - if !((compliance_cms[0].inner() == resource_logic_cms[0] - && compliance_cms[1].inner() == resource_logic_cms[1]) - || (compliance_cms[0].inner() == resource_logic_cms[1] - && compliance_cms[1].inner() == resource_logic_cms[0])) - { - return Err(TransactionError::InconsistentOutputResourceCommitment); + if logic_resource_merkle_root != *compliance_resource_merkle_root { + return Err(TransactionError::InconsistentResourceMerkleRoot); } - Ok(public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_OWNED_RESOURCE_ID_PUBLIC_INPUT_IDX)) + Ok(public_inputs.get_from_index(RESOURCE_LOGIC_CIRCUIT_SELF_RESOURCE_ID_IDX)) } } @@ -251,19 +213,18 @@ impl ApplicationByteCode { // Verify resource_logic circuits transparently and return owned resource PubID for further checking pub fn verify_transparently( &self, - compliance_nfs: &[Nullifier], - compliance_cms: &[ResourceCommitment], + compliance_root: &pallas::Base, ) -> Result { - let owned_resource_id = self + let self_resource_id = self .app_resource_logic_bytecode - .verify_transparently(compliance_nfs, compliance_cms)?; + .verify_transparently(compliance_root)?; for dynamic_resource_logic in self.dynamic_resource_logic_bytecode.iter() { - let id = dynamic_resource_logic.verify_transparently(compliance_nfs, compliance_cms)?; + let id = dynamic_resource_logic.verify_transparently(compliance_root)?; // check: the app_resource_logic and dynamic_resource_logics belong to the resource - if id != owned_resource_id { - return Err(TransactionError::InconsistentOwnedResourceID); + if id != self_resource_id { + return Err(TransactionError::InconsistentSelfResourceID); } } - Ok(owned_resource_id) + Ok(self_resource_id) } } diff --git a/taiga_halo2/src/circuit/resource_logic_circuit.rs b/taiga_halo2/src/circuit/resource_logic_circuit.rs index 2782350c..4e6f2fa3 100644 --- a/taiga_halo2/src/circuit/resource_logic_circuit.rs +++ b/taiga_halo2/src/circuit/resource_logic_circuit.rs @@ -4,37 +4,32 @@ use crate::{ blake2s::Blake2sConfig, gadgets::{ add::{AddChip, AddConfig}, - assign_free_advice, conditional_equal::ConditionalEqualConfig, conditional_select::ConditionalSelectConfig, extended_or_relation::ExtendedOrRelationConfig, mul::{MulChip, MulConfig}, sub::{SubChip, SubConfig}, - target_resource_variable::{ - GetIsInputResourceFlagConfig, GetOwnedResourceVariableConfig, - }, }, - integrity::{check_input_resource, check_output_resource}, + integrity::load_resource, + merkle_circuit::{MerklePoseidonChip, MerklePoseidonConfig}, resource_commitment::{ResourceCommitChip, ResourceCommitConfig}, vamp_ir_utils::{get_circuit_assignments, parse, VariableAssignmentError}, }, constant::{ - TaigaFixedBases, NUM_RESOURCE, RESOURCE_ENCRYPTION_CIPHERTEXT_NUM, - RESOURCE_LOGIC_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_OWNED_RESOURCE_ID_PUBLIC_INPUT_IDX, - RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, RESOURCE_LOGIC_CIRCUIT_PUBLIC_INPUT_NUM, + TaigaFixedBases, RESOURCE_ENCRYPTION_CIPHERTEXT_NUM, RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, + RESOURCE_LOGIC_CIRCUIT_PUBLIC_INPUT_NUM, RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PK_X_IDX, RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PK_Y_IDX, - RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PUBLIC_INPUT_BEGIN_IDX, SETUP_PARAMS_MAP, + RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PUBLIC_INPUT_BEGIN_IDX, + RESOURCE_LOGIC_CIRCUIT_RESOURCE_MERKLE_ROOT_IDX, + RESOURCE_LOGIC_CIRCUIT_SELF_RESOURCE_ID_IDX, SETUP_PARAMS_MAP, }, error::TransactionError, proof::Proof, - resource::{RandomSeed, Resource, ResourceCommitment}, + resource::RandomSeed, resource_encryption::{ResourceCiphertext, SecretKey}, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, utils::mod_r_p, }; use dyn_clone::{clone_trait_object, DynClone}; @@ -165,29 +160,14 @@ impl ResourceLogicVerifyingInfo { .verify(&self.vk, params, &[self.public_inputs.inner()]) } - pub fn get_nullifiers(&self) -> [pallas::Base; NUM_RESOURCE] { - [ - self.public_inputs - .get_from_index(RESOURCE_LOGIC_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX), - self.public_inputs - .get_from_index(RESOURCE_LOGIC_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX), - ] - } - - pub fn get_resource_commitments(&self) -> [ResourceCommitment; NUM_RESOURCE] { - [ - self.public_inputs - .get_from_index(RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX) - .into(), - self.public_inputs - .get_from_index(RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX) - .into(), - ] + pub fn get_resource_merkle_root(&self) -> pallas::Base { + self.public_inputs + .get_from_index(RESOURCE_LOGIC_CIRCUIT_RESOURCE_MERKLE_ROOT_IDX) } - pub fn get_owned_resource_id(&self) -> pallas::Base { + pub fn get_self_resource_id(&self) -> pallas::Base { self.public_inputs - .get_from_index(RESOURCE_LOGIC_CIRCUIT_OWNED_RESOURCE_ID_PUBLIC_INPUT_IDX) + .get_from_index(RESOURCE_LOGIC_CIRCUIT_SELF_RESOURCE_ID_IDX) } } @@ -326,8 +306,7 @@ pub struct ResourceLogicConfig { pub table_idx: TableColumn, pub ecc_config: EccConfig, pub poseidon_config: PoseidonConfig, - pub get_is_input_resource_flag_config: GetIsInputResourceFlagConfig, - pub get_owned_resource_variable_config: GetOwnedResourceVariableConfig, + pub merkle_config: MerklePoseidonConfig, pub conditional_equal_config: ConditionalEqualConfig, pub conditional_select_config: ConditionalSelectConfig, pub extended_or_relation_config: ExtendedOrRelationConfig, @@ -387,15 +366,6 @@ impl ResourceLogicConfig { lagrange_coeffs[5..8].try_into().unwrap(), ); - let get_owned_resource_variable_config = GetOwnedResourceVariableConfig::configure( - meta, - advices[0], - [advices[1], advices[2], advices[3], advices[4]], - ); - - let get_is_input_resource_flag_config = - GetIsInputResourceFlagConfig::configure(meta, advices[0], advices[1], advices[2]); - let conditional_equal_config = ConditionalEqualConfig::configure(meta, [advices[0], advices[1], advices[2]]); let conditional_select_config = @@ -414,14 +384,20 @@ impl ResourceLogicConfig { poseidon_config.clone(), range_check, ); + + let merkle_config = MerklePoseidonChip::configure( + meta, + advices[..5].try_into().unwrap(), + poseidon_config.clone(), + ); + Self { advices, instances, table_idx, ecc_config, poseidon_config, - get_is_input_resource_flag_config, - get_owned_resource_variable_config, + merkle_config, conditional_equal_config, conditional_select_config, extended_or_relation_config, @@ -443,13 +419,13 @@ pub trait ResourceLogicVerifyingInfoTrait: DynClone { clone_trait_object!(ResourceLogicVerifyingInfoTrait); pub trait ResourceLogicCircuit: Circuit + ResourceLogicVerifyingInfoTrait { - // Default implementation, constrains the resources integrity. + // Load self resource and return self_resource and resource_merkle_root // TODO: how to enforce the constraints in resource_logic circuit? fn basic_constraints( &self, config: ResourceLogicConfig, mut layouter: impl Layouter, - ) -> Result { + ) -> Result { layouter.assign_table( || "table_idx", |mut table| { @@ -465,70 +441,46 @@ pub trait ResourceLogicCircuit: Circuit + ResourceLogicVerifyingIn }, )?; + // Construct a merkle chip + let merkle_chip = MerklePoseidonChip::construct(config.merkle_config); // Construct a resource_commit chip let resource_commit_chip = ResourceCommitChip::construct(config.resource_commit_config.clone()); - let input_resources = self.get_input_resources(); - let output_resources = self.get_output_resources(); - let mut input_resource_variables = vec![]; - let mut output_resource_variables = vec![]; - for i in 0..NUM_RESOURCE { - input_resource_variables.push(check_input_resource( - layouter.namespace(|| "check input resource"), - config.advices, - config.instances, - resource_commit_chip.clone(), - input_resources[i], - i * 2, - )?); - - // The old_nf may not be from above input resource - let old_nf = assign_free_advice( - layouter.namespace(|| "old nf"), - config.advices[0], - Value::known(output_resources[i].nonce.inner()), - )?; - output_resource_variables.push(check_output_resource( - layouter.namespace(|| "check output resource"), - config.advices, - config.instances, - resource_commit_chip.clone(), - output_resources[i], - old_nf, - i * 2 + 1, - )?); - } + // Load self_resource + let self_resource_status = load_resource( + layouter.namespace(|| "load self resource"), + config.advices, + resource_commit_chip, + config.conditional_select_config, + merkle_chip, + &self.get_self_resource(), + )?; - // Publicize the owned_resource_id - let owned_resource_id = assign_free_advice( - layouter.namespace(|| "owned_resource_id"), - config.advices[0], - Value::known(self.get_owned_resource_id()), + // Publicize the resource_merkle_root + layouter.constrain_instance( + self_resource_status.resource_merkle_root.cell(), + config.instances, + RESOURCE_LOGIC_CIRCUIT_RESOURCE_MERKLE_ROOT_IDX, )?; + + // Publicize the self resource id layouter.constrain_instance( - owned_resource_id.cell(), + self_resource_status.identity.cell(), config.instances, - RESOURCE_LOGIC_CIRCUIT_OWNED_RESOURCE_ID_PUBLIC_INPUT_IDX, + RESOURCE_LOGIC_CIRCUIT_SELF_RESOURCE_ID_IDX, )?; - Ok(BasicResourceLogicVariables { - owned_resource_id, - input_resource_variables: input_resource_variables.try_into().unwrap(), - output_resource_variables: output_resource_variables.try_into().unwrap(), - }) + Ok(self_resource_status) } - // resource logic designer need to implement the following functions. - // `get_input_resources` and `get_output_resources` will be used in `basic_constraints` to get the basic resource info. - // Add custom constraints on basic resource variables and user-defined variables. // It should at least contain the default resource_logic commitment fn custom_constraints( &self, config: ResourceLogicConfig, mut layouter: impl Layouter, - _basic_variables: BasicResourceLogicVariables, + _self_resource: ResourceStatus, ) -> Result<(), Error> { // Publicize the dynamic resource_logic commitments with default value publicize_default_dynamic_resource_logic_commitments( @@ -541,35 +493,23 @@ pub trait ResourceLogicCircuit: Circuit + ResourceLogicVerifyingIn } fn get_mandatory_public_inputs(&self) -> Vec { - let mut public_inputs = vec![]; - self.get_input_resources() - .iter() - .zip(self.get_output_resources().iter()) - .for_each(|(input_resource, output_resource)| { - let nf = input_resource.get_nf().unwrap().inner(); - public_inputs.push(nf); - let cm = output_resource.commitment(); - public_inputs.push(cm.inner()); - }); - public_inputs.push(self.get_owned_resource_id()); - public_inputs + let resource_witness = self.get_self_resource(); + let root = resource_witness.get_root(); + let id = resource_witness.get_identity(); + vec![root, id] } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE]; - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE]; + fn get_public_inputs(&self, rng: impl RngCore) -> ResourceLogicPublicInputs; - // The owned_resource_id is the input_resource_nf or the output_resource_cm_x - // The owned_resource_id is the key to look up the target variables and - // help determine whether the owned resource is the input resource or not in resource logic circuit. - fn get_owned_resource_id(&self) -> pallas::Base; + + fn get_self_resource(&self) -> ResourceExistenceWitness; } -/// BasicResourceLogicVariables are generally constrained in ResourceLogicCircuit::basic_constraints -/// and will be used in ResourceLogicCircuit::custom_constraints #[derive(Debug, Clone)] -pub struct BasicResourceLogicVariables { - pub owned_resource_id: AssignedCell, - pub input_resource_variables: [InputResourceVariables; NUM_RESOURCE], - pub output_resource_variables: [OutputResourceVariables; NUM_RESOURCE], +pub struct ResourceStatus { + pub resource_merkle_root: AssignedCell, + pub is_input: AssignedCell, + pub identity: AssignedCell, // nf or cm + pub resource: ResourceVariables, } #[derive(Debug, Clone)] @@ -592,135 +532,6 @@ pub struct InputResourceVariables { pub resource_variables: ResourceVariables, } -// Variables in the out resource -#[derive(Debug, Clone)] -pub struct OutputResourceVariables { - pub cm: AssignedCell, - pub resource_variables: ResourceVariables, -} - -#[derive(Debug, Clone)] -pub struct ResourceSearchableVariablePair { - // src_variable is the input_resource_nf or the output_resource_cm_x - pub src_variable: AssignedCell, - // target_variable is one of the parameter in the ResourceVariables - pub target_variable: AssignedCell, -} - -impl BasicResourceLogicVariables { - pub fn get_owned_resource_id(&self) -> AssignedCell { - self.owned_resource_id.clone() - } - - pub fn get_input_resource_nfs( - &self, - ) -> [AssignedCell; NUM_RESOURCE] { - let ret: Vec<_> = self - .input_resource_variables - .iter() - .map(|variables| variables.nf.clone()) - .collect(); - ret.try_into().unwrap() - } - - pub fn get_output_resource_cms( - &self, - ) -> [AssignedCell; NUM_RESOURCE] { - let ret: Vec<_> = self - .output_resource_variables - .iter() - .map(|variables| variables.cm.clone()) - .collect(); - ret.try_into().unwrap() - } - - fn get_variable_searchable_pairs( - &self, - input_target_variable: impl Fn( - &InputResourceVariables, - ) -> AssignedCell, - output_target_variable: impl Fn( - &OutputResourceVariables, - ) -> AssignedCell, - ) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.input_resource_variables - .iter() - .map(|variables| ResourceSearchableVariablePair { - src_variable: variables.nf.clone(), - target_variable: input_target_variable(variables), - }) - .chain(self.output_resource_variables.iter().map(|variables| { - ResourceSearchableVariablePair { - src_variable: variables.cm.clone(), - target_variable: output_target_variable(variables), - } - })) - .collect::>() - .try_into() - .unwrap() - } - - pub fn get_logic_searchable_pairs(&self) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.logic.clone(), - |variables| variables.resource_variables.logic.clone(), - ) - } - - pub fn get_label_searchable_pairs(&self) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.label.clone(), - |variables| variables.resource_variables.label.clone(), - ) - } - - pub fn get_quantity_searchable_pairs( - &self, - ) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.quantity.clone(), - |variables| variables.resource_variables.quantity.clone(), - ) - } - - pub fn get_is_ephemeral_searchable_pairs( - &self, - ) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.is_ephemeral.clone(), - |variables| variables.resource_variables.is_ephemeral.clone(), - ) - } - - pub fn get_value_searchable_pairs(&self) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.value.clone(), - |variables| variables.resource_variables.value.clone(), - ) - } - - pub fn get_nonce_searchable_pairs(&self) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.nonce.clone(), - |variables| variables.resource_variables.nonce.clone(), - ) - } - - pub fn get_npk_searchable_pairs(&self) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.npk.clone(), - |variables| variables.resource_variables.npk.clone(), - ) - } - - pub fn get_rseed_searchable_pairs(&self) -> [ResourceSearchableVariablePair; NUM_RESOURCE * 2] { - self.get_variable_searchable_pairs( - |variables| variables.resource_variables.rseed.clone(), - |variables| variables.resource_variables.rseed.clone(), - ) - } -} - // Default Circuit trait implementation #[macro_export] macro_rules! resource_logic_circuit_impl { @@ -742,14 +553,14 @@ macro_rules! resource_logic_circuit_impl { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - let basic_variables = self.basic_constraints( + let self_resource = self.basic_constraints( config.clone(), layouter.namespace(|| "basic constraints"), )?; self.custom_constraints( config, layouter.namespace(|| "custom constraints"), - basic_variables, + self_resource, )?; Ok(()) } diff --git a/taiga_halo2/src/circuit/resource_logic_examples.rs b/taiga_halo2/src/circuit/resource_logic_examples.rs index 983ee1f1..b9a15473 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples.rs @@ -5,15 +5,17 @@ use crate::{ ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, }, - constant::{NUM_RESOURCE, RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, SETUP_PARAMS_MAP}, + constant::{RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, SETUP_PARAMS_MAP, TAIGA_RESOURCE_TREE_DEPTH}, error::TransactionError, + merkle_tree::LR, proof::Proof, resource::{RandomSeed, Resource}, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, }; -#[cfg(feature = "borsh")] -use borsh::{BorshDeserialize, BorshSerialize}; +// #[cfg(feature = "borsh")] +// use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::plonk::{keygen_pk, keygen_vk, ProvingKey}; use halo2_proofs::{ circuit::{floor_planner, Layouter}, @@ -22,11 +24,7 @@ use halo2_proofs::{ use lazy_static::lazy_static; use pasta_curves::{pallas, vesta}; use rand::{rngs::OsRng, RngCore}; -#[cfg(feature = "nif")] -use rustler::{Decoder, Encoder, Env, NifResult, NifStruct, Term}; -#[cfg(feature = "examples")] -pub mod cascade_intent; #[cfg(feature = "examples")] mod field_addition; #[cfg(feature = "examples")] @@ -67,33 +65,15 @@ lazy_static! { // TrivialResourceLogicCircuit with empty custom constraints. #[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TrivialResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], -} - -// I only exist to allow trivial derivation of the nifstruct -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "nif", derive(NifStruct))] -#[cfg_attr(feature = "nif", module = "Taiga.ResourceLogic.Trivial")] -struct TrivialResourceLogicCircuitProxy { - owned_resource_id: pallas::Base, - input_resources: Vec, - output_resources: Vec, + self_resource: ResourceExistenceWitness, } impl TrivialResourceLogicCircuit { - pub fn new( - owned_resource_id: pallas::Base, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], - ) -> Self { - Self { - owned_resource_id, - input_resources, - output_resources, - } + pub fn new(resource: Resource, path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH]) -> Self { + let self_resource = ResourceExistenceWitness::new(resource, path); + Self { self_resource } } // Only for test @@ -102,97 +82,16 @@ impl TrivialResourceLogicCircuit { ResourceLogicByteCode::new(ResourceLogicRepresentation::Trivial, self.to_bytes()) } - // Only for test - #[cfg(feature = "borsh")] pub fn to_bytes(&self) -> Vec { - borsh::to_vec(&self).unwrap() - } - - // Only for test - #[cfg(feature = "borsh")] - pub fn from_bytes(bytes: &Vec) -> Self { - BorshDeserialize::deserialize(&mut bytes.as_ref()).unwrap() - } - - fn to_proxy(&self) -> TrivialResourceLogicCircuitProxy { - TrivialResourceLogicCircuitProxy { - owned_resource_id: self.owned_resource_id, - input_resources: self.input_resources.to_vec(), - output_resources: self.output_resources.to_vec(), - } - } -} - -#[cfg(feature = "borsh")] -impl BorshSerialize for TrivialResourceLogicCircuit { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - use ff::PrimeField; - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } - Ok(()) + bincode::serialize(&self).unwrap() } -} - -#[cfg(feature = "borsh")] -impl BorshDeserialize for TrivialResourceLogicCircuit { - fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = crate::utils::read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), - }) - } -} -impl TrivialResourceLogicCircuitProxy { - fn to_concrete(&self) -> Option { - let input_resources = self.input_resources.clone().try_into().ok()?; - let output_resources = self.output_resources.clone().try_into().ok()?; - let owned_resource_id = self.owned_resource_id; - Some(TrivialResourceLogicCircuit { - owned_resource_id, - input_resources, - output_resources, - }) - } -} -#[cfg(feature = "nif")] -impl Encoder for TrivialResourceLogicCircuit { - fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { - self.to_proxy().encode(env) - } -} -#[cfg(feature = "nif")] -impl<'a> Decoder<'a> for TrivialResourceLogicCircuit { - fn decode(term: Term<'a>) -> NifResult { - let val: TrivialResourceLogicCircuitProxy = Decoder::decode(term)?; - val.to_concrete() - .ok_or(rustler::Error::RaiseAtom("Could not decode proxy")) + pub fn from_bytes(bytes: &[u8]) -> Self { + bincode::deserialize(bytes).unwrap() } } impl ResourceLogicCircuit for TrivialResourceLogicCircuit { - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); let default_resource_logic_cm: [pallas::Base; 2] = @@ -207,8 +106,8 @@ impl ResourceLogicCircuit for TrivialResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -252,18 +151,7 @@ impl ResourceLogicVerifyingInfoTrait for TrivialResourceLogicCircuit { #[cfg(test)] pub mod tests { use super::TrivialResourceLogicCircuit; - use crate::{constant::NUM_RESOURCE, resource::tests::random_resource}; - use ff::Field; use pasta_curves::pallas; - use rand::RngCore; - pub fn random_trivial_resource_logic_circuit( - mut rng: R, - ) -> TrivialResourceLogicCircuit { - let owned_resource_id = pallas::Base::random(&mut rng); - let input_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - let output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - TrivialResourceLogicCircuit::new(owned_resource_id, input_resources, output_resources) - } #[test] fn test_halo2_trivial_resource_logic_circuit() { @@ -273,7 +161,7 @@ pub mod tests { use rand::rngs::OsRng; let mut rng = OsRng; - let circuit = random_trivial_resource_logic_circuit(&mut rng); + let circuit = TrivialResourceLogicCircuit::default(); let public_inputs = circuit.get_public_inputs(&mut rng); let prover = MockProver::::run( diff --git a/taiga_halo2/src/circuit/resource_logic_examples/cascade_intent.rs b/taiga_halo2/src/circuit/resource_logic_examples/cascade_intent.rs deleted file mode 100644 index 51af856a..00000000 --- a/taiga_halo2/src/circuit/resource_logic_examples/cascade_intent.rs +++ /dev/null @@ -1,264 +0,0 @@ -/// This example is to demonstrate how to cascade partial transactions for -/// atomic execution by the cascade intent. In this example, Alice wants to -/// simultaneously spend three different kinds of tokens/resources (more than -/// the fixed NUM_RESOURCE). She needs to distribute the resources into two -/// partial transactions and can utilize a cascade intent resource for encoding -/// and verifying the third resource information in the first transaction. In -/// the second transaction, she spends both the cascade resource and the third -/// resource. -use crate::{ - circuit::{ - blake2s::publicize_default_dynamic_resource_logic_commitments, - gadgets::{ - assign_free_advice, - target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, - }, - resource_logic_bytecode::{ResourceLogicByteCode, ResourceLogicRepresentation}, - resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, - }, - }, - constant::{NUM_RESOURCE, SETUP_PARAMS_MAP}, - error::TransactionError, - nullifier::Nullifier, - proof::Proof, - resource::{RandomSeed, Resource}, - resource_logic_commitment::ResourceLogicCommitment, - resource_logic_vk::ResourceLogicVerifyingKey, - utils::read_base_field, -}; -use borsh::{BorshDeserialize, BorshSerialize}; -use halo2_proofs::arithmetic::Field; -use halo2_proofs::{ - circuit::{floor_planner, Layouter, Value}, - plonk::{keygen_pk, keygen_vk, Circuit, ConstraintSystem, Error}, -}; -use lazy_static::lazy_static; -use pasta_curves::{group::ff::PrimeField, pallas}; -use rand::rngs::OsRng; -use rand::RngCore; - -lazy_static! { - pub static ref CASCADE_INTENT_VK: ResourceLogicVerifyingKey = - CascadeIntentResourceLogicCircuit::default().get_resource_logic_vk(); - pub static ref COMPRESSED_CASCADE_INTENT_VK: pallas::Base = CASCADE_INTENT_VK.get_compressed(); -} - -// CascadeIntentResourceLogicCircuit -#[derive(Clone, Debug, Default)] -pub struct CascadeIntentResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], - // use the resource commitment to identify the resource. - pub cascade_resource_cm: pallas::Base, -} - -impl CascadeIntentResourceLogicCircuit { - // We can encode at most three resources to label if needed. - pub fn encode_label(cascade_resource_cm: pallas::Base) -> pallas::Base { - cascade_resource_cm - } - - pub fn to_bytecode(&self) -> ResourceLogicByteCode { - ResourceLogicByteCode::new(ResourceLogicRepresentation::CascadeIntent, self.to_bytes()) - } - - pub fn to_bytes(&self) -> Vec { - borsh::to_vec(&self).unwrap() - } - - pub fn from_bytes(bytes: &Vec) -> Self { - BorshDeserialize::deserialize(&mut bytes.as_ref()).unwrap() - } -} - -impl ResourceLogicCircuit for CascadeIntentResourceLogicCircuit { - // Add custom constraints - fn custom_constraints( - &self, - config: Self::Config, - mut layouter: impl Layouter, - basic_variables: BasicResourceLogicVariables, - ) -> Result<(), Error> { - let owned_resource_id = basic_variables.get_owned_resource_id(); - let is_input_resource = get_is_input_resource_flag( - config.get_is_input_resource_flag_config, - layouter.namespace(|| "get is_input_resource_flag"), - &owned_resource_id, - &basic_variables.get_input_resource_nfs(), - &basic_variables.get_output_resource_cms(), - )?; - - // If the number of cascade resources is more than one, encode them. - let cascade_resource_cm = assign_free_advice( - layouter.namespace(|| "witness cascade_resource_cm"), - config.advices[0], - Value::known(self.cascade_resource_cm), - )?; - - // search target resource and get the intent label - let label = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource label"), - &owned_resource_id, - &basic_variables.get_label_searchable_pairs(), - )?; - - // check the label of intent resource - layouter.assign_region( - || "check label", - |mut region| region.constrain_equal(cascade_resource_cm.cell(), label.cell()), - )?; - - // check the cascade resource - layouter.assign_region( - || "conditional equal: check the cascade resource", - |mut region| { - config.conditional_equal_config.assign_region( - &is_input_resource, - &label, - &basic_variables.input_resource_variables[1].cm, - 0, - &mut region, - ) - }, - )?; - - // Publicize the dynamic resource_logic commitments with default value - publicize_default_dynamic_resource_logic_commitments( - &mut layouter, - config.advices[0], - config.instances, - )?; - - Ok(()) - } - - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let default_resource_logic_cm: [pallas::Base; 2] = - ResourceLogicCommitment::default().to_public_inputs(); - public_inputs.extend(default_resource_logic_cm); - public_inputs.extend(default_resource_logic_cm); - let padding = ResourceLogicPublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id - } -} - -resource_logic_circuit_impl!(CascadeIntentResourceLogicCircuit); -resource_logic_verifying_info_impl!(CascadeIntentResourceLogicCircuit); - -impl BorshSerialize for CascadeIntentResourceLogicCircuit { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } - - writer.write_all(&self.cascade_resource_cm.to_repr())?; - Ok(()) - } -} - -impl BorshDeserialize for CascadeIntentResourceLogicCircuit { - fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let cascade_resource_cm = read_base_field(reader)?; - Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), - cascade_resource_cm, - }) - } -} - -pub fn create_intent_resource( - mut rng: R, - cascade_resource_cm: pallas::Base, - nk: pallas::Base, -) -> Resource { - let label = CascadeIntentResourceLogicCircuit::encode_label(cascade_resource_cm); - let rseed = pallas::Base::random(&mut rng); - let nonce = Nullifier::random(&mut rng); - Resource::new_input_resource( - *COMPRESSED_CASCADE_INTENT_VK, - label, - pallas::Base::zero(), - 1u64, - nk, - nonce, - true, - rseed, - ) -} - -#[test] -fn test_halo2_cascade_intent_resource_logic_circuit() { - use crate::constant::RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE; - use crate::resource::tests::random_resource; - use halo2_proofs::arithmetic::Field; - use halo2_proofs::dev::MockProver; - use rand::rngs::OsRng; - - let mut rng = OsRng; - let circuit = { - let cascade_input_resource = random_resource(&mut rng); - let cascade_resource_cm = cascade_input_resource.commitment().inner(); - let nk = pallas::Base::random(&mut rng); - let intent_resource = create_intent_resource(&mut rng, cascade_resource_cm, nk); - let input_resources = [intent_resource, cascade_input_resource]; - let output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - - CascadeIntentResourceLogicCircuit { - owned_resource_id: input_resources[0].get_nf().unwrap().inner(), - input_resources, - output_resources, - cascade_resource_cm, - } - }; - - // Test serialization - let circuit = { - let circuit_bytes = circuit.to_bytes(); - CascadeIntentResourceLogicCircuit::from_bytes(&circuit_bytes) - }; - - let public_inputs = circuit.get_public_inputs(&mut rng); - - let prover = MockProver::::run( - RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, - &circuit, - vec![public_inputs.to_vec()], - ) - .unwrap(); - assert_eq!(prover.verify(), Ok(())); -} diff --git a/taiga_halo2/src/circuit/resource_logic_examples/field_addition.rs b/taiga_halo2/src/circuit/resource_logic_examples/field_addition.rs index 2dae6a81..6634fe36 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/field_addition.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/field_addition.rs @@ -6,18 +6,17 @@ use crate::{ assign_free_advice, }, resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, + ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, + ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, ResourceStatus, }, }, - constant::{ - NUM_RESOURCE, RESOURCE_LOGIC_CIRCUIT_CUSTOM_PUBLIC_INPUT_BEGIN_IDX, SETUP_PARAMS_MAP, - }, + constant::{RESOURCE_LOGIC_CIRCUIT_CUSTOM_PUBLIC_INPUT_BEGIN_IDX, SETUP_PARAMS_MAP}, error::TransactionError, proof::Proof, - resource::{RandomSeed, Resource}, + resource::RandomSeed, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, }; use halo2_proofs::{ circuit::{floor_planner, Layouter, Value}, @@ -30,9 +29,7 @@ use rand::RngCore; // FieldAdditionResourceLogicCircuit with a trivial constraint a + b = c. #[derive(Clone, Debug, Default)] struct FieldAdditionResourceLogicCircuit { - owned_resource_id: pallas::Base, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], + self_resource: ResourceExistenceWitness, a: pallas::Base, b: pallas::Base, } @@ -44,7 +41,7 @@ impl ResourceLogicCircuit for FieldAdditionResourceLogicCircuit { &self, config: Self::Config, mut layouter: impl Layouter, - _basic_variables: BasicResourceLogicVariables, + _self_resource: ResourceStatus, ) -> Result<(), Error> { let a = assign_free_advice( layouter.namespace(|| "witness a"), @@ -79,14 +76,6 @@ impl ResourceLogicCircuit for FieldAdditionResourceLogicCircuit { Ok(()) } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); let default_resource_logic_cm: [pallas::Base; 2] = @@ -102,8 +91,8 @@ impl ResourceLogicCircuit for FieldAdditionResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -113,22 +102,17 @@ resource_logic_verifying_info_impl!(FieldAdditionResourceLogicCircuit); #[test] fn test_halo2_addition_resource_logic_circuit() { use crate::constant::RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE; - use crate::resource::tests::random_resource; use halo2_proofs::arithmetic::Field; use halo2_proofs::dev::MockProver; use rand::rngs::OsRng; let mut rng = OsRng; let circuit = { - let input_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - let output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); + let self_resource_witness = ResourceExistenceWitness::default(); let a = pallas::Base::random(&mut rng); let b = pallas::Base::random(&mut rng); - let owned_resource_id = pallas::Base::random(&mut rng); FieldAdditionResourceLogicCircuit { - owned_resource_id, - input_resources, - output_resources, + self_resource: self_resource_witness, a, b, } diff --git a/taiga_halo2/src/circuit/resource_logic_examples/or_relation_intent.rs b/taiga_halo2/src/circuit/resource_logic_examples/or_relation_intent.rs index 4967db55..5ec569c5 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/or_relation_intent.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/or_relation_intent.rs @@ -5,25 +5,25 @@ use crate::{ circuit::{ blake2s::publicize_default_dynamic_resource_logic_commitments, - gadgets::{ - assign_free_advice, - poseidon_hash::poseidon_hash_gadget, - target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, - }, + gadgets::{assign_free_advice, assign_free_constant, poseidon_hash::poseidon_hash_gadget}, + integrity::load_resource, + merkle_circuit::MerklePoseidonChip, + resource_commitment::ResourceCommitChip, resource_logic_bytecode::{ResourceLogicByteCode, ResourceLogicRepresentation}, resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, + ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, + ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, ResourceStatus, }, resource_logic_examples::token::{Token, TOKEN_VK}, }, - constant::{NUM_RESOURCE, SETUP_PARAMS_MAP}, + constant::SETUP_PARAMS_MAP, error::TransactionError, nullifier::Nullifier, proof::Proof, resource::{RandomSeed, Resource}, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, utils::poseidon_hash_n, utils::read_base_field, }; @@ -48,9 +48,10 @@ lazy_static! { // OrRelationIntentResourceLogicCircuit #[derive(Clone, Debug, Default)] pub struct OrRelationIntentResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], + // self_resource is the intent resource + pub self_resource: ResourceExistenceWitness, + // If the self_resource(intent) is an output resource, a dummy desired resource is needed. + pub desired_resource: ResourceExistenceWitness, pub token_1: Token, pub token_2: Token, pub receiver_npk: pallas::Base, @@ -101,15 +102,53 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { &self, config: Self::Config, mut layouter: impl Layouter, - basic_variables: BasicResourceLogicVariables, + self_resource: ResourceStatus, ) -> Result<(), Error> { - let owned_resource_id = basic_variables.get_owned_resource_id(); - let is_input_resource = get_is_input_resource_flag( - config.get_is_input_resource_flag_config, - layouter.namespace(|| "get is_input_resource_flag"), - &owned_resource_id, - &basic_variables.get_input_resource_nfs(), - &basic_variables.get_output_resource_cms(), + // check + { + let one = assign_free_constant( + layouter.namespace(|| "constant one"), + config.advices[0], + pallas::Base::one(), + )?; + layouter.assign_region( + || "check label", + |mut region| { + region.constrain_equal(one.cell(), self_resource.resource.is_ephemeral.cell()) + }, + )?; + } + // load the desired resource + let desired_resource = { + // Construct a merkle chip + let merkle_chip = MerklePoseidonChip::construct(config.merkle_config); + + // Construct a resource_commit chip + let resource_commit_chip = + ResourceCommitChip::construct(config.resource_commit_config.clone()); + + load_resource( + layouter.namespace(|| "load the desired resource"), + config.advices, + resource_commit_chip, + config.conditional_select_config, + merkle_chip, + &self.desired_resource, + )? + }; + + // check self_resource and desired_resource are on the same tree + layouter.assign_region( + || "conditional equal: check root", + |mut region| { + config.conditional_equal_config.assign_region( + &self_resource.is_input, + &self_resource.resource_merkle_root, + &desired_resource.resource_merkle_root, + 0, + &mut region, + ) + }, )?; let token_resource_logic_vk = assign_free_advice( @@ -169,18 +208,12 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { ], )?; - // search target resource and get the intent label - let label = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource label"), - &owned_resource_id, - &basic_variables.get_label_searchable_pairs(), - )?; - // check the label of intent resource layouter.assign_region( || "check label", - |mut region| region.constrain_equal(encoded_label.cell(), label.cell()), + |mut region| { + region.constrain_equal(encoded_label.cell(), self_resource.resource.label.cell()) + }, )?; // check the resource_logic vk of output resource @@ -188,11 +221,9 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { || "conditional equal: check resource_logic vk", |mut region| { config.conditional_equal_config.assign_region( - &is_input_resource, + &self_resource.is_input, &token_resource_logic_vk, - &basic_variables.output_resource_variables[0] - .resource_variables - .logic, + &desired_resource.resource.logic, 0, &mut region, ) @@ -204,11 +235,9 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { || "conditional equal: check npk", |mut region| { config.conditional_equal_config.assign_region( - &is_input_resource, + &self_resource.is_input, &receiver_npk, - &basic_variables.output_resource_variables[0] - .resource_variables - .npk, + &desired_resource.resource.npk, 0, &mut region, ) @@ -220,34 +249,48 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { || "conditional equal: check value", |mut region| { config.conditional_equal_config.assign_region( - &is_input_resource, + &self_resource.is_input, &receiver_value, - &basic_variables.output_resource_variables[0] - .resource_variables - .value, + &desired_resource.resource.value, 0, &mut region, ) }, )?; + // check the desired_resource is an output + { + let zero_constant = assign_free_constant( + layouter.namespace(|| "constant zero"), + config.advices[0], + pallas::Base::zero(), + )?; + + layouter.assign_region( + || "conditional equal: check desired_resource is_input", + |mut region| { + config.conditional_equal_config.assign_region( + &self_resource.is_input, + &zero_constant, + &desired_resource.is_input, + 0, + &mut region, + ) + }, + )?; + } + // check the token_property and token_quantity in conditions - let output_resource_token_property = &basic_variables.output_resource_variables[0] - .resource_variables - .label; - let output_resource_token_quantity = &basic_variables.output_resource_variables[0] - .resource_variables - .quantity; layouter.assign_region( || "extended or relatioin", |mut region| { config.extended_or_relation_config.assign_region( - &is_input_resource, + &self_resource.is_input, (&token_property_1, &token_quantity_1), (&token_property_2, &token_quantity_2), ( - output_resource_token_property, - output_resource_token_quantity, + &desired_resource.resource.label, + &desired_resource.resource.quantity, ), 0, &mut region, @@ -265,14 +308,6 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { Ok(()) } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); let default_resource_logic_cm: [pallas::Base; 2] = @@ -287,8 +322,8 @@ impl ResourceLogicCircuit for OrRelationIntentResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -297,14 +332,8 @@ resource_logic_verifying_info_impl!(OrRelationIntentResourceLogicCircuit); impl BorshSerialize for OrRelationIntentResourceLogicCircuit { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } + self.self_resource.serialize(writer)?; + self.desired_resource.serialize(writer)?; self.token_1.serialize(writer)?; self.token_2.serialize(writer)?; @@ -318,21 +347,15 @@ impl BorshSerialize for OrRelationIntentResourceLogicCircuit { impl BorshDeserialize for OrRelationIntentResourceLogicCircuit { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; + let self_resource = ResourceExistenceWitness::deserialize_reader(reader)?; + let desired_resource = ResourceExistenceWitness::deserialize_reader(reader)?; let token_1 = Token::deserialize_reader(reader)?; let token_2 = Token::deserialize_reader(reader)?; let receiver_npk = read_base_field(reader)?; let receiver_value = read_base_field(reader)?; Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), + self_resource, + desired_resource, token_1, token_2, receiver_npk, @@ -374,7 +397,7 @@ fn test_halo2_or_relation_intent_resource_logic_circuit() { use crate::constant::RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE; use crate::{ circuit::resource_logic_examples::token::COMPRESSED_TOKEN_VK, - resource::tests::random_resource, + resource::tests::random_resource, resource_tree::ResourceMerkleTreeLeaves, }; use halo2_proofs::arithmetic::Field; use halo2_proofs::dev::MockProver; @@ -382,33 +405,52 @@ fn test_halo2_or_relation_intent_resource_logic_circuit() { let mut rng = OsRng; let circuit = { - let mut output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); let token_1 = Token::new("token1".to_string(), 1u64); let token_2 = Token::new("token2".to_string(), 2u64); - output_resources[0].kind.logic = *COMPRESSED_TOKEN_VK; - output_resources[0].kind.label = token_1.encode_name(); - output_resources[0].quantity = token_1.quantity(); + + // Create an output desired resource + let mut desired_resource = random_resource(&mut rng); + desired_resource.kind.logic = *COMPRESSED_TOKEN_VK; + desired_resource.kind.label = token_1.encode_name(); + desired_resource.quantity = token_1.quantity(); let nk = pallas::Base::random(&mut rng); - let npk = output_resources[0].get_npk(); let intent_resource = create_intent_resource( &mut rng, &token_1, &token_2, - npk, - output_resources[0].value, + desired_resource.get_npk(), + desired_resource.value, nk, ); - let padding_input_resource = Resource::random_padding_resource(&mut rng); - let input_resources = [intent_resource, padding_input_resource]; + + // Collect resource merkle leaves + let input_resource_nf_1 = intent_resource.get_nf().unwrap().inner(); + let output_resource_cm_1 = desired_resource.commitment().inner(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![input_resource_nf_1, output_resource_cm_1]); + + let intent_resource_witness = { + let merkle_path = resource_merkle_tree + .generate_path(input_resource_nf_1) + .unwrap(); + ResourceExistenceWitness::new(intent_resource, merkle_path) + }; + + let desired_resource_witness = { + let merkle_path = resource_merkle_tree + .generate_path(output_resource_cm_1) + .unwrap(); + ResourceExistenceWitness::new(desired_resource, merkle_path) + }; + OrRelationIntentResourceLogicCircuit { - owned_resource_id: input_resources[0].get_nf().unwrap().inner(), - input_resources, - output_resources, + self_resource: intent_resource_witness, + desired_resource: desired_resource_witness, token_1, token_2, - receiver_npk: npk, - receiver_value: output_resources[0].value, + receiver_npk: desired_resource.get_npk(), + receiver_value: desired_resource.value, } }; diff --git a/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent.rs b/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent.rs index dfa3acaa..e714e86c 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent.rs @@ -1,28 +1,26 @@ -/// The intent can be "partially fulfilled". For instance, Alice has 5 BTC and +/// The intent can be "partially fulfilled". For instance, Alice has 5 BTC(sell_resource) and /// wants 10 ETH. Alice utilizes this intent to swap a portion proportionally, -/// exchanging 2 BTC for 4 ETH and receiving back 3 BTC. +/// exchanging 2 BTC for 4 ETH(offer resource) and receiving back 3 BTC(returned resource). use crate::{ circuit::{ blake2s::publicize_default_dynamic_resource_logic_commitments, - gadgets::{ - assign_free_constant, - mul::MulChip, - sub::{SubChip, SubInstructions}, - target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, - }, + gadgets::{mul::MulChip, sub::SubChip}, + integrity::load_resource, + merkle_circuit::MerklePoseidonChip, + resource_commitment::ResourceCommitChip, resource_logic_bytecode::{ResourceLogicByteCode, ResourceLogicRepresentation}, resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, + ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, + ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, ResourceStatus, }, }, - constant::{NUM_RESOURCE, SETUP_PARAMS_MAP}, + constant::SETUP_PARAMS_MAP, error::TransactionError, proof::Proof, - resource::{RandomSeed, Resource}, + resource::RandomSeed, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, - utils::read_base_field, + resource_tree::ResourceExistenceWitness, }; use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::{ @@ -30,7 +28,7 @@ use halo2_proofs::{ plonk::{keygen_pk, keygen_vk, Circuit, ConstraintSystem, Error}, }; use lazy_static::lazy_static; -use pasta_curves::{group::ff::PrimeField, pallas}; +use pasta_curves::pallas; use rand::rngs::OsRng; use rand::RngCore; @@ -50,9 +48,14 @@ lazy_static! { // PartialFulfillmentIntentResourceLogicCircuit #[derive(Clone, Debug, Default)] pub struct PartialFulfillmentIntentResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], + // intent resource + pub self_resource: ResourceExistenceWitness, + // constraints on sell_resource will be enabled only when creating the intent resource, otherwise it's a dummy one + pub sell_resource: ResourceExistenceWitness, + // constraints will be enabled only when consuming the intent resource, otherwise it's a dummy one + pub offer_resource: ResourceExistenceWitness, + // constraints will be enabled only when consuming the intent resource, otherwise it's a dummy one + pub returned_resource: ResourceExistenceWitness, pub swap: Swap, } @@ -79,12 +82,61 @@ impl ResourceLogicCircuit for PartialFulfillmentIntentResourceLogicCircuit { &self, config: Self::Config, mut layouter: impl Layouter, - basic_variables: BasicResourceLogicVariables, + self_resource: ResourceStatus, ) -> Result<(), Error> { + // Construct a merkle chip + let merkle_chip = MerklePoseidonChip::construct(config.merkle_config); + + // Construct a resource_commit chip + let resource_commit_chip = + ResourceCommitChip::construct(config.resource_commit_config.clone()); + let sub_chip = SubChip::construct(config.sub_config.clone(), ()); let mul_chip = MulChip::construct(config.mul_config.clone()); - let owned_resource_id = basic_variables.get_owned_resource_id(); + // load the sell resource + let sell_resource = load_resource( + layouter.namespace(|| "load the sell resource"), + config.advices, + resource_commit_chip.clone(), + config.conditional_select_config, + merkle_chip.clone(), + &self.sell_resource, + )?; + + // load the offer resource + let offer_resource = load_resource( + layouter.namespace(|| "load the offer resource"), + config.advices, + resource_commit_chip.clone(), + config.conditional_select_config, + merkle_chip.clone(), + &self.offer_resource, + )?; + + // load the returned resource + let returned_resource = load_resource( + layouter.namespace(|| "load the returned resource"), + config.advices, + resource_commit_chip, + config.conditional_select_config, + merkle_chip, + &self.returned_resource, + )?; + + // check: self_resource and offer_resource are on the same tree + layouter.assign_region( + || "conditional equal: check offer_resource root", + |mut region| { + config.conditional_equal_config.assign_region( + &self_resource.is_input, + &self_resource.resource_merkle_root, + &offer_resource.resource_merkle_root, + 0, + &mut region, + ) + }, + )?; let label = self .swap @@ -94,67 +146,42 @@ impl ResourceLogicCircuit for PartialFulfillmentIntentResourceLogicCircuit { layouter.namespace(|| "encode label"), )?; - // search target resource and get the intent label - let owned_resource_label = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource label"), - &owned_resource_id, - &basic_variables.get_label_searchable_pairs(), - )?; - // Enforce consistency of label: // - as witnessed in the swap, and // - as encoded in the intent resource layouter.assign_region( || "check label", - |mut region| region.constrain_equal(encoded_label.cell(), owned_resource_label.cell()), + |mut region| { + region.constrain_equal(encoded_label.cell(), self_resource.resource.label.cell()) + }, )?; - let is_input_resource = get_is_input_resource_flag( - config.get_is_input_resource_flag_config, - layouter.namespace(|| "get is_input_resource_flag"), - &owned_resource_id, - &basic_variables.get_input_resource_nfs(), - &basic_variables.get_output_resource_cms(), - )?; - // Conditional checks if is_input_resource == 1 - label.is_input_resource_checks( - &is_input_resource, - &basic_variables, + // intent resource creation + label.intent_resource_creation_check( + &self_resource, + &sell_resource, + &config.advices, &config.conditional_equal_config, - layouter.namespace(|| "is_input_resource checks"), + &sub_chip, + layouter.namespace(|| "intent resource creation"), )?; - let is_output_resource = { - let constant_one = assign_free_constant( - layouter.namespace(|| "one"), - config.advices[0], - pallas::Base::one(), - )?; - // TODO: use a nor gate to replace the sub gate. - SubInstructions::sub( - &sub_chip, - layouter.namespace(|| "expected_sold_quantity - returned_quantity"), - &is_input_resource, - &constant_one, - )? - }; - // Conditional checks if is_output_resource == 1 - label.is_output_resource_checks( - &is_output_resource, - &basic_variables, + // intent resource consumption + label.intent_resource_consumption_check( + &self_resource.is_input, + &offer_resource, &config.conditional_equal_config, - layouter.namespace(|| "is_output_resource checks"), + layouter.namespace(|| "intent resource consumption"), )?; - // Conditional checks if is_partial_fulfillment == 1 - label.is_partial_fulfillment_checks( - &is_input_resource, - &basic_variables, + label.partial_fulfillment_check( + &self_resource, + &offer_resource, + &returned_resource, &config.conditional_equal_config, &sub_chip, &mul_chip, - layouter.namespace(|| "is_partial_fulfillment checks"), + layouter.namespace(|| "partial fulfillment check"), )?; // Publicize the dynamic resource_logic commitments with default value @@ -167,14 +194,6 @@ impl ResourceLogicCircuit for PartialFulfillmentIntentResourceLogicCircuit { Ok(()) } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); let default_resource_logic_cm: [pallas::Base; 2] = @@ -189,8 +208,8 @@ impl ResourceLogicCircuit for PartialFulfillmentIntentResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -199,15 +218,10 @@ resource_logic_verifying_info_impl!(PartialFulfillmentIntentResourceLogicCircuit impl BorshSerialize for PartialFulfillmentIntentResourceLogicCircuit { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } - + self.self_resource.serialize(writer)?; + self.sell_resource.serialize(writer)?; + self.offer_resource.serialize(writer)?; + self.returned_resource.serialize(writer)?; self.swap.serialize(writer)?; Ok(()) @@ -216,18 +230,16 @@ impl BorshSerialize for PartialFulfillmentIntentResourceLogicCircuit { impl BorshDeserialize for PartialFulfillmentIntentResourceLogicCircuit { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; + let self_resource = ResourceExistenceWitness::deserialize_reader(reader)?; + let sell_resource = ResourceExistenceWitness::deserialize_reader(reader)?; + let offer_resource = ResourceExistenceWitness::deserialize_reader(reader)?; + let returned_resource = ResourceExistenceWitness::deserialize_reader(reader)?; let swap = Swap::deserialize_reader(reader)?; Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), + self_resource, + sell_resource, + offer_resource, + returned_resource, swap, }) } @@ -241,6 +253,7 @@ mod tests { token::{Token, TokenAuthorization}, }; use crate::constant::RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE; + use crate::resource_tree::ResourceMerkleTreeLeaves; use halo2_proofs::arithmetic::Field; use halo2_proofs::dev::MockProver; use rand::rngs::OsRng; @@ -262,17 +275,28 @@ mod tests { let swap = swap(&mut rng, sell, buy); let intent_resource = swap.create_intent_resource(&mut rng); + let sell_resource = swap.sell.resource(); + let sell_nf = sell_resource.get_nf().unwrap().inner(); + let intent_resource_cm = intent_resource.commitment().inner(); + let resource_merkle_tree = ResourceMerkleTreeLeaves::new(vec![sell_nf, intent_resource_cm]); + + let sell_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(sell_nf).unwrap(); + ResourceExistenceWitness::new(*sell_resource, merkle_path) + }; - let input_padding_resource = Resource::random_padding_resource(&mut rng); - let output_padding_resource = Resource::random_padding_resource(&mut rng); - - let input_resources = [*swap.sell.resource(), input_padding_resource]; - let output_resources = [intent_resource, output_padding_resource]; + let intent_resource_witness = { + let merkle_path = resource_merkle_tree + .generate_path(intent_resource_cm) + .unwrap(); + ResourceExistenceWitness::new(intent_resource, merkle_path) + }; let circuit = PartialFulfillmentIntentResourceLogicCircuit { - owned_resource_id: intent_resource.commitment().inner(), - input_resources, - output_resources, + self_resource: intent_resource_witness, + sell_resource: sell_resource_witness, + offer_resource: ResourceExistenceWitness::default(), // a dummy resource + returned_resource: ResourceExistenceWitness::default(), // a dummy resource swap, }; let public_inputs = circuit.get_public_inputs(&mut rng); @@ -296,14 +320,30 @@ mod tests { let intent_resource = swap.create_intent_resource(&mut rng); let bob_sell = swap.buy.clone(); - let (input_resources, output_resources) = swap.fill(&mut rng, intent_resource, bob_sell); + let (offer_resource, _returned_resource) = swap.fill(&mut rng, bob_sell); + + let intent_nf = intent_resource.get_nf().unwrap().inner(); + let offer_cm = offer_resource.commitment().inner(); + let resource_merkle_tree = ResourceMerkleTreeLeaves::new(vec![intent_nf, offer_cm]); + + let intent_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(intent_nf).unwrap(); + ResourceExistenceWitness::new(intent_resource, merkle_path) + }; + + let offer_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(offer_cm).unwrap(); + ResourceExistenceWitness::new(offer_resource, merkle_path) + }; let circuit = PartialFulfillmentIntentResourceLogicCircuit { - owned_resource_id: intent_resource.get_nf().unwrap().inner(), - input_resources, - output_resources, + self_resource: intent_resource_witness, + sell_resource: ResourceExistenceWitness::default(), // a dummy one + offer_resource: offer_resource_witness, + returned_resource: ResourceExistenceWitness::default(), // a dummy one swap, }; + let public_inputs = circuit.get_public_inputs(&mut rng); let prover = MockProver::::run( @@ -325,12 +365,38 @@ mod tests { let intent_resource = swap.create_intent_resource(&mut rng); let bob_sell = Token::new(swap.buy.name().inner().to_string(), 2u64); - let (input_resources, output_resources) = swap.fill(&mut rng, intent_resource, bob_sell); + let (offer_resource, returned_resource) = swap.fill(&mut rng, bob_sell); + + let intent_nf = intent_resource.get_nf().unwrap().inner(); + let offer_cm = offer_resource.commitment().inner(); + let returned_cm = returned_resource.commitment().inner(); + let resource_merkle_tree = ResourceMerkleTreeLeaves::new(vec![ + intent_nf, + offer_cm, + pallas::Base::zero(), + returned_cm, + ]); + + let intent_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(intent_nf).unwrap(); + ResourceExistenceWitness::new(intent_resource, merkle_path) + }; + + let offer_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(offer_cm).unwrap(); + ResourceExistenceWitness::new(offer_resource, merkle_path) + }; + + let returned_resource_witness = { + let merkle_path = resource_merkle_tree.generate_path(returned_cm).unwrap(); + ResourceExistenceWitness::new(returned_resource, merkle_path) + }; let circuit = PartialFulfillmentIntentResourceLogicCircuit { - owned_resource_id: intent_resource.get_nf().unwrap().inner(), - input_resources, - output_resources, + self_resource: intent_resource_witness, + sell_resource: ResourceExistenceWitness::default(), // a dummy one + offer_resource: offer_resource_witness, + returned_resource: returned_resource_witness, swap, }; diff --git a/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/label.rs b/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/label.rs index 2d477451..6942f4bc 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/label.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/label.rs @@ -1,16 +1,17 @@ use crate::circuit::{ gadgets::{ + assign_free_constant, conditional_equal::ConditionalEqualConfig, mul::{MulChip, MulInstructions}, poseidon_hash::poseidon_hash_gadget, sub::{SubChip, SubInstructions}, }, - resource_logic_circuit::BasicResourceLogicVariables, + resource_logic_circuit::ResourceStatus, }; use halo2_gadgets::poseidon::Pow5Config as PoseidonConfig; use halo2_proofs::{ circuit::{AssignedCell, Layouter}, - plonk::Error, + plonk::{Advice, Column, Error}, }; use pasta_curves::pallas; @@ -47,11 +48,11 @@ impl PartialFulfillmentIntentLabel { ) } - /// Checks to be enforced if `is_input_resource == 1` - pub fn is_input_resource_checks( + /// constraints on intent resource consumption + pub fn intent_resource_consumption_check( &self, is_input_resource: &AssignedCell, - basic_variables: &BasicResourceLogicVariables, + offer_resource: &ResourceStatus, config: &ConditionalEqualConfig, mut layouter: impl Layouter, ) -> Result<(), Error> { @@ -61,9 +62,7 @@ impl PartialFulfillmentIntentLabel { config.assign_region( is_input_resource, &self.token_resource_logic_vk, - &basic_variables.output_resource_variables[0] - .resource_variables - .logic, + &offer_resource.resource.logic, 0, &mut region, ) @@ -76,9 +75,7 @@ impl PartialFulfillmentIntentLabel { config.assign_region( is_input_resource, &self.bought_token, - &basic_variables.output_resource_variables[0] - .resource_variables - .label, + &offer_resource.resource.label, 0, &mut region, ) @@ -92,9 +89,7 @@ impl PartialFulfillmentIntentLabel { config.assign_region( is_input_resource, &self.receiver_npk, - &basic_variables.output_resource_variables[0] - .resource_variables - .npk, + &offer_resource.resource.npk, 0, &mut region, ) @@ -108,9 +103,7 @@ impl PartialFulfillmentIntentLabel { config.assign_region( is_input_resource, &self.receiver_value, - &basic_variables.output_resource_variables[0] - .resource_variables - .value, + &offer_resource.resource.value, 0, &mut region, ) @@ -120,23 +113,38 @@ impl PartialFulfillmentIntentLabel { Ok(()) } - /// Checks to be enforced if `is_output_resource == 1` - pub fn is_output_resource_checks( + /// constraints on intent resource creation + pub fn intent_resource_creation_check( &self, - is_output_resource: &AssignedCell, - basic_variables: &BasicResourceLogicVariables, + intent_resource: &ResourceStatus, + sell_resource: &ResourceStatus, + advices: &[Column; 10], config: &ConditionalEqualConfig, + sub_chip: &SubChip, mut layouter: impl Layouter, ) -> Result<(), Error> { + let is_output_resource = { + let constant_one = assign_free_constant( + layouter.namespace(|| "one"), + advices[0], + pallas::Base::one(), + )?; + // TODO: use a nor gate to replace the sub gate. + SubInstructions::sub( + sub_chip, + layouter.namespace(|| "is_output"), + &intent_resource.is_input, + &constant_one, + )? + }; + layouter.assign_region( - || "conditional equal: check sold token resource_logic_vk", + || "conditional equal: check sell token resource_logic_vk", |mut region| { config.assign_region( - is_output_resource, + &is_output_resource, &self.token_resource_logic_vk, - &basic_variables.input_resource_variables[0] - .resource_variables - .logic, + &sell_resource.resource.logic, 0, &mut region, ) @@ -144,14 +152,12 @@ impl PartialFulfillmentIntentLabel { )?; layouter.assign_region( - || "conditional equal: check sold token label", + || "conditional equal: check sell token label", |mut region| { config.assign_region( - is_output_resource, + &is_output_resource, &self.sold_token, - &basic_variables.input_resource_variables[0] - .resource_variables - .label, + &sell_resource.resource.label, 0, &mut region, ) @@ -159,14 +165,12 @@ impl PartialFulfillmentIntentLabel { )?; layouter.assign_region( - || "conditional equal: check sold token quantity", + || "conditional equal: check sell token quantity", |mut region| { config.assign_region( - is_output_resource, + &is_output_resource, &self.sold_token_quantity, - &basic_variables.input_resource_variables[0] - .resource_variables - .quantity, + &sell_resource.resource.quantity, 0, &mut region, ) @@ -176,11 +180,15 @@ impl PartialFulfillmentIntentLabel { Ok(()) } - /// Checks to be enforced if `is_partial_fulfillment == 1` - pub fn is_partial_fulfillment_checks( + /// partial fulfillment check: + /// validity of the returned resource + /// partial fulfillment equation + #[allow(clippy::too_many_arguments)] + pub fn partial_fulfillment_check( &self, - is_input_resource: &AssignedCell, - basic_variables: &BasicResourceLogicVariables, + intent_resource: &ResourceStatus, + offer_resource: &ResourceStatus, + returned_resource: &ResourceStatus, config: &ConditionalEqualConfig, sub_chip: &SubChip, mul_chip: &MulChip, @@ -192,44 +200,52 @@ impl PartialFulfillmentIntentLabel { layouter .namespace(|| "expected_bought_token_quantity - actual_bought_token_quantity"), &self.bought_token_quantity, - &basic_variables.output_resource_variables[0] - .resource_variables - .quantity, + &offer_resource.resource.quantity, )?; MulInstructions::mul( mul_chip, layouter.namespace(|| "is_input * is_partial_fulfillment"), - is_input_resource, + &intent_resource.is_input, &is_partial_fulfillment, )? }; - // check returned token vk if it's partially fulfilled + // check: self_resource and returned_resource are on the same tree + layouter.assign_region( + || "conditional equal: check returned_resource root", + |mut region| { + config.assign_region( + &is_partial_fulfillment, + &intent_resource.resource_merkle_root, + &returned_resource.resource_merkle_root, + 0, + &mut region, + ) + }, + )?; + + // check the returned resource vk if it's partially fulfilled layouter.assign_region( || "conditional equal: check returned token vk", |mut region| { config.assign_region( &is_partial_fulfillment, &self.token_resource_logic_vk, - &basic_variables.output_resource_variables[1] - .resource_variables - .logic, + &returned_resource.resource.logic, 0, &mut region, ) }, )?; - // check return token label if it's partially fulfilled + // check the returned resource label if it's partially fulfilled layouter.assign_region( || "conditional equal: check returned token label", |mut region| { config.assign_region( &is_partial_fulfillment, &self.sold_token, - &basic_variables.output_resource_variables[1] - .resource_variables - .label, + &returned_resource.resource.label, 0, &mut region, ) @@ -242,9 +258,7 @@ impl PartialFulfillmentIntentLabel { config.assign_region( &is_partial_fulfillment, &self.receiver_npk, - &basic_variables.output_resource_variables[1] - .resource_variables - .npk, + &returned_resource.resource.npk, 0, &mut region, ) @@ -257,9 +271,7 @@ impl PartialFulfillmentIntentLabel { config.assign_region( &is_partial_fulfillment, &self.receiver_value, - &basic_variables.output_resource_variables[1] - .resource_variables - .value, + &returned_resource.resource.value, 0, &mut region, ) @@ -272,9 +284,7 @@ impl PartialFulfillmentIntentLabel { sub_chip, layouter.namespace(|| "expected_sold_quantity - returned_quantity"), &self.sold_token_quantity, - &basic_variables.output_resource_variables[1] - .resource_variables - .quantity, + &returned_resource.resource.quantity, )?; // check (expected_bought_quantity * actual_sold_quantity) == (expected_sold_quantity * actual_bought_quantity) @@ -289,9 +299,7 @@ impl PartialFulfillmentIntentLabel { mul_chip, layouter.namespace(|| "expected_sold_quantity * actual_bought_quantity"), &self.sold_token_quantity, - &basic_variables.output_resource_variables[0] - .resource_variables - .quantity, + &offer_resource.resource.quantity, )?; layouter.assign_region( diff --git a/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/swap.rs b/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/swap.rs index 0aed6188..42073c02 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/swap.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/partial_fulfillment_intent/swap.rs @@ -4,7 +4,6 @@ use crate::{ gadgets::assign_free_advice, resource_logic_examples::token::{Token, TokenAuthorization, TokenResource, TOKEN_VK}, }, - constant::NUM_RESOURCE, resource::Resource, utils::poseidon_hash_n, }; @@ -45,12 +44,7 @@ impl Swap { /// - completely fills the swap using a single `TokenResource`, or /// - partially fills the swap, producing a `TokenResource` and a /// returned resource. - pub fn fill( - &self, - mut rng: impl RngCore, - intent_resource: Resource, - offer: Token, - ) -> ([Resource; NUM_RESOURCE], [Resource; NUM_RESOURCE]) { + pub fn fill(&self, mut rng: impl RngCore, offer: Token) -> (Resource, Resource) { assert_eq!(offer.name(), self.buy.name()); let ratio = self.buy.quantity() / self.sell.quantity; @@ -62,8 +56,6 @@ impl Swap { &self.auth, ); - let input_padding_resource = Resource::random_padding_resource(&mut rng); - let returned_resource = if offer.quantity() < self.buy.quantity() { let filled_quantity = offer.quantity() / ratio; let returned_quantity = self.sell.quantity - filled_quantity; @@ -82,10 +74,7 @@ impl Swap { Resource::random_padding_resource(&mut rng) }; - let input_resources = [intent_resource, input_padding_resource]; - let output_resources = [*offer_resource.resource(), returned_resource]; - - (input_resources, output_resources) + (*offer_resource, returned_resource) } pub fn encode_label(&self) -> pallas::Base { diff --git a/taiga_halo2/src/circuit/resource_logic_examples/receiver_resource_logic.rs b/taiga_halo2/src/circuit/resource_logic_examples/receiver_resource_logic.rs index 544bddf9..3d5fd15a 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/receiver_resource_logic.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/receiver_resource_logic.rs @@ -1,25 +1,23 @@ use crate::{ circuit::{ blake2s::publicize_default_dynamic_resource_logic_commitments, - gadgets::{ - add::AddChip, assign_free_advice, poseidon_hash::poseidon_hash_gadget, - target_resource_variable::get_owned_resource_variable, - }, + gadgets::{add::AddChip, assign_free_advice, poseidon_hash::poseidon_hash_gadget}, resource_encryption_circuit::resource_encryption_gadget, resource_logic_bytecode::{ResourceLogicByteCode, ResourceLogicRepresentation}, resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, + ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, + ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, ResourceStatus, }, resource_logic_examples::signature_verification::COMPRESSED_TOKEN_AUTH_VK, }, - constant::{GENERATOR, NUM_RESOURCE, SETUP_PARAMS_MAP}, + constant::{GENERATOR, SETUP_PARAMS_MAP}, error::TransactionError, proof::Proof, - resource::{RandomSeed, Resource}, + resource::RandomSeed, resource_encryption::{ResourceCiphertext, ResourcePlaintext, SecretKey}, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, utils::{mod_r_p, read_base_field, read_point}, }; use borsh::{BorshDeserialize, BorshSerialize}; @@ -46,9 +44,7 @@ lazy_static! { // ReceiverResourceLogicCircuit is used in the token resource_logic as dynamic resource_logic and contains the resource encryption constraints. #[derive(Clone, Debug)] pub struct ReceiverResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], + pub self_resource: ResourceExistenceWitness, pub resource_logic_vk: pallas::Base, pub encrypt_nonce: pallas::Base, pub sk: pallas::Base, @@ -73,9 +69,7 @@ impl ReceiverResourceLogicCircuit { impl Default for ReceiverResourceLogicCircuit { fn default() -> Self { Self { - owned_resource_id: pallas::Base::zero(), - input_resources: [(); NUM_RESOURCE].map(|_| Resource::default()), - output_resources: [(); NUM_RESOURCE].map(|_| Resource::default()), + self_resource: ResourceExistenceWitness::default(), resource_logic_vk: pallas::Base::zero(), encrypt_nonce: pallas::Base::zero(), sk: pallas::Base::zero(), @@ -91,7 +85,7 @@ impl ResourceLogicCircuit for ReceiverResourceLogicCircuit { &self, config: Self::Config, mut layouter: impl Layouter, - basic_variables: BasicResourceLogicVariables, + self_resource: ResourceStatus, ) -> Result<(), Error> { let encrypt_nonce = assign_free_advice( layouter.namespace(|| "witness encrypt_nonce"), @@ -114,14 +108,6 @@ impl ResourceLogicCircuit for ReceiverResourceLogicCircuit { Value::known(self.rcv_pk.to_affine()), )?; - let owned_resource_id = basic_variables.get_owned_resource_id(); - let value = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource value"), - &owned_resource_id, - &basic_variables.get_value_searchable_pairs(), - )?; - let auth_resource_logic_vk = assign_free_advice( layouter.namespace(|| "witness auth resource_logic vk"), config.advices[0], @@ -147,70 +133,20 @@ impl ResourceLogicCircuit for ReceiverResourceLogicCircuit { layouter.assign_region( || "check value encoding", - |mut region| region.constrain_equal(encoded_value.cell(), value.cell()), - )?; - - // search target resource and get the label - let label = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource label"), - &owned_resource_id, - &basic_variables.get_label_searchable_pairs(), - )?; - - // search target resource and get the logic - let logic = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource logic"), - &owned_resource_id, - &basic_variables.get_logic_searchable_pairs(), - )?; - - // search target resource and get the quantity - let quantity = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource quantity"), - &owned_resource_id, - &basic_variables.get_quantity_searchable_pairs(), - )?; - - let nonce = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource nonce"), - &owned_resource_id, - &basic_variables.get_nonce_searchable_pairs(), - )?; - - let npk = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource npk"), - &owned_resource_id, - &basic_variables.get_npk_searchable_pairs(), - )?; - - let is_ephemeral = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource is_ephemeral"), - &owned_resource_id, - &basic_variables.get_is_ephemeral_searchable_pairs(), - )?; - - let rseed = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource rseed"), - &owned_resource_id, - &basic_variables.get_rseed_searchable_pairs(), + |mut region| { + region.constrain_equal(encoded_value.cell(), self_resource.resource.value.cell()) + }, )?; let mut message = vec![ - logic, - label, - value, - quantity, - nonce, - npk, - is_ephemeral, - rseed, + self_resource.resource.logic, + self_resource.resource.label, + self_resource.resource.value, + self_resource.resource.quantity, + self_resource.resource.nonce, + self_resource.resource.npk, + self_resource.resource.is_ephemeral, + self_resource.resource.rseed, ]; let add_chip = AddChip::::construct(config.add_config.clone(), ()); @@ -239,14 +175,6 @@ impl ResourceLogicCircuit for ReceiverResourceLogicCircuit { Ok(()) } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); let default_resource_logic_cm: [pallas::Base; 2] = @@ -259,23 +187,17 @@ impl ResourceLogicCircuit for ReceiverResourceLogicCircuit { &RandomSeed::random(rng), ); public_inputs.extend(custom_public_input_padding.iter()); - assert_eq!(NUM_RESOURCE, 2); - let target_resource = if self.get_owned_resource_id() - == self.get_output_resources()[0].commitment().inner() - { - self.get_output_resources()[0] - } else { - self.get_output_resources()[1] - }; + + let self_resource = self.self_resource.get_resource(); let message = vec![ - target_resource.kind.logic, - target_resource.kind.label, - target_resource.value, - pallas::Base::from(target_resource.quantity), - target_resource.nonce.inner(), - target_resource.get_npk(), - pallas::Base::from(target_resource.is_ephemeral as u64), - target_resource.rseed, + self_resource.kind.logic, + self_resource.kind.label, + self_resource.value, + pallas::Base::from(self_resource.quantity), + self_resource.nonce.inner(), + self_resource.get_npk(), + pallas::Base::from(self_resource.is_ephemeral as u64), + self_resource.rseed, ]; let plaintext = ResourcePlaintext::padding(&message); let key = SecretKey::from_dh_exchange(&self.rcv_pk, &mod_r_p(self.sk)); @@ -290,8 +212,8 @@ impl ResourceLogicCircuit for ReceiverResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -300,15 +222,7 @@ resource_logic_verifying_info_impl!(ReceiverResourceLogicCircuit); impl BorshSerialize for ReceiverResourceLogicCircuit { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } - + self.self_resource.serialize(writer)?; writer.write_all(&self.resource_logic_vk.to_repr())?; writer.write_all(&self.encrypt_nonce.to_repr())?; writer.write_all(&self.sk.to_repr())?; @@ -321,22 +235,14 @@ impl BorshSerialize for ReceiverResourceLogicCircuit { impl BorshDeserialize for ReceiverResourceLogicCircuit { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; + let self_resource = ResourceExistenceWitness::deserialize_reader(reader)?; let resource_logic_vk = read_base_field(reader)?; let encrypt_nonce = read_base_field(reader)?; let sk = read_base_field(reader)?; let rcv_pk = read_point(reader)?; let auth_resource_logic_vk = read_base_field(reader)?; Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), + self_resource, resource_logic_vk, encrypt_nonce, sk, @@ -348,7 +254,8 @@ impl BorshDeserialize for ReceiverResourceLogicCircuit { #[test] fn test_halo2_receiver_resource_logic_circuit() { - use crate::constant::RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE; + use crate::constant::{RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, TAIGA_RESOURCE_TREE_DEPTH}; + use crate::merkle_tree::LR; use crate::{resource::tests::random_resource, utils::poseidon_hash_n}; use ff::{Field, PrimeField}; use halo2_proofs::dev::MockProver; @@ -356,26 +263,25 @@ fn test_halo2_receiver_resource_logic_circuit() { let mut rng = OsRng; let (circuit, rcv_sk) = { - let input_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - let mut output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); + // Create an output resource + let mut resource = random_resource(&mut rng); let encrypt_nonce = pallas::Base::from_u128(23333u128); let sk = pallas::Base::random(&mut rng); let rcv_sk = pallas::Base::random(&mut rng); let generator = GENERATOR.to_curve(); let rcv_pk = generator * mod_r_p(rcv_sk); let rcv_pk_coord = rcv_pk.to_affine().coordinates().unwrap(); - output_resources[0].value = poseidon_hash_n([ + resource.value = poseidon_hash_n([ *rcv_pk_coord.x(), *rcv_pk_coord.y(), *COMPRESSED_TOKEN_AUTH_VK, *COMPRESSED_RECEIVER_VK, ]); - let owned_resource_id = output_resources[0].commitment().inner(); + let merkle_path = [(pallas::Base::zero(), LR::L); TAIGA_RESOURCE_TREE_DEPTH]; + let self_resource = ResourceExistenceWitness::new(resource, merkle_path); ( ReceiverResourceLogicCircuit { - owned_resource_id, - input_resources, - output_resources, + self_resource, resource_logic_vk: *COMPRESSED_RECEIVER_VK, encrypt_nonce, sk, @@ -403,18 +309,16 @@ fn test_halo2_receiver_resource_logic_circuit() { assert_eq!(prover.verify(), Ok(())); let de_cipher = public_inputs.decrypt(rcv_sk).unwrap(); - assert_eq!(de_cipher[0], circuit.output_resources[0].get_logic()); - assert_eq!(de_cipher[1], circuit.output_resources[0].get_label()); - assert_eq!(de_cipher[2], circuit.output_resources[0].value); - assert_eq!( - de_cipher[3], - pallas::Base::from(circuit.output_resources[0].quantity) - ); - assert_eq!(de_cipher[4], circuit.output_resources[0].nonce.inner()); - assert_eq!(de_cipher[5], circuit.output_resources[0].get_npk()); + let original_resource = circuit.self_resource.get_resource(); + assert_eq!(de_cipher[0], original_resource.get_logic()); + assert_eq!(de_cipher[1], original_resource.get_label()); + assert_eq!(de_cipher[2], original_resource.value); + assert_eq!(de_cipher[3], pallas::Base::from(original_resource.quantity)); + assert_eq!(de_cipher[4], original_resource.nonce.inner()); + assert_eq!(de_cipher[5], original_resource.get_npk()); assert_eq!( de_cipher[6], - pallas::Base::from(circuit.output_resources[0].is_ephemeral) + pallas::Base::from(original_resource.is_ephemeral) ); - assert_eq!(de_cipher[7], circuit.output_resources[0].rseed); + assert_eq!(de_cipher[7], original_resource.rseed); } diff --git a/taiga_halo2/src/circuit/resource_logic_examples/signature_verification.rs b/taiga_halo2/src/circuit/resource_logic_examples/signature_verification.rs index 5a891e31..3ce98ea6 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/signature_verification.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/signature_verification.rs @@ -1,22 +1,20 @@ use crate::{ circuit::{ blake2s::publicize_default_dynamic_resource_logic_commitments, - gadgets::{ - assign_free_advice, poseidon_hash::poseidon_hash_gadget, - target_resource_variable::get_owned_resource_variable, - }, + gadgets::{assign_free_advice, poseidon_hash::poseidon_hash_gadget}, resource_logic_bytecode::{ResourceLogicByteCode, ResourceLogicRepresentation}, resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, + ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, + ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, ResourceStatus, }, }, - constant::{TaigaFixedBasesFull, NUM_RESOURCE, SETUP_PARAMS_MAP}, + constant::{TaigaFixedBasesFull, SETUP_PARAMS_MAP}, error::TransactionError, proof::Proof, - resource::{RandomSeed, Resource}, + resource::RandomSeed, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, utils::{mod_r_p, poseidon_hash_n, read_base_field, read_point, read_scalar_field}, }; use borsh::{BorshDeserialize, BorshSerialize}; @@ -35,8 +33,8 @@ use pasta_curves::{ use rand::rngs::OsRng; use rand::RngCore; -// The message contains the input resource nullifiers and output resource commitments -const MESSAGE_LEN: usize = NUM_RESOURCE * 2; +// Use the merkle root as message. +const MESSAGE_LEN: usize = 1; const POSEIDON_HASH_LEN: usize = MESSAGE_LEN + 4; lazy_static! { pub static ref TOKEN_AUTH_VK: ResourceLogicVerifyingKey = @@ -45,6 +43,7 @@ lazy_static! { } #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SchnorrSignature { // public key pk: pallas::Point, @@ -82,9 +81,6 @@ impl SchnorrSignature { *pk_coord.x(), *pk_coord.y(), message[0], - message[1], - message[2], - message[3], ])); let s = z + h * sk; Self { pk, r, s } @@ -93,10 +89,9 @@ impl SchnorrSignature { // SignatureVerificationResourceLogicCircuit uses the schnorr signature. #[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SignatureVerificationResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], + pub self_resource: ResourceExistenceWitness, pub resource_logic_vk: pallas::Base, pub signature: SchnorrSignature, pub receiver_resource_logic_vk: pallas::Base, @@ -104,17 +99,13 @@ pub struct SignatureVerificationResourceLogicCircuit { impl SignatureVerificationResourceLogicCircuit { pub fn new( - owned_resource_id: pallas::Base, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], + self_resource: ResourceExistenceWitness, resource_logic_vk: pallas::Base, signature: SchnorrSignature, receiver_resource_logic_vk: pallas::Base, ) -> Self { Self { - owned_resource_id, - input_resources, - output_resources, + self_resource, resource_logic_vk, signature, receiver_resource_logic_vk, @@ -123,29 +114,15 @@ impl SignatureVerificationResourceLogicCircuit { pub fn from_sk_and_sign( mut rng: R, - owned_resource_id: pallas::Base, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], + self_resource: ResourceExistenceWitness, resource_logic_vk: pallas::Base, sk: pallas::Scalar, receiver_resource_logic_vk: pallas::Base, ) -> Self { - assert_eq!(NUM_RESOURCE, 2); - let mut message = vec![]; - input_resources - .iter() - .zip(output_resources.iter()) - .for_each(|(input_resource, output_resource)| { - let nf = input_resource.get_nf().unwrap().inner(); - message.push(nf); - let cm = output_resource.commitment(); - message.push(cm.inner()); - }); + let message = vec![self_resource.get_root()]; let signature = SchnorrSignature::sign(&mut rng, sk, message); Self { - owned_resource_id, - input_resources, - output_resources, + self_resource, resource_logic_vk, signature, receiver_resource_logic_vk, @@ -174,7 +151,7 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { &self, config: Self::Config, mut layouter: impl Layouter, - basic_variables: BasicResourceLogicVariables, + self_resource: ResourceStatus, ) -> Result<(), Error> { // Construct an ECC chip let ecc_chip = EccChip::construct(config.ecc_config); @@ -185,15 +162,6 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { Value::known(self.signature.pk.to_affine()), )?; - // search target resource and get the value - let owned_resource_id = basic_variables.get_owned_resource_id(); - let value = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource value"), - &owned_resource_id, - &basic_variables.get_value_searchable_pairs(), - )?; - let auth_resource_logic_vk = assign_free_advice( layouter.namespace(|| "witness auth resource_logic vk"), config.advices[0], @@ -219,7 +187,9 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { layouter.assign_region( || "check value encoding", - |mut region| region.constrain_equal(encoded_value.cell(), value.cell()), + |mut region| { + region.constrain_equal(encoded_value.cell(), self_resource.resource.value.cell()) + }, )?; let r = NonIdentityPoint::new( @@ -241,9 +211,6 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { // Hash(r||P||m) let h_scalar = { - let nfs = basic_variables.get_input_resource_nfs(); - let cms = basic_variables.get_output_resource_cms(); - assert_eq!(NUM_RESOURCE, 2); let h = poseidon_hash_gadget( config.poseidon_config, layouter.namespace(|| "Poseidon_hash(r, P, m)"), @@ -252,10 +219,7 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { r.inner().y(), pk.inner().x(), pk.inner().y(), - nfs[0].clone(), - cms[0].clone(), - nfs[1].clone(), - cms[1].clone(), + self_resource.resource_merkle_root, ], )?; @@ -280,14 +244,6 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { Ok(()) } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); let default_resource_logic_cm: [pallas::Base; 2] = @@ -302,8 +258,8 @@ impl ResourceLogicCircuit for SignatureVerificationResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -312,15 +268,7 @@ resource_logic_verifying_info_impl!(SignatureVerificationResourceLogicCircuit); impl BorshSerialize for SignatureVerificationResourceLogicCircuit { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } - + self.self_resource.serialize(writer)?; writer.write_all(&self.resource_logic_vk.to_repr())?; self.signature.serialize(writer)?; writer.write_all(&self.receiver_resource_logic_vk.to_repr())?; @@ -331,20 +279,12 @@ impl BorshSerialize for SignatureVerificationResourceLogicCircuit { impl BorshDeserialize for SignatureVerificationResourceLogicCircuit { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; + let self_resource = ResourceExistenceWitness::deserialize_reader(reader)?; let resource_logic_vk = read_base_field(reader)?; let signature = SchnorrSignature::deserialize_reader(reader)?; let receiver_resource_logic_vk = read_base_field(reader)?; Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), + self_resource, resource_logic_vk, signature, receiver_resource_logic_vk, @@ -376,25 +316,25 @@ fn test_halo2_sig_verification_resource_logic_circuit() { use crate::circuit::resource_logic_examples::{ receiver_resource_logic::COMPRESSED_RECEIVER_VK, token::TokenAuthorization, }; - use crate::constant::RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE; + use crate::constant::{RESOURCE_LOGIC_CIRCUIT_PARAMS_SIZE, TAIGA_RESOURCE_TREE_DEPTH}; + use crate::merkle_tree::LR; use crate::resource::tests::random_resource; use halo2_proofs::dev::MockProver; use rand::rngs::OsRng; let mut rng = OsRng; let circuit = { - let mut input_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - let output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); + // Create an input resource + let mut resource = random_resource(&mut rng); let sk = pallas::Scalar::random(&mut rng); let auth_vk = pallas::Base::random(&mut rng); let auth = TokenAuthorization::from_sk_vk(&sk, &auth_vk); - input_resources[0].value = auth.to_value(); - let owned_resource_id = input_resources[0].get_nf().unwrap().inner(); + resource.value = auth.to_value(); + let merkle_path = [(pallas::Base::zero(), LR::R); TAIGA_RESOURCE_TREE_DEPTH]; + let resource_witness = ResourceExistenceWitness::new(resource, merkle_path); SignatureVerificationResourceLogicCircuit::from_sk_and_sign( &mut rng, - owned_resource_id, - input_resources, - output_resources, + resource_witness, auth_vk, sk, *COMPRESSED_RECEIVER_VK, diff --git a/taiga_halo2/src/circuit/resource_logic_examples/token.rs b/taiga_halo2/src/circuit/resource_logic_examples/token.rs index 571afba6..cbed5492 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples/token.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples/token.rs @@ -1,15 +1,11 @@ use crate::{ circuit::{ blake2s::{resource_logic_commitment_gadget, Blake2sChip}, - gadgets::{ - assign_free_advice, assign_free_constant, - poseidon_hash::poseidon_hash_gadget, - target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, - }, + gadgets::{assign_free_advice, assign_free_constant, poseidon_hash::poseidon_hash_gadget}, resource_logic_bytecode::{ResourceLogicByteCode, ResourceLogicRepresentation}, resource_logic_circuit::{ - BasicResourceLogicVariables, ResourceLogicCircuit, ResourceLogicConfig, - ResourceLogicPublicInputs, ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, + ResourceLogicCircuit, ResourceLogicConfig, ResourceLogicPublicInputs, + ResourceLogicVerifyingInfo, ResourceLogicVerifyingInfoTrait, ResourceStatus, }, resource_logic_examples::receiver_resource_logic::{ ReceiverResourceLogicCircuit, COMPRESSED_RECEIVER_VK, @@ -19,18 +15,21 @@ use crate::{ }, }, constant::{ - NUM_RESOURCE, PRF_EXPAND_DYNAMIC_RESOURCE_LOGIC_1_CM_R, + PRF_EXPAND_DYNAMIC_RESOURCE_LOGIC_1_CM_R, RESOURCE_LOGIC_CIRCUIT_FIRST_DYNAMIC_RESOURCE_LOGIC_CM_1, RESOURCE_LOGIC_CIRCUIT_FIRST_DYNAMIC_RESOURCE_LOGIC_CM_2, RESOURCE_LOGIC_CIRCUIT_SECOND_DYNAMIC_RESOURCE_LOGIC_CM_1, RESOURCE_LOGIC_CIRCUIT_SECOND_DYNAMIC_RESOURCE_LOGIC_CM_2, SETUP_PARAMS_MAP, + TAIGA_RESOURCE_TREE_DEPTH, }, error::TransactionError, + merkle_tree::LR, nullifier::Nullifier, proof::Proof, resource::{RandomSeed, Resource, ResourceLogics}, resource_logic_commitment::ResourceLogicCommitment, resource_logic_vk::ResourceLogicVerifyingKey, + resource_tree::ResourceExistenceWitness, utils::{poseidon_hash_n, read_base_field, read_point}, }; use borsh::{BorshDeserialize, BorshSerialize}; @@ -187,20 +186,13 @@ impl TokenResource { mut rng: R, auth: TokenAuthorization, auth_sk: pallas::Scalar, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], + merkle_path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH], ) -> ResourceLogics { - let TokenResource { - token_name, - resource, - } = self; // token resource logic - let nf = resource.get_nf().unwrap().inner(); + let self_resource = ResourceExistenceWitness::new(self.resource, merkle_path); let token_resource_logic = TokenResourceLogicCircuit { - owned_resource_id: nf, - input_resources, - output_resources, - token_name: token_name.clone(), + self_resource, + token_name: self.token_name.clone(), auth, receiver_resource_logic_vk: *COMPRESSED_RECEIVER_VK, rseed: RandomSeed::random(&mut rng), @@ -209,9 +201,7 @@ impl TokenResource { // token auth resource logic let token_auth_resource_logic = SignatureVerificationResourceLogicCircuit::from_sk_and_sign( &mut rng, - nf, - input_resources, - output_resources, + self_resource, auth.vk, auth_sk, *COMPRESSED_RECEIVER_VK, @@ -227,21 +217,12 @@ impl TokenResource { &self, mut rng: R, auth: TokenAuthorization, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], + merkle_path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH], ) -> ResourceLogics { - let TokenResource { - token_name, - resource, - } = self; - - let owned_resource_id = resource.commitment().inner(); - // token resource logic + let self_resource = ResourceExistenceWitness::new(self.resource, merkle_path); let token_resource_logic = TokenResourceLogicCircuit { - owned_resource_id, - input_resources, - output_resources, - token_name: token_name.clone(), + self_resource, + token_name: self.token_name.clone(), auth, receiver_resource_logic_vk: *COMPRESSED_RECEIVER_VK, rseed: RandomSeed::random(&mut rng), @@ -249,9 +230,7 @@ impl TokenResource { // receiver resource logic let receiver_resource_logic = ReceiverResourceLogicCircuit { - owned_resource_id, - input_resources, - output_resources, + self_resource, resource_logic_vk: *COMPRESSED_RECEIVER_VK, encrypt_nonce: pallas::Base::from_u128(rng.gen()), sk: pallas::Base::random(&mut rng), @@ -269,9 +248,7 @@ impl TokenResource { // TokenResourceLogicCircuit #[derive(Clone, Debug)] pub struct TokenResourceLogicCircuit { - pub owned_resource_id: pallas::Base, - pub input_resources: [Resource; NUM_RESOURCE], - pub output_resources: [Resource; NUM_RESOURCE], + self_resource: ResourceExistenceWitness, // The token_name goes to label. It can be extended to a list and embedded to label. pub token_name: TokenName, // The auth goes to value and defines how to consume and create the resource. @@ -313,9 +290,7 @@ impl TokenResourceLogicCircuit { impl Default for TokenResourceLogicCircuit { fn default() -> Self { Self { - owned_resource_id: pallas::Base::zero(), - input_resources: [(); NUM_RESOURCE].map(|_| Resource::default()), - output_resources: [(); NUM_RESOURCE].map(|_| Resource::default()), + self_resource: ResourceExistenceWitness::default(), token_name: TokenName("Token_name".to_string()), auth: TokenAuthorization::default(), receiver_resource_logic_vk: pallas::Base::zero(), @@ -330,10 +305,8 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { &self, config: Self::Config, mut layouter: impl Layouter, - basic_variables: BasicResourceLogicVariables, + self_resource: ResourceStatus, ) -> Result<(), Error> { - let owned_resource_id = basic_variables.get_owned_resource_id(); - let token_property = assign_free_advice( layouter.namespace(|| "witness token_property"), config.advices[0], @@ -342,18 +315,12 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { // We can add more constraints on token_property or extend the token_properties. - // search target resource and get the label - let label = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource label"), - &owned_resource_id, - &basic_variables.get_label_searchable_pairs(), - )?; - // check label layouter.assign_region( || "check label", - |mut region| region.constrain_equal(token_property.cell(), label.cell()), + |mut region| { + region.constrain_equal(token_property.cell(), self_resource.resource.label.cell()) + }, )?; // Construct an ECC chip @@ -371,14 +338,6 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { Value::known(self.auth.vk), )?; - // search target resource and get the value - let value = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get owned resource value"), - &owned_resource_id, - &basic_variables.get_value_searchable_pairs(), - )?; - let receiver_resource_logic_vk = assign_free_advice( layouter.namespace(|| "witness receiver resource_logic vk"), config.advices[0], @@ -399,16 +358,12 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { layouter.assign_region( || "check value encoding", - |mut region| region.constrain_equal(encoded_value.cell(), value.cell()), + |mut region| { + region.constrain_equal(encoded_value.cell(), self_resource.resource.value.cell()) + }, )?; // check the is_ephemeral flag - let is_ephemeral = get_owned_resource_variable( - config.get_owned_resource_variable_config, - layouter.namespace(|| "get is_ephemeral"), - &owned_resource_id, - &basic_variables.get_is_ephemeral_searchable_pairs(), - )?; let constant_zero = assign_free_constant( layouter.namespace(|| "zero"), config.advices[0], @@ -416,25 +371,23 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { )?; layouter.assign_region( || "check is_ephemeral", - |mut region| region.constrain_equal(is_ephemeral.cell(), constant_zero.cell()), + |mut region| { + region.constrain_equal( + self_resource.resource.is_ephemeral.cell(), + constant_zero.cell(), + ) + }, )?; // Resource Logic Commitment // Commt the sender(authorization method included) resource_logic if it's an input resource; // Commit the receiver(resource encryption constraints included) resource_logic if it's an output resource. let first_dynamic_resource_logic = { - let is_input_resource = get_is_input_resource_flag( - config.get_is_input_resource_flag_config, - layouter.namespace(|| "get is_input_resource_flag"), - &owned_resource_id, - &basic_variables.get_input_resource_nfs(), - &basic_variables.get_output_resource_cms(), - )?; layouter.assign_region( || "conditional select: ", |mut region| { config.conditional_select_config.assign_region( - &is_input_resource, + &self_resource.is_input, &auth_resource_logic_vk, &receiver_resource_logic_vk, 0, @@ -500,23 +453,12 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { Ok(()) } - fn get_input_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.input_resources - } - - fn get_output_resources(&self) -> &[Resource; NUM_RESOURCE] { - &self.output_resources - } - fn get_public_inputs(&self, mut rng: impl RngCore) -> ResourceLogicPublicInputs { let mut public_inputs = self.get_mandatory_public_inputs(); - let dynamic_resource_logic = if self.owned_resource_id - == self.output_resources[0].commitment().inner() - || self.owned_resource_id == self.output_resources[1].commitment().inner() - { - self.receiver_resource_logic_vk - } else { + let dynamic_resource_logic = if self.get_self_resource().is_input() { self.auth.vk + } else { + self.receiver_resource_logic_vk }; let resource_logic_com_r = self @@ -538,8 +480,8 @@ impl ResourceLogicCircuit for TokenResourceLogicCircuit { public_inputs.into() } - fn get_owned_resource_id(&self) -> pallas::Base { - self.owned_resource_id + fn get_self_resource(&self) -> ResourceExistenceWitness { + self.self_resource } } @@ -548,15 +490,7 @@ resource_logic_verifying_info_impl!(TokenResourceLogicCircuit); impl BorshSerialize for TokenResourceLogicCircuit { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write_all(&self.owned_resource_id.to_repr())?; - for input in self.input_resources.iter() { - input.serialize(writer)?; - } - - for output in self.output_resources.iter() { - output.serialize(writer)?; - } - + self.self_resource.serialize(writer)?; self.token_name.serialize(writer)?; self.auth.serialize(writer)?; writer.write_all(&self.receiver_resource_logic_vk.to_repr())?; @@ -568,21 +502,13 @@ impl BorshSerialize for TokenResourceLogicCircuit { impl BorshDeserialize for TokenResourceLogicCircuit { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let owned_resource_id = read_base_field(reader)?; - let input_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; - let output_resources: Vec<_> = (0..NUM_RESOURCE) - .map(|_| Resource::deserialize_reader(reader)) - .collect::>()?; + let self_resource = ResourceExistenceWitness::deserialize_reader(reader)?; let token_name = TokenName::deserialize_reader(reader)?; let auth = TokenAuthorization::deserialize_reader(reader)?; let receiver_resource_logic_vk = read_base_field(reader)?; let rseed = RandomSeed::deserialize_reader(reader)?; Ok(Self { - owned_resource_id, - input_resources: input_resources.try_into().unwrap(), - output_resources: output_resources.try_into().unwrap(), + self_resource, token_name, auth, receiver_resource_logic_vk, @@ -646,16 +572,16 @@ fn test_halo2_token_resource_logic_circuit() { let mut rng = OsRng; let circuit = { - let mut input_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); - let output_resources = [(); NUM_RESOURCE].map(|_| random_resource(&mut rng)); + // Create an input resource + let mut resource = random_resource(&mut rng); let token_name = TokenName("Token_name".to_string()); let auth = TokenAuthorization::random(&mut rng); - input_resources[0].kind.label = token_name.encode(); - input_resources[0].value = auth.to_value(); + resource.kind.label = token_name.encode(); + resource.value = auth.to_value(); + let merkle_path = [(pallas::Base::zero(), LR::R); TAIGA_RESOURCE_TREE_DEPTH]; + let self_resource = ResourceExistenceWitness::new(resource, merkle_path); TokenResourceLogicCircuit { - owned_resource_id: input_resources[0].get_nf().unwrap().inner(), - input_resources, - output_resources, + self_resource, token_name, auth, receiver_resource_logic_vk: *COMPRESSED_RECEIVER_VK, diff --git a/taiga_halo2/src/compliance.rs b/taiga_halo2/src/compliance.rs index b3fb4753..975d6857 100644 --- a/taiga_halo2/src/compliance.rs +++ b/taiga_halo2/src/compliance.rs @@ -222,7 +222,7 @@ impl ComplianceInfo { let compliance_circuit = ComplianceCircuit { input_resource: self.input_resource, - merkle_path: self.input_merkle_path.get_path().try_into().unwrap(), + merkle_path: self.input_merkle_path.inner().try_into().unwrap(), output_resource: self.output_resource, rcv, input_resource_logic_cm_r, diff --git a/taiga_halo2/src/constant.rs b/taiga_halo2/src/constant.rs index 1f626846..152d06bc 100644 --- a/taiga_halo2/src/constant.rs +++ b/taiga_halo2/src/constant.rs @@ -44,10 +44,12 @@ pub const PRF_EXPAND_DYNAMIC_RESOURCE_LOGIC_2_CM_R: u8 = 7; /// Commitment merkle tree depth pub const TAIGA_COMMITMENT_TREE_DEPTH: usize = 32; -pub const BASE_BITS_NUM: usize = 255; +/// resource merkle tree depth +pub const TAIGA_RESOURCE_TREE_DEPTH: usize = 4; +/// resource merkle tree leaves num +pub const TAIGA_RESOURCE_TREE_LEAVES_NUM: usize = 1 << TAIGA_RESOURCE_TREE_DEPTH; -/// The number of resources in a (partial)tx. -pub const NUM_RESOURCE: usize = 2; +pub const BASE_BITS_NUM: usize = 255; pub const COMPLIANCE_NF_PUBLIC_INPUT_ROW_IDX: usize = 0; pub const COMPLIANCE_ANCHOR_PUBLIC_INPUT_ROW_IDX: usize = 1; @@ -67,29 +69,27 @@ pub const RESOURCE_LOGIC_CIRCUIT_PUBLIC_INPUT_NUM: usize = RESOURCE_LOGIC_CIRCUIT_MANDATORY_PUBLIC_INPUT_NUM + RESOURCE_LOGIC_CIRCUIT_CUSTOM_PUBLIC_INPUT_NUM + RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PUBLIC_INPUT_NUM; -pub const RESOURCE_LOGIC_CIRCUIT_MANDATORY_PUBLIC_INPUT_NUM: usize = 9; +pub const RESOURCE_LOGIC_CIRCUIT_MANDATORY_PUBLIC_INPUT_NUM: usize = 6; pub const RESOURCE_LOGIC_CIRCUIT_CUSTOM_PUBLIC_INPUT_NUM: usize = 2; pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PUBLIC_INPUT_NUM: usize = RESOURCE_ENCRYPTION_CIPHERTEXT_NUM + 2; // ciphertext(12) + public_key(2) -pub const RESOURCE_LOGIC_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX: usize = 0; -pub const RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX: usize = 1; -pub const RESOURCE_LOGIC_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX: usize = 2; -pub const RESOURCE_LOGIC_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX: usize = 3; -pub const RESOURCE_LOGIC_CIRCUIT_OWNED_RESOURCE_ID_PUBLIC_INPUT_IDX: usize = 4; -pub const RESOURCE_LOGIC_CIRCUIT_FIRST_DYNAMIC_RESOURCE_LOGIC_CM_1: usize = 5; -pub const RESOURCE_LOGIC_CIRCUIT_FIRST_DYNAMIC_RESOURCE_LOGIC_CM_2: usize = 6; -pub const RESOURCE_LOGIC_CIRCUIT_SECOND_DYNAMIC_RESOURCE_LOGIC_CM_1: usize = 7; -pub const RESOURCE_LOGIC_CIRCUIT_SECOND_DYNAMIC_RESOURCE_LOGIC_CM_2: usize = 8; +// resource logic public input index +pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_MERKLE_ROOT_IDX: usize = 0; +pub const RESOURCE_LOGIC_CIRCUIT_SELF_RESOURCE_ID_IDX: usize = 1; +pub const RESOURCE_LOGIC_CIRCUIT_FIRST_DYNAMIC_RESOURCE_LOGIC_CM_1: usize = 2; +pub const RESOURCE_LOGIC_CIRCUIT_FIRST_DYNAMIC_RESOURCE_LOGIC_CM_2: usize = 3; +pub const RESOURCE_LOGIC_CIRCUIT_SECOND_DYNAMIC_RESOURCE_LOGIC_CM_1: usize = 4; +pub const RESOURCE_LOGIC_CIRCUIT_SECOND_DYNAMIC_RESOURCE_LOGIC_CM_2: usize = 5; pub const RESOURCE_LOGIC_CIRCUIT_CUSTOM_PUBLIC_INPUT_BEGIN_IDX: usize = RESOURCE_LOGIC_CIRCUIT_MANDATORY_PUBLIC_INPUT_NUM; pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PUBLIC_INPUT_BEGIN_IDX: usize = RESOURCE_LOGIC_CIRCUIT_MANDATORY_PUBLIC_INPUT_NUM + RESOURCE_LOGIC_CIRCUIT_CUSTOM_PUBLIC_INPUT_NUM; -pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_NONCE_IDX: usize = 21; -pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_MAC_IDX: usize = 22; -pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PK_X_IDX: usize = 23; -pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PK_Y_IDX: usize = 24; +pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_NONCE_IDX: usize = 18; +pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_MAC_IDX: usize = 19; +pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PK_X_IDX: usize = 20; +pub const RESOURCE_LOGIC_CIRCUIT_RESOURCE_ENCRYPTION_PK_Y_IDX: usize = 21; // Resource encryption pub const RESOURCE_ENCRYPTION_PLAINTEXT_NUM: usize = 10; diff --git a/taiga_halo2/src/error.rs b/taiga_halo2/src/error.rs index 9a7a2efc..e599a068 100644 --- a/taiga_halo2/src/error.rs +++ b/taiga_halo2/src/error.rs @@ -15,7 +15,7 @@ pub enum TransactionError { /// Output resource commitment is inconsistent between the compliance and the resource logic. InconsistentOutputResourceCommitment, /// Owned resource id is inconsistent between the compliance and the resource logic. - InconsistentOwnedResourceID, + InconsistentSelfResourceID, /// IO error IoError(std::io::Error), /// Transparent resource nullifier key is missing @@ -26,6 +26,8 @@ pub enum TransactionError { MissingPartialTxBindingSignatureR, /// ResourceLogicRepresentation is not valid InvalidResourceLogicRepresentation, + /// Resource merkle root is inconsistent between the compliance and the resource logic. + InconsistentResourceMerkleRoot, } impl Display for TransactionError { @@ -41,7 +43,7 @@ impl Display for TransactionError { InconsistentOutputResourceCommitment => f.write_str( "Output resource commitment is not consistent between the compliance and the resource logic", ), - InconsistentOwnedResourceID => { + InconsistentSelfResourceID => { f.write_str("Owned resource id is not consistent between the compliance and the resource logic") } IoError(e) => f.write_str(&format!("IoError error: {e}")), @@ -57,6 +59,9 @@ impl Display for TransactionError { InvalidResourceLogicRepresentation => { f.write_str("ResourceLogicRepresentation is not valid, add borsh feature if using native resource logic examples ") } + InconsistentResourceMerkleRoot => { + f.write_str("Resource merkle root is not consistent between the compliance and the resource logic") + } } } } diff --git a/taiga_halo2/src/executable.rs b/taiga_halo2/src/executable.rs index 2be31744..a4b732bc 100644 --- a/taiga_halo2/src/executable.rs +++ b/taiga_halo2/src/executable.rs @@ -1,6 +1,8 @@ +use pasta_curves::pallas; + use crate::{ delta_commitment::DeltaCommitment, error::TransactionError, merkle_tree::Anchor, - nullifier::Nullifier, resource::ResourceCommitment, + nullifier::Nullifier, resource::ResourceCommitment, resource_tree::ResourceMerkleTreeLeaves, }; // Executable is an unified interface for partial transaction, which is the atomic executable uinit. @@ -10,4 +12,16 @@ pub trait Executable { fn get_output_cms(&self) -> Vec; fn get_delta_commitments(&self) -> Vec; fn get_anchors(&self) -> Vec; + fn get_resource_merkle_root(&self) -> pallas::Base { + let mut leaves = vec![]; + self.get_nullifiers() + .iter() + .zip(self.get_output_cms()) + .for_each(|(nf, cm)| { + leaves.push(nf.inner()); + leaves.push(cm.inner()); + }); + let tree = ResourceMerkleTreeLeaves::new(leaves); + tree.root() + } } diff --git a/taiga_halo2/src/lib.rs b/taiga_halo2/src/lib.rs index e03fd487..943b09cc 100644 --- a/taiga_halo2/src/lib.rs +++ b/taiga_halo2/src/lib.rs @@ -15,6 +15,7 @@ pub mod resource; pub mod resource_encryption; pub mod resource_logic_commitment; pub mod resource_logic_vk; +pub mod resource_tree; pub mod shielded_ptx; pub mod taiga_api; pub mod transaction; diff --git a/taiga_halo2/src/merkle_tree.rs b/taiga_halo2/src/merkle_tree.rs index 9674a5ec..466da0b6 100644 --- a/taiga_halo2/src/merkle_tree.rs +++ b/taiga_halo2/src/merkle_tree.rs @@ -3,7 +3,10 @@ use std::hash::{Hash, Hasher}; use crate::merkle_tree::LR::{L, R}; use crate::resource::ResourceCommitment; use crate::utils::poseidon_hash; -use crate::{constant::TAIGA_COMMITMENT_TREE_DEPTH, resource::Resource}; +use crate::{ + constant::{TAIGA_COMMITMENT_TREE_DEPTH, TAIGA_RESOURCE_TREE_DEPTH}, + resource::Resource, +}; use ff::PrimeField; use halo2_proofs::arithmetic::Field; use pasta_curves::pallas; @@ -82,17 +85,22 @@ pub enum LR { L, } -pub fn is_right(p: LR) -> bool { - match p { - R => true, - L => false, +impl LR { + pub fn is_left(&self) -> bool { + match self { + R => false, + L => true, + } } } -pub fn is_left(p: LR) -> bool { - match p { - R => false, - L => true, +impl From for LR { + fn from(b: bool) -> Self { + if b { + LR::L + } else { + LR::R + } } } @@ -139,7 +147,7 @@ impl MerklePath { } /// Returns the input parameters for merkle tree gadget. - pub fn get_path(&self) -> Vec<(pallas::Base, LR)> { + pub fn inner(&self) -> Vec<(pallas::Base, LR)> { self.merkle_path .iter() .map(|(node, b)| (node.inner(), *b)) @@ -156,6 +164,16 @@ impl Default for MerklePath { } } +impl From<[(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH]> for MerklePath { + fn from(path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH]) -> Self { + let merkle_path = path + .into_iter() + .map(|(value, b)| (Node::from(value), b)) + .collect(); + MerklePath { merkle_path } + } +} + /// A node within the Sapling commitment tree. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/taiga_halo2/src/resource.rs b/taiga_halo2/src/resource.rs index 200f5d66..3ef16d87 100644 --- a/taiga_halo2/src/resource.rs +++ b/taiga_halo2/src/resource.rs @@ -1,16 +1,15 @@ use crate::{ circuit::{ resource_logic_circuit::ResourceLogic, - resource_logic_examples::{ - TrivialResourceLogicCircuit, COMPRESSED_TRIVIAL_RESOURCE_LOGIC_VK, - }, + resource_logic_examples::TrivialResourceLogicCircuit, + resource_logic_examples::COMPRESSED_TRIVIAL_RESOURCE_LOGIC_VK, }, constant::{ - NUM_RESOURCE, POSEIDON_TO_CURVE_INPUT_LEN, PRF_EXPAND_PERSONALIZATION, + POSEIDON_TO_CURVE_INPUT_LEN, PRF_EXPAND_PERSONALIZATION, PRF_EXPAND_PERSONALIZATION_TO_FIELD, PRF_EXPAND_PSI, PRF_EXPAND_PUBLIC_INPUT_PADDING, - PRF_EXPAND_RCM, PRF_EXPAND_VCM_R, + PRF_EXPAND_RCM, PRF_EXPAND_VCM_R, TAIGA_RESOURCE_TREE_DEPTH, }, - merkle_tree::{Anchor, MerklePath, Node}, + merkle_tree::{Anchor, MerklePath, Node, LR}, nullifier::{Nullifier, NullifierKeyContainer}, shielded_ptx::ResourceLogicVerifyingInfoSet, utils::{poseidon_hash_n, poseidon_to_curve}, @@ -477,36 +476,13 @@ impl ResourceLogics { ) } - // Create resource logics for an input padding resource - pub fn create_input_padding_resource_resource_logics( - resource: &Resource, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], + // Create resource logics for a padding resource + pub fn create_padding_resource_resource_logics( + resource: Resource, + merkle_path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH], ) -> Self { - let owned_resource_id = resource.get_nf().unwrap().inner(); - let application_resource_logic = Box::new(TrivialResourceLogicCircuit::new( - owned_resource_id, - input_resources, - output_resources, - )); - Self { - application_resource_logic, - dynamic_resource_logics: vec![], - } - } - - // Create resource logics for an output padding resource - pub fn create_output_padding_resource_resource_logics( - resource: &Resource, - input_resources: [Resource; NUM_RESOURCE], - output_resources: [Resource; NUM_RESOURCE], - ) -> Self { - let owned_resource_id = resource.commitment().inner(); - let application_resource_logic = Box::new(TrivialResourceLogicCircuit::new( - owned_resource_id, - input_resources, - output_resources, - )); + let application_resource_logic = + Box::new(TrivialResourceLogicCircuit::new(resource, merkle_path)); Self { application_resource_logic, dynamic_resource_logics: vec![], diff --git a/taiga_halo2/src/resource_logic_vk.rs b/taiga_halo2/src/resource_logic_vk.rs index 3bd67c9e..a5c65d77 100644 --- a/taiga_halo2/src/resource_logic_vk.rs +++ b/taiga_halo2/src/resource_logic_vk.rs @@ -71,59 +71,3 @@ impl PartialEq for ResourceLogicVerifyingKey { } impl Eq for ResourceLogicVerifyingKey {} - -#[test] -fn test_resource_logicd_hashing() { - use crate::circuit::resource_logic_examples::tests::random_trivial_resource_logic_circuit; - use halo2_proofs::plonk; - use rand::rngs::OsRng; - use std::{collections::hash_map::DefaultHasher, hash::Hasher}; - - fn calculate_hash(t: &T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() - } - - let circuit1 = random_trivial_resource_logic_circuit(&mut OsRng); - let circuit2 = random_trivial_resource_logic_circuit(&mut OsRng); - let circuit3 = random_trivial_resource_logic_circuit(&mut OsRng); - - let params1 = halo2_proofs::poly::commitment::Params::new(12); - let vk1 = plonk::keygen_vk(¶ms1, &circuit1).unwrap(); - let resource_logicd1 = ResourceLogicVerifyingKey::from_vk(vk1.clone()); - let vk1s = format!("{:?}", vk1.pinned()); - - let params2 = halo2_proofs::poly::commitment::Params::new(12); - let vk2 = plonk::keygen_vk(¶ms2, &circuit2).unwrap(); - let resource_logicd2 = ResourceLogicVerifyingKey::from_vk(vk2.clone()); - let vk2s = format!("{:?}", vk2.pinned()); - - // Same circuit, same param => same key - assert_eq!(vk1s, vk2s); // check that the keys are actually the same - assert_eq!( - calculate_hash(&resource_logicd1), - calculate_hash(&resource_logicd2) - ); // check that the hashes are the same - assert_eq!(resource_logicd1, resource_logicd2); // check that the resource_logicd's are equal - - let params3 = halo2_proofs::poly::commitment::Params::new(13); // different param => different key - let vk3 = plonk::keygen_vk(¶ms3, &circuit3).unwrap(); - let resource_logicd3 = ResourceLogicVerifyingKey::from_vk(vk3.clone()); - let vk3s = format!("{:?}", vk3.pinned()); - - // Same circuit, different param => different key - assert_ne!(vk1s, vk3s); // check that the keys are actually different - assert_ne!( - calculate_hash(&resource_logicd1), - calculate_hash(&resource_logicd3) - ); // check that the hashes are different - assert_ne!(resource_logicd1, resource_logicd3); // check that the resource_logicd's are not equal - - // test with actual hashset - use std::collections::HashSet; - let mut set = HashSet::new(); - assert!(set.insert(resource_logicd1)); - assert!(!set.insert(resource_logicd2)); - assert!(set.insert(resource_logicd3)); -} diff --git a/taiga_halo2/src/resource_tree.rs b/taiga_halo2/src/resource_tree.rs new file mode 100644 index 00000000..196f6371 --- /dev/null +++ b/taiga_halo2/src/resource_tree.rs @@ -0,0 +1,195 @@ +use crate::{ + constant::{TAIGA_RESOURCE_TREE_DEPTH, TAIGA_RESOURCE_TREE_LEAVES_NUM}, + merkle_tree::{MerklePath, Node, LR}, + resource::Resource, + utils::poseidon_hash, +}; +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; +use pasta_curves::pallas; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ResourceExistenceWitness { + resource: Resource, + merkle_path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH], +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResourceMerkleTreeLeaves(Vec); + +impl ResourceExistenceWitness { + pub fn new( + resource: Resource, + merkle_path: [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH], + ) -> Self { + Self { + resource, + merkle_path, + } + } + + pub fn get_resource(&self) -> Resource { + self.resource + } + + pub fn get_path(&self) -> [(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH] { + self.merkle_path + } + + pub fn is_input(&self) -> bool { + !self.merkle_path[0].1.is_left() + } + + // get input nf or output cm + pub fn get_identity(&self) -> pallas::Base { + if self.is_input() { + self.resource.get_nf().unwrap().inner() + } else { + self.resource.commitment().inner() + } + } + + pub fn get_root(&self) -> pallas::Base { + let id = self.get_identity(); + let node = Node::from(id); + MerklePath::from(self.get_path()).root(node).inner() + } + + // TODO: handle the error + pub fn to_bytes(&self) -> Vec { + bincode::serialize(&self).unwrap() + } + + pub fn from_bytes(bytes: &[u8]) -> Self { + bincode::deserialize(bytes).unwrap() + } +} + +#[cfg(feature = "borsh")] +impl BorshSerialize for ResourceExistenceWitness { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + use byteorder::WriteBytesExt; + use ff::PrimeField; + self.resource.serialize(writer)?; + for node in self.merkle_path { + writer.write_all(&node.0.to_repr())?; + writer.write_u8(if node.1.is_left() { 1 } else { 0 })?; + } + + Ok(()) + } +} + +#[cfg(feature = "borsh")] +impl BorshDeserialize for ResourceExistenceWitness { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + use crate::utils::read_base_field; + use byteorder::ReadBytesExt; + let resource = Resource::deserialize_reader(reader)?; + let mut path = vec![]; + for _ in 0..TAIGA_RESOURCE_TREE_DEPTH { + let v = read_base_field(reader)?; + let byte = reader.read_u8()?; + let b = if byte == 0x01 { LR::L } else { LR::R }; + path.push((v, b)); + } + + Ok(Self { + resource, + merkle_path: path.try_into().unwrap(), + }) + } +} + +impl ResourceMerkleTreeLeaves { + pub fn new(leaves: Vec) -> Self { + assert!( + leaves.len() <= TAIGA_RESOURCE_TREE_LEAVES_NUM, + "The number of leaves exceeds the TAIGA_RESOURCE_TREE_LEAVES_NUM" + ); + ResourceMerkleTreeLeaves(leaves) + } + + pub fn insert(&mut self, value: pallas::Base) { + self.0.push(value) + } + + pub fn root(&self) -> pallas::Base { + let mut cur_layer = self.0.clone(); + cur_layer.resize(TAIGA_RESOURCE_TREE_LEAVES_NUM, pallas::Base::zero()); + while cur_layer.len() > 1 { + cur_layer = cur_layer + .chunks(2) + .map(|pair| poseidon_hash(pair[0], pair[1])) + .collect(); + } + cur_layer[0] + } + + // Generate the merkle path for the current leave + pub fn generate_path( + &self, + cur_leave: pallas::Base, + ) -> Option<[(pallas::Base, LR); TAIGA_RESOURCE_TREE_DEPTH]> { + let mut cur_layer = self.0.clone(); + cur_layer.resize(TAIGA_RESOURCE_TREE_LEAVES_NUM, pallas::Base::zero()); + if let Some(position) = cur_layer.iter().position(|&v| v == cur_leave) { + let mut merkle_path = Vec::new(); + fn build_merkle_path_inner( + cur_layer: Vec, + position: usize, + path: &mut Vec<(pallas::Base, LR)>, + ) { + if cur_layer.len() > 1 { + let sibling = { + let sibling_lr = LR::from(position % 2 != 0); + let sibling_value = match sibling_lr { + LR::L => cur_layer[position - 1], + LR::R => cur_layer[position + 1], + }; + (sibling_value, sibling_lr) + }; + path.push(sibling); + + let prev_layer = cur_layer + .chunks(2) + .map(|pair| poseidon_hash(pair[0], pair[1])) + .collect(); + + build_merkle_path_inner(prev_layer, position / 2, path); + } + } + build_merkle_path_inner(cur_layer, position, &mut merkle_path); + Some(merkle_path.try_into().unwrap()) + } else { + None + } + } +} + +#[test] +fn test_resource_merkle_leave() { + use crate::merkle_tree::{MerklePath, Node}; + + let target_leave = pallas::Base::one(); + let resource_merkle_tree = + ResourceMerkleTreeLeaves::new(vec![pallas::Base::zero(), target_leave]); + let merkle_path = resource_merkle_tree.generate_path(target_leave).unwrap(); + + let mut expected_merkle_path = vec![(pallas::Base::zero(), LR::L)]; + let mut cur_node = pallas::Base::zero(); + (1..TAIGA_RESOURCE_TREE_DEPTH).for_each(|_| { + cur_node = poseidon_hash(cur_node, cur_node); + expected_merkle_path.push((cur_node, LR::R)); + }); + + assert_eq!(merkle_path.to_vec(), expected_merkle_path); + + let merkle_root = resource_merkle_tree.root(); + let path = MerklePath::from(merkle_path); + let target_node = Node::from(target_leave); + let expected_root = path.root(target_node); + + assert_eq!(merkle_root, expected_root.inner()); +} diff --git a/taiga_halo2/src/shielded_ptx.rs b/taiga_halo2/src/shielded_ptx.rs index f3ab9b31..b27bfdba 100644 --- a/taiga_halo2/src/shielded_ptx.rs +++ b/taiga_halo2/src/shielded_ptx.rs @@ -2,7 +2,7 @@ use crate::circuit::resource_logic_circuit::{ResourceLogic, ResourceLogicVerifyi use crate::compliance::{ComplianceInfo, CompliancePublicInputs}; use crate::constant::{ COMPLIANCE_CIRCUIT_PARAMS_SIZE, COMPLIANCE_PROVING_KEY, COMPLIANCE_VERIFYING_KEY, - MAX_DYNAMIC_RESOURCE_LOGIC_NUM, NUM_RESOURCE, SETUP_PARAMS_MAP, + MAX_DYNAMIC_RESOURCE_LOGIC_NUM, SETUP_PARAMS_MAP, }; use crate::delta_commitment::DeltaCommitment; use crate::error::TransactionError; @@ -16,7 +16,7 @@ use pasta_curves::pallas; use rand::RngCore; #[cfg(feature = "nif")] -use rustler::{Decoder, Encoder, Env, NifResult, NifStruct, Term}; +use rustler::NifStruct; #[cfg(feature = "serde")] use serde; @@ -29,10 +29,12 @@ use ff::PrimeField; #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.Shielded.PTX")] pub struct ShieldedPartialTransaction { - compliances: [ComplianceVerifyingInfo; NUM_RESOURCE], - inputs: [ResourceLogicVerifyingInfoSet; NUM_RESOURCE], - outputs: [ResourceLogicVerifyingInfoSet; NUM_RESOURCE], + compliances: Vec, + inputs: Vec, + outputs: Vec, binding_sig_r: Option, hints: Vec, } @@ -59,18 +61,6 @@ pub struct ResourceLogicVerifyingInfoSet { // When the verifier proof is added, we may need to reconsider the structure of `ResourceLogicVerifyingInfo` } -// Is easier to derive traits for -#[derive(Debug, Clone)] -#[cfg_attr(feature = "nif", derive(NifStruct))] -#[cfg_attr(feature = "nif", module = "Taiga.Shielded.PTX")] -struct ShieldedPartialTransactionProxy { - compliances: Vec, - inputs: Vec, - outputs: Vec, - binding_sig_r: Option, - hints: Vec, -} - impl ShieldedPartialTransaction { pub fn from_bytecode( compliances: Vec, @@ -97,9 +87,9 @@ impl ShieldedPartialTransaction { .collect(); Ok(Self { - compliances: compliances.try_into().unwrap(), - inputs: inputs?.try_into().unwrap(), - outputs: outputs?.try_into().unwrap(), + compliances, + inputs: inputs?, + outputs: outputs?, binding_sig_r: Some(rcv_sum), hints, }) @@ -135,9 +125,9 @@ impl ShieldedPartialTransaction { .collect(); Ok(Self { - compliances: compliances.try_into().unwrap(), - inputs: inputs.try_into().unwrap(), - outputs: outputs.try_into().unwrap(), + compliances, + inputs, + outputs, binding_sig_r: Some(rcv_sum), hints, }) @@ -162,39 +152,40 @@ impl ShieldedPartialTransaction { Ok(()) } - // check the nullifiers are from compliance proofs - fn check_nullifiers(&self) -> Result<(), TransactionError> { - assert_eq!(NUM_RESOURCE, 2); - let compliance_nfs = self.get_nullifiers(); + // check resource merkle roots + fn check_resource_merkle_roots(&self) -> Result<(), TransactionError> { + let root_from_compliance = self.get_resource_merkle_root(); for resource_logic_info in self.inputs.iter().chain(self.outputs.iter()) { - for nfs in resource_logic_info.get_nullifiers().iter() { - // Check the resource logic actually uses the input resources from compliance circuits. - if !((compliance_nfs[0].inner() == nfs[0] && compliance_nfs[1].inner() == nfs[1]) - || (compliance_nfs[0].inner() == nfs[1] && compliance_nfs[1].inner() == nfs[0])) - { + for root in resource_logic_info.get_resource_merkle_roots() { + if root_from_compliance != root { return Err(TransactionError::InconsistentNullifier); } } } + Ok(()) + } + + // check the nullifiers are from compliance proofs + fn check_nullifiers(&self) -> Result<(), TransactionError> { + let compliance_nfs = self.get_nullifiers(); for (resource_logic_info, compliance_nf) in self.inputs.iter().zip(compliance_nfs.iter()) { - // Check the app resource logic and the sub resource logics use the same owned_resource_id in one resource - let owned_resource_id = resource_logic_info + // Check the app resource logic and the sub resource logics use the same self_resource_id in one resource + let self_resource_id = resource_logic_info .app_resource_logic_verifying_info - .get_owned_resource_id(); + .get_self_resource_id(); for logic_resource_logic_verifying_info in resource_logic_info .app_dynamic_resource_logic_verifying_info .iter() { - if owned_resource_id != logic_resource_logic_verifying_info.get_owned_resource_id() - { - return Err(TransactionError::InconsistentOwnedResourceID); + if self_resource_id != logic_resource_logic_verifying_info.get_self_resource_id() { + return Err(TransactionError::InconsistentSelfResourceID); } } - // Check the owned_resource_id that resource logic uses is consistent with the nf from the compliance circuit - if owned_resource_id != compliance_nf.inner() { - return Err(TransactionError::InconsistentOwnedResourceID); + // Check the self_resource_id that resource logic uses is consistent with the nf from the compliance circuit + if self_resource_id != compliance_nf.inner() { + return Err(TransactionError::InconsistentSelfResourceID); } } Ok(()) @@ -202,53 +193,29 @@ impl ShieldedPartialTransaction { // check the output cms are from compliance proofs fn check_resource_commitments(&self) -> Result<(), TransactionError> { - assert_eq!(NUM_RESOURCE, 2); let compliance_cms = self.get_output_cms(); - for resource_logic_info in self.inputs.iter().chain(self.outputs.iter()) { - for cms in resource_logic_info.get_resource_commitments().iter() { - // Check the resource logic actually uses the output resources from compliance circuits. - if !((compliance_cms[0] == cms[0] && compliance_cms[1] == cms[1]) - || (compliance_cms[0] == cms[1] && compliance_cms[1] == cms[0])) - { - return Err(TransactionError::InconsistentOutputResourceCommitment); - } - } - } - for (resource_logic_info, compliance_cm) in self.outputs.iter().zip(compliance_cms.iter()) { - // Check that the app resource logic and the sub resource_logics use the same owned_resource_id in one resource - let owned_resource_id = resource_logic_info + // Check that the app resource logic and the sub resource_logics use the same self_resource_id in one resource + let self_resource_id = resource_logic_info .app_resource_logic_verifying_info - .get_owned_resource_id(); + .get_self_resource_id(); for logic_resource_logic_verifying_info in resource_logic_info .app_dynamic_resource_logic_verifying_info .iter() { - if owned_resource_id != logic_resource_logic_verifying_info.get_owned_resource_id() - { - return Err(TransactionError::InconsistentOwnedResourceID); + if self_resource_id != logic_resource_logic_verifying_info.get_self_resource_id() { + return Err(TransactionError::InconsistentSelfResourceID); } } - // Check the owned_resource_id that resource logic uses is consistent with the cm from the compliance circuit - if owned_resource_id != compliance_cm.inner() { - return Err(TransactionError::InconsistentOwnedResourceID); + // Check the self_resource_id that resource logic uses is consistent with the cm from the compliance circuit + if self_resource_id != compliance_cm.inner() { + return Err(TransactionError::InconsistentSelfResourceID); } } Ok(()) } - // Conversion to the generic length proxy - fn to_proxy(&self) -> ShieldedPartialTransactionProxy { - ShieldedPartialTransactionProxy { - compliances: self.compliances.to_vec(), - inputs: self.inputs.to_vec(), - outputs: self.outputs.to_vec(), - binding_sig_r: self.binding_sig_r, - hints: self.hints.clone(), - } - } - pub fn get_binding_sig_r(&self) -> Option { self.binding_sig_r } @@ -263,26 +230,12 @@ impl ShieldedPartialTransaction { } } -impl ShieldedPartialTransactionProxy { - fn to_concrete(&self) -> Option { - let compliances = self.compliances.clone().try_into().ok()?; - let inputs = self.inputs.clone().try_into().ok()?; - let outputs = self.outputs.clone().try_into().ok()?; - Some(ShieldedPartialTransaction { - compliances, - inputs, - outputs, - binding_sig_r: self.binding_sig_r, - hints: self.hints.clone(), - }) - } -} - impl Executable for ShieldedPartialTransaction { fn execute(&self) -> Result<(), TransactionError> { self.verify_proof()?; self.check_nullifiers()?; self.check_resource_commitments()?; + self.check_resource_merkle_roots()?; Ok(()) } @@ -319,17 +272,9 @@ impl Executable for ShieldedPartialTransaction { impl BorshSerialize for ShieldedPartialTransaction { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { use byteorder::WriteBytesExt; - for compliance in self.compliances.iter() { - compliance.serialize(writer)?; - } - - for input in self.inputs.iter() { - input.serialize(writer)?; - } - - for output in self.outputs.iter() { - output.serialize(writer)?; - } + self.compliances.serialize(writer)?; + self.inputs.serialize(writer)?; + self.outputs.serialize(writer)?; // Write binding_sig_r match self.binding_sig_r { @@ -352,15 +297,9 @@ impl BorshSerialize for ShieldedPartialTransaction { impl BorshDeserialize for ShieldedPartialTransaction { fn deserialize_reader(reader: &mut R) -> std::io::Result { use byteorder::ReadBytesExt; - let compliances: Vec<_> = (0..NUM_RESOURCE) - .map(|_| ComplianceVerifyingInfo::deserialize_reader(reader)) - .collect::>()?; - let inputs: Vec<_> = (0..NUM_RESOURCE) - .map(|_| ResourceLogicVerifyingInfoSet::deserialize_reader(reader)) - .collect::>()?; - let outputs: Vec<_> = (0..NUM_RESOURCE) - .map(|_| ResourceLogicVerifyingInfoSet::deserialize_reader(reader)) - .collect::>()?; + let compliances = Vec::::deserialize_reader(reader)?; + let inputs = Vec::::deserialize_reader(reader)?; + let outputs = Vec::::deserialize_reader(reader)?; let binding_sig_r_type = reader.read_u8()?; let binding_sig_r = if binding_sig_r_type == 0 { None @@ -371,31 +310,15 @@ impl BorshDeserialize for ShieldedPartialTransaction { let hints = Vec::::deserialize_reader(reader)?; Ok(ShieldedPartialTransaction { - compliances: compliances.try_into().unwrap(), - inputs: inputs.try_into().unwrap(), - outputs: outputs.try_into().unwrap(), + compliances, + inputs, + outputs, binding_sig_r, hints, }) } } -#[cfg(feature = "nif")] -impl Encoder for ShieldedPartialTransaction { - fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { - self.to_proxy().encode(env) - } -} - -#[cfg(feature = "nif")] -impl<'a> Decoder<'a> for ShieldedPartialTransaction { - fn decode(term: Term<'a>) -> NifResult { - let val: ShieldedPartialTransactionProxy = Decoder::decode(term)?; - val.to_concrete() - .ok_or(rustler::Error::RaiseAtom("Could not decode proxy")) - } -} - impl ComplianceVerifyingInfo { pub fn create(compliance_info: &ComplianceInfo, mut rng: R) -> Result { let (compliance_instance, circuit) = compliance_info.build(); @@ -474,37 +397,31 @@ impl ResourceLogicVerifyingInfoSet { Ok(()) } - pub fn get_nullifiers(&self) -> Vec<[pallas::Base; NUM_RESOURCE]> { - let mut nfs = vec![self.app_resource_logic_verifying_info.get_nullifiers()]; - self.app_dynamic_resource_logic_verifying_info + pub fn get_resource_merkle_roots(&self) -> Vec { + let mut roots: Vec = self + .app_dynamic_resource_logic_verifying_info .iter() - .for_each(|resource_logic_info| nfs.push(resource_logic_info.get_nullifiers())); - nfs - } - - pub fn get_resource_commitments(&self) -> Vec<[ResourceCommitment; NUM_RESOURCE]> { - let mut cms = vec![self - .app_resource_logic_verifying_info - .get_resource_commitments()]; - self.app_dynamic_resource_logic_verifying_info - .iter() - .for_each(|resource_logic_info| { - cms.push(resource_logic_info.get_resource_commitments()) - }); - cms + .map(|info| info.get_resource_merkle_root()) + .collect(); + roots.push( + self.app_resource_logic_verifying_info + .get_resource_merkle_root(), + ); + roots } } #[cfg(test)] pub mod testing { use crate::{ - circuit::resource_logic_circuit::{ResourceLogic, ResourceLogicVerifyingInfoTrait}, + circuit::resource_logic_circuit::ResourceLogicVerifyingInfoTrait, circuit::resource_logic_examples::TrivialResourceLogicCircuit, compliance::ComplianceInfo, constant::TAIGA_COMMITMENT_TREE_DEPTH, merkle_tree::MerklePath, nullifier::Nullifier, resource::{Resource, ResourceLogics}, + resource_tree::ResourceMerkleTreeLeaves, shielded_ptx::ShieldedPartialTransaction, utils::poseidon_hash, }; @@ -629,49 +546,71 @@ pub mod testing { &mut rng, ); - // Create resource logic circuit and fill the resource info - let mut trivial_resource_logic_circuit = TrivialResourceLogicCircuit { - owned_resource_id: input_resource_1.get_nf().unwrap().inner(), - input_resources: [input_resource_1, input_resource_2], - output_resources: [output_resource_1, output_resource_2], + // Collect resource merkle leaves + let input_resource_nf_1 = input_resource_1.get_nf().unwrap().inner(); + let output_resource_cm_1 = output_resource_1.commitment().inner(); + let input_resource_nf_2 = input_resource_2.get_nf().unwrap().inner(); + let output_resource_cm_2 = output_resource_2.commitment().inner(); + let resource_merkle_tree = ResourceMerkleTreeLeaves::new(vec![ + input_resource_nf_1, + output_resource_cm_1, + input_resource_nf_2, + output_resource_cm_2, + ]); + + // Create resource logic circuit and complete the resource info + let input_resource_resource_logics_1 = { + let input_resource_path_1 = resource_merkle_tree + .generate_path(input_resource_nf_1) + .unwrap(); + let input_resource_application_logic_1 = + TrivialResourceLogicCircuit::new(input_resource_1, input_resource_path_1); + ResourceLogics::new( + Box::new(input_resource_application_logic_1.clone()), + vec![ + Box::new(input_resource_application_logic_1.clone()), + Box::new(input_resource_application_logic_1), + ], + ) }; - let input_application_resource_logic_1 = Box::new(trivial_resource_logic_circuit.clone()); - let trivial_app_logic_1: Box = - Box::new(trivial_resource_logic_circuit.clone()); - let trivial_app_logic_2 = Box::new(trivial_resource_logic_circuit.clone()); - let trivial_dynamic_resource_logics = vec![trivial_app_logic_1, trivial_app_logic_2]; - let input_resource_1_resource_logics = ResourceLogics::new( - input_application_resource_logic_1, - trivial_dynamic_resource_logics, - ); - // The following resources use empty logic resource_logics and use value with pallas::Base::zero() by default. - trivial_resource_logic_circuit.owned_resource_id = - input_resource_2.get_nf().unwrap().inner(); - let input_application_resource_logic_2 = Box::new(trivial_resource_logic_circuit.clone()); - let input_resource_2_resource_logics = - ResourceLogics::new(input_application_resource_logic_2, vec![]); + let output_resource_resource_logics_1 = { + let output_resource_path_1 = resource_merkle_tree + .generate_path(output_resource_cm_1) + .unwrap(); + let output_resource_application_logic_1 = + TrivialResourceLogicCircuit::new(output_resource_1, output_resource_path_1); + ResourceLogics::new(Box::new(output_resource_application_logic_1), vec![]) + }; - trivial_resource_logic_circuit.owned_resource_id = output_resource_1.commitment().inner(); - let output_application_resource_logic_1 = Box::new(trivial_resource_logic_circuit.clone()); - let output_resource_1_resource_logics = - ResourceLogics::new(output_application_resource_logic_1, vec![]); + let input_resource_resource_logics_2 = { + let input_resource_path_2 = resource_merkle_tree + .generate_path(input_resource_nf_2) + .unwrap(); + let input_resource_application_logic_2 = + TrivialResourceLogicCircuit::new(input_resource_2, input_resource_path_2); + ResourceLogics::new(Box::new(input_resource_application_logic_2), vec![]) + }; - trivial_resource_logic_circuit.owned_resource_id = output_resource_2.commitment().inner(); - let output_application_resource_logic_2 = Box::new(trivial_resource_logic_circuit); - let output_resource_2_resource_logics = - ResourceLogics::new(output_application_resource_logic_2, vec![]); + let output_resource_resource_logics_2 = { + let output_resource_path_2 = resource_merkle_tree + .generate_path(output_resource_cm_2) + .unwrap(); + let output_resource_application_logic_2 = + TrivialResourceLogicCircuit::new(output_resource_2, output_resource_path_2); + ResourceLogics::new(Box::new(output_resource_application_logic_2), vec![]) + }; // Create shielded partial tx ShieldedPartialTransaction::build( vec![compliance_1, compliance_2], vec![ - input_resource_1_resource_logics, - input_resource_2_resource_logics, + input_resource_resource_logics_1, + input_resource_resource_logics_2, ], vec![ - output_resource_1_resource_logics, - output_resource_2_resource_logics, + output_resource_resource_logics_1, + output_resource_resource_logics_2, ], vec![], &mut rng, diff --git a/taiga_halo2/src/taiga_api.rs b/taiga_halo2/src/taiga_api.rs index e2840b3c..74d93c06 100644 --- a/taiga_halo2/src/taiga_api.rs +++ b/taiga_halo2/src/taiga_api.rs @@ -228,7 +228,7 @@ pub fn verify_shielded_partial_transaction(ptx_bytes: Vec) -> Result<(), Tra pub mod tests { use crate::{ nullifier::tests::random_nullifier_key_commitment, resource::tests::random_resource, - taiga_api::*, + resource_tree::ResourceMerkleTreeLeaves, taiga_api::*, }; use rand::rngs::OsRng; @@ -264,7 +264,6 @@ pub mod tests { // construct resources let input_resource_1 = random_resource(&mut rng); - let input_resource_1_nf = input_resource_1.get_nf().unwrap(); let mut output_resource_1 = random_resource(&mut rng); let merkle_path_1 = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); let compliance_1 = ComplianceInfo::new( @@ -276,7 +275,6 @@ pub mod tests { ); let input_resource_2 = random_resource(&mut rng); - let input_resource_2_nf = input_resource_2.get_nf().unwrap(); let mut output_resource_2 = random_resource(&mut rng); let merkle_path_2 = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); let compliance_2 = ComplianceInfo::new( @@ -287,45 +285,57 @@ pub mod tests { &mut rng, ); + // Collect resource merkle leaves + let input_resource_nf_1 = input_resource_1.get_nf().unwrap().inner(); + let output_resource_cm_1 = output_resource_1.commitment().inner(); + let input_resource_nf_2 = input_resource_2.get_nf().unwrap().inner(); + let output_resource_cm_2 = output_resource_2.commitment().inner(); + let resource_merkle_tree = ResourceMerkleTreeLeaves::new(vec![ + input_resource_nf_1, + output_resource_cm_1, + input_resource_nf_2, + output_resource_cm_2, + ]); + // construct applications let input_resource_1_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - input_resource_1_nf.inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let input_resource_path_1 = resource_merkle_tree + .generate_path(input_resource_nf_1) + .unwrap(); + let input_resource_application_logic_1 = + TrivialResourceLogicCircuit::new(input_resource_1, input_resource_path_1); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(input_resource_application_logic_1.to_bytecode(), vec![]) }; let input_resource_2_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - input_resource_2_nf.inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let input_resource_path_2 = resource_merkle_tree + .generate_path(input_resource_nf_2) + .unwrap(); + let input_resource_application_logic_2 = + TrivialResourceLogicCircuit::new(input_resource_2, input_resource_path_2); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(input_resource_application_logic_2.to_bytecode(), vec![]) }; let output_resource_1_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - output_resource_1.commitment().inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let output_resource_path_1 = resource_merkle_tree + .generate_path(output_resource_cm_1) + .unwrap(); + let output_resource_application_logic_1 = + TrivialResourceLogicCircuit::new(output_resource_1, output_resource_path_1); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(output_resource_application_logic_1.to_bytecode(), vec![]) }; let output_resource_2_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - output_resource_2.commitment().inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let output_resource_path_2 = resource_merkle_tree + .generate_path(output_resource_cm_2) + .unwrap(); + let output_resource_application_logic_2 = + TrivialResourceLogicCircuit::new(output_resource_2, output_resource_path_2); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(output_resource_application_logic_2.to_bytecode(), vec![]) }; // construct ptx diff --git a/taiga_halo2/src/transparent_ptx.rs b/taiga_halo2/src/transparent_ptx.rs index 937e8241..ce38ab31 100644 --- a/taiga_halo2/src/transparent_ptx.rs +++ b/taiga_halo2/src/transparent_ptx.rs @@ -1,8 +1,7 @@ use crate::{ circuit::resource_logic_bytecode::ApplicationByteCode, compliance::ComplianceInfo, - constant::NUM_RESOURCE, delta_commitment::DeltaCommitment, error::TransactionError, - executable::Executable, merkle_tree::Anchor, nullifier::Nullifier, - resource::ResourceCommitment, + delta_commitment::DeltaCommitment, error::TransactionError, executable::Executable, + merkle_tree::Anchor, nullifier::Nullifier, resource::ResourceCommitment, }; use pasta_curves::pallas; @@ -29,10 +28,6 @@ impl TransparentPartialTransaction { output_resource_app: Vec, hints: Vec, ) -> Self { - assert_eq!(compliances.len(), NUM_RESOURCE); - assert_eq!(input_resource_app.len(), NUM_RESOURCE); - assert_eq!(output_resource_app.len(), NUM_RESOURCE); - Self { compliances, input_resource_app, @@ -47,21 +42,22 @@ impl Executable for TransparentPartialTransaction { // check resource logics, nullifiers, and resource commitments let compliance_nfs = self.get_nullifiers(); let compliance_cms = self.get_output_cms(); + let compliance_resource_merkle_root = self.get_resource_merkle_root(); for (resource_logic, nf) in self.input_resource_app.iter().zip(compliance_nfs.iter()) { - let owned_resource_id = - resource_logic.verify_transparently(&compliance_nfs, &compliance_cms)?; + let self_resource_id = + resource_logic.verify_transparently(&compliance_resource_merkle_root)?; // Make sure all resource logics are checked - if owned_resource_id != nf.inner() { - return Err(TransactionError::InconsistentOwnedResourceID); + if self_resource_id != nf.inner() { + return Err(TransactionError::InconsistentSelfResourceID); } } for (resource_logic, cm) in self.output_resource_app.iter().zip(compliance_cms.iter()) { - let owned_resource_id = - resource_logic.verify_transparently(&compliance_nfs, &compliance_cms)?; + let self_resource_id = + resource_logic.verify_transparently(&compliance_resource_merkle_root)?; // Make sure all resource logics are checked - if owned_resource_id != cm.inner() { - return Err(TransactionError::InconsistentOwnedResourceID); + if self_resource_id != cm.inner() { + return Err(TransactionError::InconsistentSelfResourceID); } } @@ -107,7 +103,8 @@ pub mod testing { use crate::{ circuit::resource_logic_examples::TrivialResourceLogicCircuit, constant::TAIGA_COMMITMENT_TREE_DEPTH, merkle_tree::MerklePath, - resource::tests::random_resource, transparent_ptx::*, + resource::tests::random_resource, resource_tree::ResourceMerkleTreeLeaves, + transparent_ptx::*, }; use rand::rngs::OsRng; @@ -146,45 +143,57 @@ pub mod testing { &mut rng, ); + // Collect resource merkle leaves + let input_resource_nf_1 = input_resource_1.get_nf().unwrap().inner(); + let output_resource_cm_1 = output_resource_1.commitment().inner(); + let input_resource_nf_2 = input_resource_2.get_nf().unwrap().inner(); + let output_resource_cm_2 = output_resource_2.commitment().inner(); + let resource_merkle_tree = ResourceMerkleTreeLeaves::new(vec![ + input_resource_nf_1, + output_resource_cm_1, + input_resource_nf_2, + output_resource_cm_2, + ]); + // construct applications let input_resource_1_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - input_resource_1.get_nf().unwrap().inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let input_resource_path_1 = resource_merkle_tree + .generate_path(input_resource_nf_1) + .unwrap(); + let input_resource_application_logic_1 = + TrivialResourceLogicCircuit::new(input_resource_1, input_resource_path_1); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(input_resource_application_logic_1.to_bytecode(), vec![]) }; let input_resource_2_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - input_resource_2.get_nf().unwrap().inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let input_resource_path_2 = resource_merkle_tree + .generate_path(input_resource_nf_2) + .unwrap(); + let input_resource_application_logic_2 = + TrivialResourceLogicCircuit::new(input_resource_2, input_resource_path_2); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(input_resource_application_logic_2.to_bytecode(), vec![]) }; let output_resource_1_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - output_resource_1.commitment().inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let output_resource_path_1 = resource_merkle_tree + .generate_path(output_resource_cm_1) + .unwrap(); + let output_resource_application_logic_1 = + TrivialResourceLogicCircuit::new(output_resource_1, output_resource_path_1); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(output_resource_application_logic_1.to_bytecode(), vec![]) }; let output_resource_2_app = { - let app_resource_logic = TrivialResourceLogicCircuit::new( - output_resource_2.commitment().inner(), - [input_resource_1, input_resource_2], - [output_resource_1, output_resource_2], - ); + let output_resource_path_2 = resource_merkle_tree + .generate_path(output_resource_cm_2) + .unwrap(); + let output_resource_application_logic_2 = + TrivialResourceLogicCircuit::new(output_resource_2, output_resource_path_2); - ApplicationByteCode::new(app_resource_logic.to_bytecode(), vec![]) + ApplicationByteCode::new(output_resource_application_logic_2.to_bytecode(), vec![]) }; TransparentPartialTransaction::new(