diff --git a/taiga_halo2/examples/tx_examples/main.rs b/taiga_halo2/examples/tx_examples/main.rs index 41ab3299..e0cf8fb7 100644 --- a/taiga_halo2/examples/tx_examples/main.rs +++ b/taiga_halo2/examples/tx_examples/main.rs @@ -1,4 +1,4 @@ -// mod partial_fulfillment_token_swap; +mod partial_fulfillment_token_swap; mod token; mod token_swap_with_intent; mod token_swap_without_intent; @@ -12,6 +12,6 @@ fn main() { let tx = token_swap_with_intent::create_token_swap_intent_transaction(rng); tx.execute().unwrap(); - // let tx = partial_fulfillment_token_swap::create_token_swap_transaction(rng); - // tx.execute().unwrap(); + let tx = partial_fulfillment_token_swap::create_token_swap_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 aa6bd21d..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 { - self_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 { - self_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/src/circuit/resource_logic_bytecode.rs b/taiga_halo2/src/circuit/resource_logic_bytecode.rs index 7fd37506..c5a3bb26 100644 --- a/taiga_halo2/src/circuit/resource_logic_bytecode.rs +++ b/taiga_halo2/src/circuit/resource_logic_bytecode.rs @@ -3,7 +3,7 @@ use crate::circuit::resource_logic_examples::TrivialResourceLogicCircuit; #[cfg(feature = "examples")] use crate::circuit::resource_logic_examples::{ or_relation_intent::OrRelationIntentResourceLogicCircuit, - // partial_fulfillment_intent::PartialFulfillmentIntentResourceLogicCircuit, + partial_fulfillment_intent::PartialFulfillmentIntentResourceLogicCircuit, receiver_resource_logic::ReceiverResourceLogicCircuit, signature_verification::SignatureVerificationResourceLogicCircuit, token::TokenResourceLogicCircuit, @@ -101,12 +101,12 @@ impl ResourceLogicByteCode { let resource_logic = ReceiverResourceLogicCircuit::from_bytes(&self.inputs); Ok(resource_logic.get_verifying_info()) } - // #[cfg(feature = "examples")] - // ResourceLogicRepresentation::PartialFulfillmentIntent => { - // let resource_logic = - // PartialFulfillmentIntentResourceLogicCircuit::from_bytes(&self.inputs); - // Ok(resource_logic.get_verifying_info()) - // } + #[cfg(feature = "examples")] + ResourceLogicRepresentation::PartialFulfillmentIntent => { + let resource_logic = + PartialFulfillmentIntentResourceLogicCircuit::from_bytes(&self.inputs); + Ok(resource_logic.get_verifying_info()) + } #[cfg(feature = "examples")] ResourceLogicRepresentation::OrRelationIntent => { let resource_logic = OrRelationIntentResourceLogicCircuit::from_bytes(&self.inputs); @@ -157,12 +157,12 @@ impl ResourceLogicByteCode { let resource_logic = ReceiverResourceLogicCircuit::from_bytes(&self.inputs); resource_logic.verify_transparently()? } - // #[cfg(feature = "examples")] - // ResourceLogicRepresentation::PartialFulfillmentIntent => { - // let resource_logic = - // PartialFulfillmentIntentResourceLogicCircuit::from_bytes(&self.inputs); - // resource_logic.verify_transparently()? - // } + #[cfg(feature = "examples")] + ResourceLogicRepresentation::PartialFulfillmentIntent => { + let resource_logic = + PartialFulfillmentIntentResourceLogicCircuit::from_bytes(&self.inputs); + resource_logic.verify_transparently()? + } #[cfg(feature = "examples")] ResourceLogicRepresentation::OrRelationIntent => { let resource_logic = OrRelationIntentResourceLogicCircuit::from_bytes(&self.inputs); diff --git a/taiga_halo2/src/circuit/resource_logic_examples.rs b/taiga_halo2/src/circuit/resource_logic_examples.rs index 3edd70ab..b9a15473 100644 --- a/taiga_halo2/src/circuit/resource_logic_examples.rs +++ b/taiga_halo2/src/circuit/resource_logic_examples.rs @@ -29,8 +29,8 @@ use rand::{rngs::OsRng, RngCore}; mod field_addition; #[cfg(feature = "examples")] pub mod or_relation_intent; -// #[cfg(feature = "examples")] -// pub mod partial_fulfillment_intent; +#[cfg(feature = "examples")] +pub mod partial_fulfillment_intent; #[cfg(feature = "examples")] pub mod receiver_resource_logic; #[cfg(feature = "examples")] 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 7459f691..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 self_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 self_resource_id = basic_variables.get_self_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"), - &self_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"), - &self_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_self_resource_id(&self) -> pallas::Base { - self.self_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.self_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 self_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 { - self_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 { - self_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 { - self_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 { - self_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/resource.rs b/taiga_halo2/src/resource.rs index 658e08e6..3ef16d87 100644 --- a/taiga_halo2/src/resource.rs +++ b/taiga_halo2/src/resource.rs @@ -1,14 +1,15 @@ use crate::{ circuit::{ resource_logic_circuit::ResourceLogic, + resource_logic_examples::TrivialResourceLogicCircuit, resource_logic_examples::COMPRESSED_TRIVIAL_RESOURCE_LOGIC_VK, }, constant::{ 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}, @@ -475,41 +476,18 @@ 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], - // ) -> Self { - // let self_resource_id = resource.get_nf().unwrap().inner(); - // let application_resource_logic = Box::new(TrivialResourceLogicCircuit::new( - // self_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 self_resource_id = resource.commitment().inner(); - // let application_resource_logic = Box::new(TrivialResourceLogicCircuit::new( - // self_resource_id, - // input_resources, - // output_resources, - // )); - // Self { - // application_resource_logic, - // dynamic_resource_logics: vec![], - // } - // } + // 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 application_resource_logic = + Box::new(TrivialResourceLogicCircuit::new(resource, merkle_path)); + Self { + application_resource_logic, + dynamic_resource_logics: vec![], + } + } } #[cfg(test)]