diff --git a/taiga_halo2/src/circuit/vp_bytecode.rs b/taiga_halo2/src/circuit/vp_bytecode.rs index afff7eac..fc6b9f43 100644 --- a/taiga_halo2/src/circuit/vp_bytecode.rs +++ b/taiga_halo2/src/circuit/vp_bytecode.rs @@ -2,6 +2,7 @@ use crate::circuit::vp_examples::TrivialValidityPredicateCircuit; #[cfg(feature = "examples")] use crate::circuit::vp_examples::{ + partial_fulfillment_intent::PartialFulfillmentIntentValidityPredicateCircuit, receiver_vp::ReceiverValidityPredicateCircuit, signature_verification::SignatureVerificationValidityPredicateCircuit, token::TokenValidityPredicateCircuit, @@ -40,7 +41,8 @@ pub enum ValidityPredicateRepresentation { Token, SignatureVerification, Receiver, - // TODO: add other vp types here if needed + PartialFulfillmentIntent, + // Add other native vp types here if needed } #[derive(Clone, Debug)] @@ -98,6 +100,11 @@ impl ValidityPredicateByteCode { let vp = ReceiverValidityPredicateCircuit::from_bytes(&self.inputs); Ok(vp.get_verifying_info()) } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::PartialFulfillmentIntent => { + let vp = PartialFulfillmentIntentValidityPredicateCircuit::from_bytes(&self.inputs); + Ok(vp.get_verifying_info()) + } #[allow(unreachable_patterns)] _ => Err(TransactionError::InvalidValidityPredicateRepresentation), } @@ -143,6 +150,11 @@ impl ValidityPredicateByteCode { let vp = ReceiverValidityPredicateCircuit::from_bytes(&self.inputs); vp.verify_transparently()? } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::PartialFulfillmentIntent => { + let vp = PartialFulfillmentIntentValidityPredicateCircuit::from_bytes(&self.inputs); + vp.verify_transparently()? + } #[allow(unreachable_patterns)] _ => return Err(TransactionError::InvalidValidityPredicateRepresentation), }; diff --git a/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs b/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs index bff301f8..dbba58d9 100644 --- a/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs +++ b/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs @@ -11,6 +11,7 @@ use crate::{ sub::{SubChip, SubInstructions}, target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, }, + vp_bytecode::{ValidityPredicateByteCode, ValidityPredicateRepresentation}, vp_circuit::{ BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, @@ -23,12 +24,13 @@ use crate::{ vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; +use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::{ circuit::{floor_planner, Layouter}, plonk::{keygen_pk, keygen_vk, Circuit, ConstraintSystem, Error}, }; use lazy_static::lazy_static; -use pasta_curves::pallas; +use pasta_curves::{group::ff::PrimeField, pallas}; use rand::rngs::OsRng; use rand::RngCore; @@ -54,6 +56,23 @@ pub struct PartialFulfillmentIntentValidityPredicateCircuit { pub swap: Swap, } +impl PartialFulfillmentIntentValidityPredicateCircuit { + pub fn to_bytecode(&self) -> ValidityPredicateByteCode { + ValidityPredicateByteCode::new( + ValidityPredicateRepresentation::PartialFulfillmentIntent, + 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 ValidityPredicateCircuit for PartialFulfillmentIntentValidityPredicateCircuit { // Add custom constraints fn custom_constraints( @@ -178,6 +197,49 @@ impl ValidityPredicateCircuit for PartialFulfillmentIntentValidityPredicateCircu vp_circuit_impl!(PartialFulfillmentIntentValidityPredicateCircuit); vp_verifying_info_impl!(PartialFulfillmentIntentValidityPredicateCircuit); +impl BorshSerialize for PartialFulfillmentIntentValidityPredicateCircuit { + 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.swap.serialize(writer)?; + + Ok(()) + } +} + +impl BorshDeserialize for PartialFulfillmentIntentValidityPredicateCircuit { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let owned_resource_id_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let owned_resource_id = Option::from(pallas::Base::from_repr(owned_resource_id_bytes)) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "owned_resource_id not in field", + ) + })?; + 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 swap = Swap::deserialize_reader(reader)?; + Ok(Self { + owned_resource_id, + input_resources: input_resources.try_into().unwrap(), + output_resources: output_resources.try_into().unwrap(), + swap, + }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -278,6 +340,13 @@ mod tests { output_resources, swap, }; + + // Test serialization + let circuit = { + let circuit_bytes = circuit.to_bytes(); + PartialFulfillmentIntentValidityPredicateCircuit::from_bytes(&circuit_bytes) + }; + let public_inputs = circuit.get_public_inputs(&mut rng); let prover = MockProver::::run( diff --git a/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent/swap.rs b/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent/swap.rs index 549cb166..ae391226 100644 --- a/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent/swap.rs +++ b/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent/swap.rs @@ -8,6 +8,7 @@ use crate::{ resource::Resource, utils::poseidon_hash_n, }; +use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::arithmetic::Field; use halo2_proofs::{ circuit::{Layouter, Value}, @@ -16,7 +17,7 @@ use halo2_proofs::{ use pasta_curves::pallas; use rand::RngCore; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, BorshSerialize, BorshDeserialize)] pub struct Swap { pub sell: TokenResource, pub buy: Token, diff --git a/taiga_halo2/src/circuit/vp_examples/token.rs b/taiga_halo2/src/circuit/vp_examples/token.rs index 327e98c8..bdc994d2 100644 --- a/taiga_halo2/src/circuit/vp_examples/token.rs +++ b/taiga_halo2/src/circuit/vp_examples/token.rs @@ -64,7 +64,7 @@ impl TokenName { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, BorshSerialize, BorshDeserialize)] pub struct Token { name: TokenName, quantity: u64, @@ -147,7 +147,7 @@ impl Token { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] pub struct TokenResource { pub token_name: TokenName, pub resource: Resource, @@ -537,9 +537,7 @@ impl BorshSerialize for TokenValidityPredicateCircuit { } self.token_name.serialize(writer)?; - - writer.write_all(&self.auth.pk.to_bytes())?; - writer.write_all(&self.auth.vk.to_repr())?; + self.auth.serialize(writer)?; writer.write_all(&self.receiver_vp_vk.to_repr())?; self.rseed.serialize(writer)?; @@ -564,18 +562,7 @@ impl BorshDeserialize for TokenValidityPredicateCircuit { .map(|_| Resource::deserialize_reader(reader)) .collect::>()?; let token_name = TokenName::deserialize_reader(reader)?; - let pk_bytes = <[u8; 32]>::deserialize_reader(reader)?; - let pk = Option::from(pallas::Point::from_bytes(&pk_bytes)).ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "owned_resource_id not in point", - ) - })?; - let vk_bytes = <[u8; 32]>::deserialize_reader(reader)?; - let vk = Option::from(pallas::Base::from_repr(vk_bytes)).ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::InvalidData, "vk not in field") - })?; - let auth = TokenAuthorization { pk, vk }; + let auth = TokenAuthorization::deserialize_reader(reader)?; let receiver_vp_vk_bytes = <[u8; 32]>::deserialize_reader(reader)?; let receiver_vp_vk = Option::from(pallas::Base::from_repr(receiver_vp_vk_bytes)) .ok_or_else(|| { @@ -597,6 +584,32 @@ impl BorshDeserialize for TokenValidityPredicateCircuit { } } +impl BorshSerialize for TokenAuthorization { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + writer.write_all(&self.pk.to_bytes())?; + writer.write_all(&self.vk.to_repr())?; + Ok(()) + } +} + +impl BorshDeserialize for TokenAuthorization { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let pk_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let pk = Option::from(pallas::Point::from_bytes(&pk_bytes)).ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "owned_resource_id not in point", + ) + })?; + let vk_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let vk = Option::from(pallas::Base::from_repr(vk_bytes)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "vk not in field") + })?; + + Ok(Self { pk, vk }) + } +} + impl TokenAuthorization { pub fn new(pk: pallas::Point, vk: pallas::Base) -> Self { Self { pk, vk }