diff --git a/taiga_halo2/Cargo.toml b/taiga_halo2/Cargo.toml index 857e7b7e..9de5e1f5 100644 --- a/taiga_halo2/Cargo.toml +++ b/taiga_halo2/Cargo.toml @@ -51,4 +51,4 @@ default = [] nif = ["dep:rustler", "borsh", "pasta_curves/repr-erlang"] serde = ["dep:serde", "pasta_curves/serde"] borsh = ["dep:borsh"] -examples = [] +examples = ["borsh"] diff --git a/taiga_halo2/src/circuit/vp_bytecode.rs b/taiga_halo2/src/circuit/vp_bytecode.rs index 202edcf8..2b91d503 100644 --- a/taiga_halo2/src/circuit/vp_bytecode.rs +++ b/taiga_halo2/src/circuit/vp_bytecode.rs @@ -1,5 +1,14 @@ #[cfg(feature = "borsh")] use crate::circuit::vp_examples::TrivialValidityPredicateCircuit; +#[cfg(feature = "examples")] +use crate::circuit::vp_examples::{ + cascade_intent::CascadeIntentValidityPredicateCircuit, + or_relation_intent::OrRelationIntentValidityPredicateCircuit, + partial_fulfillment_intent::PartialFulfillmentIntentValidityPredicateCircuit, + receiver_vp::ReceiverValidityPredicateCircuit, + signature_verification::SignatureVerificationValidityPredicateCircuit, + token::TokenValidityPredicateCircuit, +}; use crate::error::TransactionError; use crate::shielded_ptx::ResourceVPVerifyingInfoSet; use crate::{ @@ -31,7 +40,13 @@ pub enum ValidityPredicateRepresentation { // Native halo2 circuits don't have a unified representatioin, enumerate the vp circuit examples for the moment. // TODO: figure out if we can have a unified circuit presentation. In theory, it's possible to separate the circuit system and proving system. Trivial, - // TODO: add other vp types here if needed + Token, + SignatureVerification, + Receiver, + PartialFulfillmentIntent, + OrRelationIntent, + CascadeIntent, + // Add other native vp types here if needed } #[derive(Clone, Debug)] @@ -74,6 +89,36 @@ impl ValidityPredicateByteCode { let vp = TrivialValidityPredicateCircuit::from_bytes(&self.inputs); Ok(vp.get_verifying_info()) } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::Token => { + let vp = TokenValidityPredicateCircuit::from_bytes(&self.inputs); + Ok(vp.get_verifying_info()) + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::SignatureVerification => { + let vp = SignatureVerificationValidityPredicateCircuit::from_bytes(&self.inputs); + Ok(vp.get_verifying_info()) + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::Receiver => { + 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()) + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::OrRelationIntent => { + let vp = OrRelationIntentValidityPredicateCircuit::from_bytes(&self.inputs); + Ok(vp.get_verifying_info()) + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::CascadeIntent => { + let vp = CascadeIntentValidityPredicateCircuit::from_bytes(&self.inputs); + Ok(vp.get_verifying_info()) + } #[allow(unreachable_patterns)] _ => Err(TransactionError::InvalidValidityPredicateRepresentation), } @@ -104,6 +149,36 @@ impl ValidityPredicateByteCode { let vp = TrivialValidityPredicateCircuit::from_bytes(&self.inputs); vp.verify_transparently()? } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::Token => { + let vp = TokenValidityPredicateCircuit::from_bytes(&self.inputs); + vp.verify_transparently()? + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::SignatureVerification => { + let vp = SignatureVerificationValidityPredicateCircuit::from_bytes(&self.inputs); + vp.verify_transparently()? + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::Receiver => { + 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()? + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::OrRelationIntent => { + let vp = OrRelationIntentValidityPredicateCircuit::from_bytes(&self.inputs); + vp.verify_transparently()? + } + #[cfg(feature = "examples")] + ValidityPredicateRepresentation::CascadeIntent => { + let vp = CascadeIntentValidityPredicateCircuit::from_bytes(&self.inputs); + vp.verify_transparently()? + } #[allow(unreachable_patterns)] _ => return Err(TransactionError::InvalidValidityPredicateRepresentation), }; diff --git a/taiga_halo2/src/circuit/vp_circuit.rs b/taiga_halo2/src/circuit/vp_circuit.rs index e6769def..01c62494 100644 --- a/taiga_halo2/src/circuit/vp_circuit.rs +++ b/taiga_halo2/src/circuit/vp_circuit.rs @@ -31,7 +31,7 @@ use crate::{ proof::Proof, resource::{RandomSeed, Resource, ResourceCommitment}, resource_encryption::{ResourceCiphertext, SecretKey}, - utils::mod_r_p, + utils::{mod_r_p, read_base_field}, vp_vk::ValidityPredicateVerifyingKey, }; use dyn_clone::{clone_trait_object, DynClone}; @@ -204,8 +204,6 @@ impl BorshSerialize for VPVerifyingInfo { #[cfg(feature = "borsh")] impl BorshDeserialize for VPVerifyingInfo { fn deserialize_reader(reader: &mut R) -> std::io::Result { - use ff::PrimeField; - use std::io; // Read vk use crate::circuit::vp_examples::TrivialValidityPredicateCircuit; let params = SETUP_PARAMS_MAP.get(&VP_CIRCUIT_PARAMS_SIZE).unwrap(); @@ -214,12 +212,7 @@ impl BorshDeserialize for VPVerifyingInfo { let proof = Proof::deserialize_reader(reader)?; // Read public inputs let public_inputs: Vec<_> = (0..VP_CIRCUIT_PUBLIC_INPUT_NUM) - .map(|_| { - let bytes = <[u8; 32]>::deserialize_reader(reader)?; - Option::from(pallas::Base::from_repr(bytes)).ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidData, "public input not in field") - }) - }) + .map(|_| read_base_field(reader)) .collect::>()?; Ok(VPVerifyingInfo { vk, diff --git a/taiga_halo2/src/circuit/vp_examples.rs b/taiga_halo2/src/circuit/vp_examples.rs index 48bda659..6adb3daf 100644 --- a/taiga_halo2/src/circuit/vp_examples.rs +++ b/taiga_halo2/src/circuit/vp_examples.rs @@ -9,6 +9,7 @@ use crate::{ error::TransactionError, proof::Proof, resource::{RandomSeed, Resource}, + utils::read_base_field, vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; @@ -133,15 +134,7 @@ impl BorshSerialize for TrivialValidityPredicateCircuit { #[cfg(feature = "borsh")] impl BorshDeserialize for TrivialValidityPredicateCircuit { fn deserialize_reader(reader: &mut R) -> std::io::Result { - use ff::PrimeField; - 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 owned_resource_id = read_base_field(reader)?; let input_resources: Vec<_> = (0..NUM_RESOURCE) .map(|_| Resource::deserialize_reader(reader)) .collect::>()?; diff --git a/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs b/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs index d0f98e03..f6feea9f 100644 --- a/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs +++ b/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs @@ -11,6 +11,7 @@ use crate::{ assign_free_advice, target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, }, + vp_bytecode::{ValidityPredicateByteCode, ValidityPredicateRepresentation}, vp_circuit::{ BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, @@ -21,16 +22,18 @@ use crate::{ nullifier::Nullifier, proof::Proof, resource::{RandomSeed, Resource}, + utils::read_base_field, vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; +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::pallas; +use pasta_curves::{group::ff::PrimeField, pallas}; use rand::rngs::OsRng; use rand::RngCore; @@ -55,6 +58,21 @@ impl CascadeIntentValidityPredicateCircuit { pub fn encode_label(cascade_resource_cm: pallas::Base) -> pallas::Base { cascade_resource_cm } + + pub fn to_bytecode(&self) -> ValidityPredicateByteCode { + ValidityPredicateByteCode::new( + ValidityPredicateRepresentation::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 ValidityPredicateCircuit for CascadeIntentValidityPredicateCircuit { @@ -149,6 +167,41 @@ impl ValidityPredicateCircuit for CascadeIntentValidityPredicateCircuit { vp_circuit_impl!(CascadeIntentValidityPredicateCircuit); vp_verifying_info_impl!(CascadeIntentValidityPredicateCircuit); +impl BorshSerialize for CascadeIntentValidityPredicateCircuit { + 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 CascadeIntentValidityPredicateCircuit { + 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, @@ -193,6 +246,13 @@ fn test_halo2_cascade_intent_vp_circuit() { cascade_resource_cm, } }; + + // Test serialization + let circuit = { + let circuit_bytes = circuit.to_bytes(); + CascadeIntentValidityPredicateCircuit::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/or_relation_intent.rs b/taiga_halo2/src/circuit/vp_examples/or_relation_intent.rs index 11dd25db..427b04ff 100644 --- a/taiga_halo2/src/circuit/vp_examples/or_relation_intent.rs +++ b/taiga_halo2/src/circuit/vp_examples/or_relation_intent.rs @@ -10,6 +10,7 @@ use crate::{ poseidon_hash::poseidon_hash_gadget, target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, }, + vp_bytecode::{ValidityPredicateByteCode, ValidityPredicateRepresentation}, vp_circuit::{ BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, @@ -22,16 +23,18 @@ use crate::{ proof::Proof, resource::{RandomSeed, Resource}, utils::poseidon_hash_n, + utils::read_base_field, vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; +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::pallas; +use pasta_curves::{group::ff::PrimeField, pallas}; use rand::rngs::OsRng; use rand::RngCore; @@ -75,6 +78,21 @@ impl OrRelationIntentValidityPredicateCircuit { receiver_value, ]) } + + pub fn to_bytecode(&self) -> ValidityPredicateByteCode { + ValidityPredicateByteCode::new( + ValidityPredicateRepresentation::OrRelationIntent, + 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 OrRelationIntentValidityPredicateCircuit { @@ -277,6 +295,52 @@ impl ValidityPredicateCircuit for OrRelationIntentValidityPredicateCircuit { vp_circuit_impl!(OrRelationIntentValidityPredicateCircuit); vp_verifying_info_impl!(OrRelationIntentValidityPredicateCircuit); +impl BorshSerialize for OrRelationIntentValidityPredicateCircuit { + 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.token_1.serialize(writer)?; + self.token_2.serialize(writer)?; + + writer.write_all(&self.receiver_npk.to_repr())?; + writer.write_all(&self.receiver_value.to_repr())?; + + Ok(()) + } +} + +impl BorshDeserialize for OrRelationIntentValidityPredicateCircuit { + 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 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(), + token_1, + token_2, + receiver_npk, + receiver_value, + }) + } +} + pub fn create_intent_resource( mut rng: R, token_1: &Token, @@ -346,6 +410,13 @@ fn test_halo2_or_relation_intent_vp_circuit() { receiver_value: output_resources[0].value, } }; + + // Test serialization + let circuit = { + let circuit_bytes = circuit.to_bytes(); + OrRelationIntentValidityPredicateCircuit::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.rs b/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs index bff301f8..c3d2fb28 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, @@ -20,15 +21,17 @@ use crate::{ error::TransactionError, proof::Proof, resource::{RandomSeed, Resource}, + utils::read_base_field, 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 +57,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 +198,42 @@ 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 = 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 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 +334,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/receiver_vp.rs b/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs index f54e61b8..4cead5c9 100644 --- a/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs +++ b/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs @@ -6,6 +6,7 @@ use crate::{ target_resource_variable::get_owned_resource_variable, }, resource_encryption_circuit::resource_encryption_gadget, + vp_bytecode::{ValidityPredicateByteCode, ValidityPredicateRepresentation}, vp_circuit::{ BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, @@ -17,12 +18,12 @@ use crate::{ proof::Proof, resource::{RandomSeed, Resource}, resource_encryption::{ResourceCiphertext, ResourcePlaintext, SecretKey}, - utils::mod_r_p, + utils::{mod_r_p, read_base_field, read_point}, vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; -use group::Group; -use group::{cofactor::CofactorCurveAffine, Curve}; +use borsh::{BorshDeserialize, BorshSerialize}; +use group::{cofactor::CofactorCurveAffine, ff::PrimeField, Curve, Group, GroupEncoding}; use halo2_gadgets::ecc::{chip::EccChip, NonIdentityPoint}; use halo2_proofs::{ arithmetic::CurveAffine, @@ -33,6 +34,7 @@ use lazy_static::lazy_static; use pasta_curves::pallas; use rand::rngs::OsRng; use rand::RngCore; + const CIPHER_LEN: usize = 9; lazy_static! { @@ -54,6 +56,20 @@ pub struct ReceiverValidityPredicateCircuit { pub auth_vp_vk: pallas::Base, } +impl ReceiverValidityPredicateCircuit { + pub fn to_bytecode(&self) -> ValidityPredicateByteCode { + ValidityPredicateByteCode::new(ValidityPredicateRepresentation::Receiver, 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 Default for ReceiverValidityPredicateCircuit { fn default() -> Self { Self { @@ -282,6 +298,54 @@ impl ValidityPredicateCircuit for ReceiverValidityPredicateCircuit { vp_circuit_impl!(ReceiverValidityPredicateCircuit); vp_verifying_info_impl!(ReceiverValidityPredicateCircuit); +impl BorshSerialize for ReceiverValidityPredicateCircuit { + 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.vp_vk.to_repr())?; + writer.write_all(&self.encrypt_nonce.to_repr())?; + writer.write_all(&self.sk.to_repr())?; + writer.write_all(&self.rcv_pk.to_bytes())?; + writer.write_all(&self.auth_vp_vk.to_repr())?; + + Ok(()) + } +} + +impl BorshDeserialize for ReceiverValidityPredicateCircuit { + 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 vp_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_vp_vk = read_base_field(reader)?; + Ok(Self { + owned_resource_id, + input_resources: input_resources.try_into().unwrap(), + output_resources: output_resources.try_into().unwrap(), + vp_vk, + encrypt_nonce, + sk, + rcv_pk, + auth_vp_vk, + }) + } +} + #[test] fn test_halo2_receiver_vp_circuit() { use crate::constant::VP_CIRCUIT_PARAMS_SIZE; @@ -321,6 +385,13 @@ fn test_halo2_receiver_vp_circuit() { rcv_sk, ) }; + + // Test serialization + let circuit = { + let circuit_bytes = circuit.to_bytes(); + ReceiverValidityPredicateCircuit::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/signature_verification.rs b/taiga_halo2/src/circuit/vp_examples/signature_verification.rs index 5bce9e67..7ee323b4 100644 --- a/taiga_halo2/src/circuit/vp_examples/signature_verification.rs +++ b/taiga_halo2/src/circuit/vp_examples/signature_verification.rs @@ -5,6 +5,7 @@ use crate::{ assign_free_advice, poseidon_hash::poseidon_hash_gadget, target_resource_variable::get_owned_resource_variable, }, + vp_bytecode::{ValidityPredicateByteCode, ValidityPredicateRepresentation}, vp_circuit::{ BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, @@ -14,10 +15,11 @@ use crate::{ error::TransactionError, proof::Proof, resource::{RandomSeed, Resource}, - utils::{mod_r_p, poseidon_hash_n}, + utils::{mod_r_p, poseidon_hash_n, read_base_field, read_point, read_scalar_field}, vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; +use borsh::{BorshDeserialize, BorshSerialize}; use halo2_gadgets::ecc::{chip::EccChip, FixedPoint, NonIdentityPoint, ScalarFixed, ScalarVar}; use halo2_proofs::{ arithmetic::Field, @@ -27,7 +29,7 @@ use halo2_proofs::{ use lazy_static::lazy_static; use pasta_curves::{ arithmetic::CurveAffine, - group::{Curve, Group}, + group::{ff::PrimeField, Curve, Group, GroupEncoding}, pallas, }; use rand::rngs::OsRng; @@ -149,6 +151,21 @@ impl SignatureVerificationValidityPredicateCircuit { receiver_vp_vk, } } + + pub fn to_bytecode(&self) -> ValidityPredicateByteCode { + ValidityPredicateByteCode::new( + ValidityPredicateRepresentation::SignatureVerification, + 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 SignatureVerificationValidityPredicateCircuit { @@ -288,6 +305,67 @@ impl ValidityPredicateCircuit for SignatureVerificationValidityPredicateCircuit vp_circuit_impl!(SignatureVerificationValidityPredicateCircuit); vp_verifying_info_impl!(SignatureVerificationValidityPredicateCircuit); +impl BorshSerialize for SignatureVerificationValidityPredicateCircuit { + 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.vp_vk.to_repr())?; + self.signature.serialize(writer)?; + writer.write_all(&self.receiver_vp_vk.to_repr())?; + + Ok(()) + } +} + +impl BorshDeserialize for SignatureVerificationValidityPredicateCircuit { + 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 vp_vk = read_base_field(reader)?; + let signature = SchnorrSignature::deserialize_reader(reader)?; + let receiver_vp_vk = read_base_field(reader)?; + Ok(Self { + owned_resource_id, + input_resources: input_resources.try_into().unwrap(), + output_resources: output_resources.try_into().unwrap(), + vp_vk, + signature, + receiver_vp_vk, + }) + } +} + +impl BorshSerialize for SchnorrSignature { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + writer.write_all(&self.pk.to_bytes())?; + writer.write_all(&self.r.to_bytes())?; + writer.write_all(&self.s.to_repr())?; + + Ok(()) + } +} + +impl BorshDeserialize for SchnorrSignature { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let pk = read_point(reader)?; + let r = read_point(reader)?; + let s = read_scalar_field(reader)?; + Ok(Self { pk, r, s }) + } +} + #[test] fn test_halo2_sig_verification_vp_circuit() { use crate::circuit::vp_examples::{ @@ -317,6 +395,13 @@ fn test_halo2_sig_verification_vp_circuit() { *COMPRESSED_RECEIVER_VK, ) }; + + // Test serialization + let circuit = { + let circuit_bytes = circuit.to_bytes(); + SignatureVerificationValidityPredicateCircuit::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/token.rs b/taiga_halo2/src/circuit/vp_examples/token.rs index 8dfc6683..1b3329b0 100644 --- a/taiga_halo2/src/circuit/vp_examples/token.rs +++ b/taiga_halo2/src/circuit/vp_examples/token.rs @@ -6,6 +6,7 @@ use crate::{ poseidon_hash::poseidon_hash_gadget, target_resource_variable::{get_is_input_resource_flag, get_owned_resource_variable}, }, + vp_bytecode::{ValidityPredicateByteCode, ValidityPredicateRepresentation}, vp_circuit::{ BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, @@ -24,12 +25,13 @@ use crate::{ nullifier::Nullifier, proof::Proof, resource::{RandomSeed, Resource, ResourceValidityPredicates}, - utils::poseidon_hash_n, + utils::{poseidon_hash_n, read_base_field, read_point}, vp_commitment::ValidityPredicateCommitment, vp_vk::ValidityPredicateVerifyingKey, }; +use borsh::{BorshDeserialize, BorshSerialize}; use ff::Field; -use group::{Curve, Group}; +use group::{Curve, Group, GroupEncoding}; use halo2_gadgets::ecc::{chip::EccChip, NonIdentityPoint}; use halo2_proofs::{ circuit::{floor_planner, Layouter, Value}, @@ -46,7 +48,7 @@ lazy_static! { pub static ref COMPRESSED_TOKEN_VK: pallas::Base = TOKEN_VK.get_compressed(); } -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq, BorshSerialize, BorshDeserialize)] pub struct TokenName(String); impl TokenName { @@ -62,7 +64,7 @@ impl TokenName { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, BorshSerialize, BorshDeserialize)] pub struct Token { name: TokenName, quantity: u64, @@ -145,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, @@ -284,6 +286,20 @@ impl Default for TokenAuthorization { } } +impl TokenValidityPredicateCircuit { + pub fn to_bytecode(&self) -> ValidityPredicateByteCode { + ValidityPredicateByteCode::new(ValidityPredicateRepresentation::Token, 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 Default for TokenValidityPredicateCircuit { fn default() -> Self { Self { @@ -509,6 +525,68 @@ impl ValidityPredicateCircuit for TokenValidityPredicateCircuit { vp_circuit_impl!(TokenValidityPredicateCircuit); vp_verifying_info_impl!(TokenValidityPredicateCircuit); +impl BorshSerialize for TokenValidityPredicateCircuit { + 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.token_name.serialize(writer)?; + self.auth.serialize(writer)?; + writer.write_all(&self.receiver_vp_vk.to_repr())?; + self.rseed.serialize(writer)?; + + Ok(()) + } +} + +impl BorshDeserialize for TokenValidityPredicateCircuit { + 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 token_name = TokenName::deserialize_reader(reader)?; + let auth = TokenAuthorization::deserialize_reader(reader)?; + let receiver_vp_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(), + token_name, + auth, + receiver_vp_vk, + rseed, + }) + } +} + +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 = read_point(reader)?; + let vk = read_base_field(reader)?; + + Ok(Self { pk, vk }) + } +} + impl TokenAuthorization { pub fn new(pk: pallas::Point, vk: pallas::Base) -> Self { Self { pk, vk } @@ -564,6 +642,12 @@ fn test_halo2_token_vp_circuit() { } }; + // Test serialization + let circuit = { + let circuit_bytes = circuit.to_bytes(); + TokenValidityPredicateCircuit::from_bytes(&circuit_bytes) + }; + let public_inputs = circuit.get_public_inputs(&mut rng); let prover = MockProver::::run( diff --git a/taiga_halo2/src/merkle_tree.rs b/taiga_halo2/src/merkle_tree.rs index 1fe05a1d..4336cbed 100644 --- a/taiga_halo2/src/merkle_tree.rs +++ b/taiga_halo2/src/merkle_tree.rs @@ -2,7 +2,7 @@ use std::hash::{Hash, Hasher}; use crate::merkle_tree::LR::{L, R}; use crate::resource::ResourceCommitment; -use crate::utils::poseidon_hash; +use crate::utils::{poseidon_hash, read_base_field}; use crate::{constant::TAIGA_COMMITMENT_TREE_DEPTH, resource::Resource}; use ff::PrimeField; use halo2_proofs::arithmetic::Field; @@ -68,11 +68,7 @@ impl BorshSerialize for Anchor { #[cfg(feature = "borsh")] impl BorshDeserialize for Anchor { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let mut repr = [0u8; 32]; - reader.read_exact(&mut repr)?; - let value = Option::from(pallas::Base::from_repr(repr)).ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::InvalidData, "Anchor not in field") - })?; + let value = read_base_field(reader)?; Ok(Self(value)) } } @@ -208,11 +204,7 @@ impl BorshSerialize for Node { #[cfg(feature = "borsh")] impl BorshDeserialize for Node { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let mut repr = [0u8; 32]; - reader.read_exact(&mut repr)?; - let value = Option::from(pallas::Base::from_repr(repr)).ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::InvalidData, "Node value not in field") - })?; + let value = read_base_field(reader)?; Ok(Self(value)) } } diff --git a/taiga_halo2/src/nullifier.rs b/taiga_halo2/src/nullifier.rs index 45366ea3..d591ee8b 100644 --- a/taiga_halo2/src/nullifier.rs +++ b/taiga_halo2/src/nullifier.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use crate::{ resource::ResourceCommitment, - utils::{poseidon_hash_n, prf_nf}, + utils::{poseidon_hash_n, prf_nf, read_base_field}, }; use halo2_proofs::arithmetic::Field; use pasta_curves::group::ff::PrimeField; @@ -91,11 +91,7 @@ impl BorshSerialize for Nullifier { #[cfg(feature = "borsh")] impl BorshDeserialize for Nullifier { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let mut repr = [0u8; 32]; - reader.read_exact(&mut repr)?; - let value = Option::from(pallas::Base::from_repr(repr)).ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::InvalidData, "Nullifier not in field") - })?; + let value = read_base_field(reader)?; Ok(Self(value)) } } diff --git a/taiga_halo2/src/resource.rs b/taiga_halo2/src/resource.rs index 8c67dd27..05590a0f 100644 --- a/taiga_halo2/src/resource.rs +++ b/taiga_halo2/src/resource.rs @@ -11,7 +11,7 @@ use crate::{ merkle_tree::{Anchor, MerklePath, Node}, nullifier::{Nullifier, NullifierKeyContainer}, shielded_ptx::ResourceVPVerifyingInfoSet, - utils::{poseidon_hash_n, poseidon_to_curve}, + utils::{poseidon_hash_n, poseidon_to_curve, read_base_field}, }; use blake2b_simd::Params as Blake2bParams; use ff::{FromUniformBytes, PrimeField}; @@ -67,14 +67,7 @@ impl BorshSerialize for ResourceCommitment { #[cfg(feature = "borsh")] impl BorshDeserialize for ResourceCommitment { fn deserialize_reader(reader: &mut R) -> std::io::Result { - let mut repr = [0u8; 32]; - reader.read_exact(&mut repr)?; - let value = Option::from(pallas::Base::from_repr(repr)).ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "ResourceCommitment value not in field", - ) - })?; + let value = read_base_field(reader)?; Ok(Self(value)) } } @@ -339,30 +332,16 @@ impl BorshDeserialize for Resource { use byteorder::{LittleEndian, ReadBytesExt}; use std::io; // Read logic - let mut logic_bytes = [0u8; 32]; - reader.read_exact(&mut logic_bytes)?; - let logic = Option::from(pallas::Base::from_repr(logic_bytes)) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "logic not in field"))?; + let logic = read_base_field(reader)?; // Read label - let mut label_bytes = [0u8; 32]; - reader.read_exact(&mut label_bytes)?; - let label = Option::from(pallas::Base::from_repr(label_bytes)) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "label not in field"))?; + let label = read_base_field(reader)?; // Read value - let mut value_bytes = [0u8; 32]; - reader.read_exact(&mut value_bytes)?; - let value = Option::from(pallas::Base::from_repr(value_bytes)) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "value not in field"))?; + let value = read_base_field(reader)?; // Read resource quantity let quantity = reader.read_u64::()?; // Read nk_container - let mut nk_container_type = [0u8; 1]; - reader.read_exact(&mut nk_container_type)?; - let nk_container_type = nk_container_type[0]; - let mut nk_container_bytes = [0u8; 32]; - reader.read_exact(&mut nk_container_bytes)?; - let nk = Option::from(pallas::Base::from_repr(nk_container_bytes)) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nk not in field"))?; + let nk_container_type = reader.read_u8()?; + let nk = read_base_field(reader)?; let nk_container = if nk_container_type == 0x01 { NullifierKeyContainer::from_npk(nk) } else { @@ -381,10 +360,8 @@ impl BorshDeserialize for Resource { let is_ephemeral = is_ephemeral_byte == 0x01; // Read rseed - let mut rseed_bytes = [0u8; 32]; - reader.read_exact(&mut rseed_bytes)?; - let rseed = Option::from(pallas::Base::from_repr(rseed_bytes)) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "rseed not in field"))?; + let rseed = read_base_field(reader)?; + // Construct resource Ok(Resource::from_full( logic, diff --git a/taiga_halo2/src/shielded_ptx.rs b/taiga_halo2/src/shielded_ptx.rs index 661157b7..1774c454 100644 --- a/taiga_halo2/src/shielded_ptx.rs +++ b/taiga_halo2/src/shielded_ptx.rs @@ -11,6 +11,7 @@ use crate::merkle_tree::Anchor; use crate::nullifier::Nullifier; use crate::proof::Proof; use crate::resource::{ResourceCommitment, ResourceValidityPredicates}; +use crate::utils::read_scalar_field; use halo2_proofs::plonk::Error; use pasta_curves::pallas; use rand::RngCore; @@ -353,14 +354,7 @@ impl BorshDeserialize for ShieldedPartialTransaction { let binding_sig_r = if binding_sig_r_type == 0 { None } else { - let binding_sig_r_bytes = <[u8; 32]>::deserialize_reader(reader)?; - let r = - Option::from(pallas::Scalar::from_repr(binding_sig_r_bytes)).ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "binding_sig_r not in field", - ) - })?; + let r = read_scalar_field(reader)?; Some(r) }; diff --git a/taiga_halo2/src/utils.rs b/taiga_halo2/src/utils.rs index bfdcb44f..b36a5a7a 100644 --- a/taiga_halo2/src/utils.rs +++ b/taiga_halo2/src/utils.rs @@ -3,7 +3,7 @@ use halo2_gadgets::poseidon::primitives as poseidon; use halo2_proofs::arithmetic::CurveAffine; use pasta_curves::{ arithmetic::CurveExt, - group::{ff::PrimeField, Curve}, + group::{ff::PrimeField, Curve, GroupEncoding}, hashtocurve, pallas, }; @@ -96,3 +96,24 @@ pub fn to_field_elements(bytes: &[u8]) -> Vec { }) .collect::>() } + +pub fn read_base_field(reader: &mut R) -> std::io::Result { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + Option::from(pallas::Base::from_repr(bytes)) + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid base field")) +} + +pub fn read_scalar_field(reader: &mut R) -> std::io::Result { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + Option::from(pallas::Scalar::from_repr(bytes)) + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid scalar field")) +} + +pub fn read_point(reader: &mut R) -> std::io::Result { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + Option::from(pallas::Point::from_bytes(&bytes)) + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid point")) +}