diff --git a/rust-toolchain b/rust-toolchain index d624c9ff..cad2ef94 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.67.1 \ No newline at end of file +1.72.0 \ No newline at end of file diff --git a/taiga_halo2/Cargo.toml b/taiga_halo2/Cargo.toml index 87a87e75..ebdcc3fe 100644 --- a/taiga_halo2/Cargo.toml +++ b/taiga_halo2/Cargo.toml @@ -4,27 +4,31 @@ version = "0.1.0" edition = "2021" [dependencies] +rustler = {version = "0.29.1", optional = true} rand = "0.8" lazy_static = "1.4" blake2b_simd = "1.0" -pasta_curves = "0.5.1" +pasta_curves = {git = "https://github.com/heliaxdev/pasta_curves", branch = "taiga", features = ["repr-erlang"]} ff = "0.13" group = "0.13" -halo2_gadgets = { version = "0.3", features = ["test-dependencies"] } -halo2_proofs = { version = "0.3", features = ["dev-graph"] } +halo2_gadgets = {git = "https://github.com/heliaxdev/halo2", branch = "taiga", features = ["test-dependencies"]} +halo2_proofs = {git = "https://github.com/heliaxdev/halo2", branch = "taiga", features = ["dev-graph"]} bitvec = "1.0" 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" -borsh = { version = "0.10", features = ["const-generics"] } byteorder = "1.4" num-bigint = "0.4" +serde = { version = "1.0", features = ["derive"], optional = true } +borsh = { version = "0.10", features = ["const-generics"], optional = true } + [dev-dependencies] criterion = "0.5" proptest = "1.2" +serde_json = "1.0" [[bench]] name = "action_proof" @@ -34,8 +38,14 @@ harness = false name = "vp_proof" harness = false -[[example]] -name = "taiga_sudoku" +# [[example]] +# name = "taiga_sudoku" [[example]] name = "tx_examples" + +[features] +default = ["borsh", "serde"] +nif = ["rustler", "pasta_curves/repr-erlang"] +serde = ["dep:serde", "pasta_curves/serde"] +borsh = ["dep:borsh"] diff --git a/taiga_halo2/benches/vp_proof.rs b/taiga_halo2/benches/vp_proof.rs index ee1a22cd..7672c210 100644 --- a/taiga_halo2/benches/vp_proof.rs +++ b/taiga_halo2/benches/vp_proof.rs @@ -6,7 +6,7 @@ use pasta_curves::pallas; use rand::rngs::OsRng; use rand::Rng; use taiga_halo2::{ - circuit::{vp_circuit::ValidityPredicateInfo, vp_examples::TrivialValidityPredicateCircuit}, + circuit::{vp_circuit::ValidityPredicateCircuit, vp_examples::TrivialValidityPredicateCircuit}, constant::{NUM_NOTE, SETUP_PARAMS_MAP}, note::{Note, NoteType, RandomSeed}, nullifier::{Nullifier, NullifierKeyContainer}, @@ -87,7 +87,8 @@ fn bench_vp_proof(name: &str, c: &mut Criterion) { vp_circuit.clone(), &[public_inputs.inner()], &mut rng, - ); + ) + .unwrap(); }) }); @@ -105,9 +106,9 @@ fn bench_vp_proof(name: &str, c: &mut Criterion) { let verifier_name = name.to_string() + "-verifier"; c.bench_function(&verifier_name, |b| { b.iter(|| { - proof + assert!(proof .verify(pk.get_vk(), ¶ms, &[public_inputs.inner()]) - .is_ok(); + .is_ok()); }) }); } diff --git a/taiga_halo2/examples/simple_sudoku/circuit.rs b/taiga_halo2/deprecated/simple_sudoku/circuit.rs similarity index 100% rename from taiga_halo2/examples/simple_sudoku/circuit.rs rename to taiga_halo2/deprecated/simple_sudoku/circuit.rs diff --git a/taiga_halo2/examples/simple_sudoku/main.rs b/taiga_halo2/deprecated/simple_sudoku/main.rs similarity index 100% rename from taiga_halo2/examples/simple_sudoku/main.rs rename to taiga_halo2/deprecated/simple_sudoku/main.rs diff --git a/taiga_halo2/examples/simple_sudoku/vp.rs b/taiga_halo2/deprecated/simple_sudoku/vp.rs similarity index 89% rename from taiga_halo2/examples/simple_sudoku/vp.rs rename to taiga_halo2/deprecated/simple_sudoku/vp.rs index cadb1614..90a318af 100644 --- a/taiga_halo2/examples/simple_sudoku/vp.rs +++ b/taiga_halo2/deprecated/simple_sudoku/vp.rs @@ -30,21 +30,6 @@ pub struct SudokuVPConfig { sudoku_config: SudokuConfig, } -impl ValidityPredicateConfig for SudokuVPConfig { - fn get_note_config(&self) -> NoteConfig { - self.note_config.clone() - } - - fn configure(meta: &mut ConstraintSystem) -> Self { - let note_config = Self::configure_note(meta); - let sudoku_config = SudokuCircuit::configure(meta); - Self { - note_config, - sudoku_config, - } - } -} - #[derive(Clone, Debug, Default)] pub struct SudokuVP { pub sudoku: SudokuCircuit, @@ -53,11 +38,9 @@ pub struct SudokuVP { } impl ValidityPredicateCircuit for SudokuVP { - type VPConfig = SudokuVPConfig; - fn custom_constraints( &self, - config: Self::VPConfig, + config: ValidityPredicateConfig, layouter: impl Layouter, _basic_variables: BasicValidityPredicateVariables, ) -> Result<(), plonk::Error> { diff --git a/taiga_halo2/examples/taiga_sudoku/app_vp.rs b/taiga_halo2/deprecated/taiga_sudoku/app_vp.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/app_vp.rs rename to taiga_halo2/deprecated/taiga_sudoku/app_vp.rs diff --git a/taiga_halo2/examples/taiga_sudoku/dealer_intent_app_vp.rs b/taiga_halo2/deprecated/taiga_sudoku/dealer_intent_app_vp.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/dealer_intent_app_vp.rs rename to taiga_halo2/deprecated/taiga_sudoku/dealer_intent_app_vp.rs diff --git a/taiga_halo2/examples/taiga_sudoku/gadgets/mod.rs b/taiga_halo2/deprecated/taiga_sudoku/gadgets/mod.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/gadgets/mod.rs rename to taiga_halo2/deprecated/taiga_sudoku/gadgets/mod.rs diff --git a/taiga_halo2/examples/taiga_sudoku/gadgets/state_check.rs b/taiga_halo2/deprecated/taiga_sudoku/gadgets/state_check.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/gadgets/state_check.rs rename to taiga_halo2/deprecated/taiga_sudoku/gadgets/state_check.rs diff --git a/taiga_halo2/examples/taiga_sudoku/gadgets/state_update.rs b/taiga_halo2/deprecated/taiga_sudoku/gadgets/state_update.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/gadgets/state_update.rs rename to taiga_halo2/deprecated/taiga_sudoku/gadgets/state_update.rs diff --git a/taiga_halo2/examples/taiga_sudoku/gadgets/value_check.rs b/taiga_halo2/deprecated/taiga_sudoku/gadgets/value_check.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/gadgets/value_check.rs rename to taiga_halo2/deprecated/taiga_sudoku/gadgets/value_check.rs diff --git a/taiga_halo2/examples/taiga_sudoku/main.rs b/taiga_halo2/deprecated/taiga_sudoku/main.rs similarity index 100% rename from taiga_halo2/examples/taiga_sudoku/main.rs rename to taiga_halo2/deprecated/taiga_sudoku/main.rs diff --git a/taiga_halo2/src/action.rs b/taiga_halo2/src/action.rs index 1edf8251..834be741 100644 --- a/taiga_halo2/src/action.rs +++ b/taiga_halo2/src/action.rs @@ -5,15 +5,22 @@ use crate::{ nullifier::Nullifier, value_commitment::ValueCommitment, }; -use borsh::{BorshDeserialize, BorshSerialize}; -use ff::PrimeField; use halo2_proofs::arithmetic::Field; use pasta_curves::pallas; use rand::RngCore; -use std::io; +#[cfg(feature = "nif")] +use rustler::NifStruct; +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; /// The action result used in transaction. #[derive(Copy, Debug, Clone)] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.Action.Instance")] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ActionInstance { /// The root of the note commitment Merkle tree. pub anchor: pallas::Base, @@ -46,8 +53,10 @@ impl ActionInstance { } } +#[cfg(feature = "borsh")] impl BorshSerialize for ActionInstance { fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + use ff::PrimeField; writer.write_all(&self.anchor.to_repr())?; writer.write_all(&self.nf.to_bytes())?; writer.write_all(&self.cm_x.to_repr())?; @@ -56,8 +65,11 @@ impl BorshSerialize for ActionInstance { } } +#[cfg(feature = "borsh")] impl BorshDeserialize for ActionInstance { - fn deserialize_reader(reader: &mut R) -> io::Result { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + use ff::PrimeField; + use std::io; let anchor_bytes = <[u8; 32]>::deserialize_reader(reader)?; let anchor = Option::from(pallas::Base::from_repr(anchor_bytes)) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "anchor not in field"))?; diff --git a/taiga_halo2/src/binding_signature.rs b/taiga_halo2/src/binding_signature.rs index 5dcf5af3..8eaf9778 100644 --- a/taiga_halo2/src/binding_signature.rs +++ b/taiga_halo2/src/binding_signature.rs @@ -1,11 +1,15 @@ use crate::constant::NOTE_COMMITMENT_R_GENERATOR; -use borsh::{BorshDeserialize, BorshSerialize}; use pasta_curves::group::cofactor::CofactorCurveAffine; use pasta_curves::group::{ff::PrimeField, GroupEncoding}; use pasta_curves::pallas; use rand::{CryptoRng, RngCore}; use reddsa::{private, Error, SigType, Signature, SigningKey, VerificationKey}; -use std::io; + +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum TaigaBinding {} @@ -29,12 +33,15 @@ impl private::Sealed for TaigaBinding { impl SigType for TaigaBinding {} #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BindingSignature(Signature); #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BindingSigningKey(SigningKey); #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BindingVerificationKey(VerificationKey); impl BindingSignature { @@ -48,14 +55,16 @@ impl BindingSignature { } } +#[cfg(feature = "borsh")] impl BorshSerialize for BindingSignature { - fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { writer.write_all(&self.to_bytes()) } } +#[cfg(feature = "borsh")] impl BorshDeserialize for BindingSignature { - fn deserialize_reader(reader: &mut R) -> io::Result { + fn deserialize_reader(reader: &mut R) -> std::io::Result { let mut sig_bytes = [0u8; 64]; reader.read_exact(&mut sig_bytes)?; Ok(Self::from_bytes(sig_bytes)) @@ -81,14 +90,17 @@ impl BindingSigningKey { } } +#[cfg(feature = "borsh")] impl BorshSerialize for BindingSigningKey { fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { writer.write_all(&self.to_bytes()) } } +#[cfg(feature = "borsh")] impl BorshDeserialize for BindingSigningKey { - fn deserialize_reader(reader: &mut R) -> io::Result { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + use std::io; let mut key_bytes = [0u8; 32]; reader.read_exact(&mut key_bytes)?; Self::from_bytes(key_bytes).map_err(|_| { diff --git a/taiga_halo2/src/circuit/vp_circuit.rs b/taiga_halo2/src/circuit/vp_circuit.rs index 341e0a4f..d2ef2bca 100644 --- a/taiga_halo2/src/circuit/vp_circuit.rs +++ b/taiga_halo2/src/circuit/vp_circuit.rs @@ -29,10 +29,8 @@ use crate::{ utils::mod_r_p, vp_vk::ValidityPredicateVerifyingKey, }; -use borsh::{BorshDeserialize, BorshSerialize}; -// use byteorder::{ReadBytesExt, WriteBytesExt}; use dyn_clone::{clone_trait_object, DynClone}; -use ff::PrimeField; +//use ff::PrimeField; use group::cofactor::CofactorCurveAffine; use halo2_gadgets::{ecc::chip::EccChip, sinsemilla::chip::SinsemillaChip}; use halo2_proofs::{ @@ -48,7 +46,7 @@ use pasta_curves::{pallas, vesta, EqAffine, Fp}; use rand::{rngs::OsRng, RngCore}; use std::collections::HashMap; use std::fs; -use std::io; +//use std::io; use std::path::PathBuf; use std::rc::Rc; use vamp_ir::ast::Module; @@ -56,16 +54,95 @@ use vamp_ir::halo2::synth::{make_constant, Halo2Module, PrimeFieldOps}; use vamp_ir::transform::compile; use vamp_ir::util::{read_inputs_from_file, Config}; +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; + +#[cfg(feature = "nif")] +use rustler::types::atom; +#[cfg(feature = "nif")] +use rustler::{Decoder, Encoder, Env, NifResult, Term}; + +pub type ValidityPredicate = dyn ValidityPredicateVerifyingInfo; + #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct VPVerifyingInfo { + #[cfg_attr( + feature = "serde", + serde( + serialize_with = "serde_serialize_verifying_key", + deserialize_with = "serde_deserialize_verifying_key" + ) + )] pub vk: VerifyingKey, pub proof: Proof, pub public_inputs: ValidityPredicatePublicInputs, } +#[cfg(feature = "nif")] +rustler::atoms! {verifying_info} + +#[cfg(feature = "nif")] +impl Encoder for VPVerifyingInfo { + fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { + ( + verifying_info().encode(env), + self.vk.to_bytes().encode(env), + self.proof.encode(env), + self.public_inputs.encode(env), + ) + .encode(env) + } +} + +#[cfg(feature = "nif")] +impl<'a> Decoder<'a> for VPVerifyingInfo { + fn decode(term: Term<'a>) -> NifResult { + let (term, vk, proof, public_inputs): ( + atom::Atom, + Vec, + Proof, + ValidityPredicatePublicInputs, + ) = term.decode()?; + if term == verifying_info() { + use crate::circuit::vp_examples::TrivialValidityPredicateCircuit; + let params = SETUP_PARAMS_MAP.get(&VP_CIRCUIT_PARAMS_SIZE).unwrap(); + let vk = VerifyingKey::from_bytes::(&vk, params) + .map_err(|_e| rustler::Error::Atom("failure to decode"))?; + Ok(VPVerifyingInfo { + vk, + proof, + public_inputs, + }) + } else { + Err(rustler::Error::BadArg) + } + } +} + #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ValidityPredicatePublicInputs([pallas::Base; VP_CIRCUIT_PUBLIC_INPUT_NUM]); +#[cfg(feature = "nif")] +impl Encoder for ValidityPredicatePublicInputs { + fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { + self.0.to_vec().encode(env) + } +} + +#[cfg(feature = "nif")] +impl<'a> Decoder<'a> for ValidityPredicatePublicInputs { + fn decode(term: Term<'a>) -> NifResult { + let val: Vec = Decoder::decode(term)?; + val.try_into() + .map_err(|_e| rustler::Error::Atom("failure to decode")) + } +} + impl VPVerifyingInfo { pub fn verify(&self) -> Result<(), Error> { let params = SETUP_PARAMS_MAP.get(&VP_CIRCUIT_PARAMS_SIZE).unwrap(); @@ -97,8 +174,10 @@ impl VPVerifyingInfo { } } +#[cfg(feature = "borsh")] impl BorshSerialize for VPVerifyingInfo { fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + use ff::PrimeField; // Write vk self.vk.write(writer)?; // Write proof @@ -111,8 +190,11 @@ impl BorshSerialize for VPVerifyingInfo { } } +#[cfg(feature = "borsh")] impl BorshDeserialize for VPVerifyingInfo { - fn deserialize_reader(reader: &mut R) -> io::Result { + 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(); @@ -136,6 +218,34 @@ impl BorshDeserialize for VPVerifyingInfo { } } +#[cfg(feature = "serde")] +fn serde_serialize_verifying_key( + x: &VerifyingKey, + s: S, +) -> Result +where + S: serde::Serializer, +{ + let mut buf = Vec::new(); + x.write(&mut buf).unwrap(); + s.serialize_bytes(&buf) +} + +#[cfg(feature = "serde")] +fn serde_deserialize_verifying_key<'de, D>(d: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + use serde::de::Error; + let buf: Vec = serde::Deserialize::deserialize(d)?; + + use crate::circuit::vp_examples::TrivialValidityPredicateCircuit; + let params = SETUP_PARAMS_MAP.get(&VP_CIRCUIT_PARAMS_SIZE).unwrap(); + let vk = VerifyingKey::read::<_, TrivialValidityPredicateCircuit>(&mut buf.as_slice(), params) + .map_err(|e| Error::custom(format!("Error reading VerifyingKey: {}", e)))?; + Ok(vk) +} + impl ValidityPredicatePublicInputs { pub fn inner(&self) -> &[pallas::Base; VP_CIRCUIT_PUBLIC_INPUT_NUM] { &self.0 @@ -190,8 +300,22 @@ impl From> for ValidityPredicatePublicInputs { } } -pub trait ValidityPredicateConfig { - fn configure_note(meta: &mut ConstraintSystem) -> NoteConfig { +#[derive(Clone, Debug)] +pub struct ValidityPredicateConfig { + pub note_conifg: NoteConfig, + pub advices: [Column; 10], + pub instances: Column, + pub get_is_input_note_flag_config: GetIsInputNoteFlagConfig, + pub get_owned_note_variable_config: GetOwnedNoteVariableConfig, + pub conditional_equal_config: ConditionalEqualConfig, + pub extended_or_relation_config: ExtendedOrRelationConfig, + pub add_config: AddConfig, + pub sub_config: SubConfig, + pub mul_config: MulConfig, +} + +impl ValidityPredicateConfig { + pub fn configure(meta: &mut ConstraintSystem) -> Self { let instances = meta.instance_column(); meta.enable_equality(instances); @@ -212,36 +336,7 @@ pub trait ValidityPredicateConfig { meta.enable_equality(*advice); } - NoteChip::configure(meta, instances, advices) - } - fn get_note_config(&self) -> NoteConfig; - fn configure(meta: &mut ConstraintSystem) -> Self; -} - -#[derive(Clone, Debug)] -pub struct GeneralVerificationValidityPredicateConfig { - pub note_conifg: NoteConfig, - pub advices: [Column; 10], - pub instances: Column, - pub get_is_input_note_flag_config: GetIsInputNoteFlagConfig, - pub get_owned_note_variable_config: GetOwnedNoteVariableConfig, - pub conditional_equal_config: ConditionalEqualConfig, - pub extended_or_relation_config: ExtendedOrRelationConfig, - pub add_config: AddConfig, - pub sub_config: SubConfig, - pub mul_config: MulConfig, -} - -impl ValidityPredicateConfig for GeneralVerificationValidityPredicateConfig { - fn get_note_config(&self) -> NoteConfig { - self.note_conifg.clone() - } - - fn configure(meta: &mut ConstraintSystem) -> Self { - let note_conifg = Self::configure_note(meta); - - let advices = note_conifg.advices; - let instances = note_conifg.instances; + let note_conifg = NoteChip::configure(meta, instances, advices); let get_owned_note_variable_config = GetOwnedNoteVariableConfig::configure( meta, @@ -277,30 +372,6 @@ impl ValidityPredicateConfig for GeneralVerificationValidityPredicateConfig { } } -pub trait ValidityPredicateInfo { - fn get_input_notes(&self) -> &[Note; NUM_NOTE]; - fn get_output_notes(&self) -> &[Note; NUM_NOTE]; - fn get_mandatory_public_inputs(&self) -> Vec { - let mut public_inputs = vec![]; - self.get_input_notes() - .iter() - .zip(self.get_output_notes().iter()) - .for_each(|(input_note, output_note)| { - let nf = input_note.get_nf().unwrap().inner(); - public_inputs.push(nf); - let cm = output_note.commitment(); - public_inputs.push(cm.get_x()); - }); - public_inputs.push(self.get_owned_note_pub_id()); - public_inputs - } - fn get_public_inputs(&self, rng: impl RngCore) -> ValidityPredicatePublicInputs; - // The owned_note_pub_id is the input_note_nf or the output_note_cm_x - // The owned_note_pub_id is the key to look up the target variables and - // help determine whether the owned note is the input note or not in VP circuit. - fn get_owned_note_pub_id(&self) -> pallas::Base; -} - pub trait ValidityPredicateVerifyingInfo: DynClone { fn get_verifying_info(&self) -> VPVerifyingInfo; fn get_vp_vk(&self) -> ValidityPredicateVerifyingKey; @@ -308,18 +379,15 @@ pub trait ValidityPredicateVerifyingInfo: DynClone { clone_trait_object!(ValidityPredicateVerifyingInfo); -pub trait ValidityPredicateCircuit: - Circuit + ValidityPredicateInfo + ValidityPredicateVerifyingInfo -{ - type VPConfig: ValidityPredicateConfig + Clone; +pub trait ValidityPredicateCircuit: Circuit + ValidityPredicateVerifyingInfo { // Default implementation, constrains the notes integrity. // TODO: how to enforce the constraints in vp circuit? fn basic_constraints( &self, - config: Self::VPConfig, + config: ValidityPredicateConfig, mut layouter: impl Layouter, ) -> Result { - let note_config = config.get_note_config(); + let note_config = config.note_conifg; // Load the Sinsemilla generator lookup table used by the whole circuit. SinsemillaChip::::load( note_config.sinsemilla_config.clone(), @@ -402,12 +470,34 @@ pub trait ValidityPredicateCircuit: // Add custom constraints on basic note variables and user-defined variables. fn custom_constraints( &self, - _config: Self::VPConfig, + _config: ValidityPredicateConfig, mut _layouter: impl Layouter, _basic_variables: BasicValidityPredicateVariables, ) -> Result<(), Error> { Ok(()) } + + fn get_mandatory_public_inputs(&self) -> Vec { + let mut public_inputs = vec![]; + self.get_input_notes() + .iter() + .zip(self.get_output_notes().iter()) + .for_each(|(input_note, output_note)| { + let nf = input_note.get_nf().unwrap().inner(); + public_inputs.push(nf); + let cm = output_note.commitment(); + public_inputs.push(cm.get_x()); + }); + public_inputs.push(self.get_owned_note_pub_id()); + public_inputs + } + fn get_input_notes(&self) -> &[Note; NUM_NOTE]; + fn get_output_notes(&self) -> &[Note; NUM_NOTE]; + fn get_public_inputs(&self, rng: impl RngCore) -> ValidityPredicatePublicInputs; + // The owned_note_pub_id is the input_note_nf or the output_note_cm_x + // The owned_note_pub_id is the key to look up the target variables and + // help determine whether the owned note is the input note or not in VP circuit. + fn get_owned_note_pub_id(&self) -> pallas::Base; } /// BasicValidityPredicateVariables are generally constrained in ValidityPredicateCircuit::basic_constraints @@ -704,7 +794,7 @@ impl BasicValidityPredicateVariables { macro_rules! vp_circuit_impl { ($name:ident) => { impl Circuit for $name { - type Config = ::VPConfig; + type Config = ValidityPredicateConfig; type FloorPlanner = floor_planner::V1; fn without_witnesses(&self) -> Self { @@ -996,4 +1086,39 @@ mod tests { ) .is_err()); } + + #[cfg(feature = "serde")] + #[test] + fn test_vk_serialize() { + use crate::circuit::{ + vp_circuit::{serde_deserialize_verifying_key, serde_serialize_verifying_key}, + vp_examples::TrivialValidityPredicateCircuit, + }; + use halo2_proofs::plonk::VerifyingKey; + use pasta_curves::vesta; + use serde_json; + + #[derive(serde::Serialize, serde::Deserialize)] + struct TestStruct { + #[serde( + serialize_with = "serde_serialize_verifying_key", + deserialize_with = "serde_deserialize_verifying_key" + )] + vk: VerifyingKey, + } + + let t = TrivialValidityPredicateCircuit::default().get_vp_vk(); + + let a = TestStruct { + vk: t.get_vk().unwrap(), + }; + + let ser = serde_json::to_string(&a).unwrap(); + let deser: TestStruct = serde_json::from_str(&ser).unwrap(); + + let a_bytes = a.vk.to_bytes(); + let deser_bytes = deser.vk.to_bytes(); + + assert_eq!(a_bytes, deser_bytes); + } } diff --git a/taiga_halo2/src/circuit/vp_examples.rs b/taiga_halo2/src/circuit/vp_examples.rs index 1da218b0..1246a5f6 100644 --- a/taiga_halo2/src/circuit/vp_examples.rs +++ b/taiga_halo2/src/circuit/vp_examples.rs @@ -1,8 +1,7 @@ use crate::{ circuit::vp_circuit::{ - GeneralVerificationValidityPredicateConfig, VPVerifyingInfo, ValidityPredicateCircuit, - ValidityPredicateConfig, ValidityPredicateInfo, ValidityPredicatePublicInputs, - ValidityPredicateVerifyingInfo, + VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, + ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, constant::{NUM_NOTE, SETUP_PARAMS_MAP}, note::{Note, RandomSeed}, @@ -17,6 +16,8 @@ use halo2_proofs::{ use lazy_static::lazy_static; use pasta_curves::pallas; use rand::{rngs::OsRng, RngCore}; +#[cfg(feature = "nif")] +use rustler::{Decoder, Encoder, Env, NifResult, NifStruct, Term}; pub mod cascade_intent; mod field_addition; @@ -40,6 +41,16 @@ pub struct TrivialValidityPredicateCircuit { pub output_notes: [Note; NUM_NOTE], } +// 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.VP.Trivial")] +struct TrivialValidtyPredicateCircuitProxy { + owned_note_pub_id: pallas::Base, + input_notes: Vec, + output_notes: Vec, +} + impl TrivialValidityPredicateCircuit { pub fn new( owned_note_pub_id: pallas::Base, @@ -52,9 +63,44 @@ impl TrivialValidityPredicateCircuit { output_notes, } } + + fn to_proxy(&self) -> TrivialValidtyPredicateCircuitProxy { + TrivialValidtyPredicateCircuitProxy { + owned_note_pub_id: self.owned_note_pub_id, + input_notes: self.input_notes.to_vec(), + output_notes: self.output_notes.to_vec(), + } + } +} + +impl TrivialValidtyPredicateCircuitProxy { + fn to_concrete(&self) -> Option { + let input_notes = self.input_notes.clone().try_into().ok()?; + let output_notes = self.output_notes.clone().try_into().ok()?; + let owned_note_pub_id = self.owned_note_pub_id; + Some(TrivialValidityPredicateCircuit { + owned_note_pub_id, + input_notes, + output_notes, + }) + } +} +#[cfg(feature = "nif")] +impl Encoder for TrivialValidityPredicateCircuit { + fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { + self.to_proxy().encode(env) + } +} +#[cfg(feature = "nif")] +impl<'a> Decoder<'a> for TrivialValidityPredicateCircuit { + fn decode(term: Term<'a>) -> NifResult { + let val: TrivialValidtyPredicateCircuitProxy = Decoder::decode(term)?; + val.to_concrete() + .ok_or(rustler::Error::RaiseAtom("Could not decode proxy")) + } } -impl ValidityPredicateInfo for TrivialValidityPredicateCircuit { +impl ValidityPredicateCircuit for TrivialValidityPredicateCircuit { fn get_input_notes(&self) -> &[Note; NUM_NOTE] { &self.input_notes } @@ -78,10 +124,6 @@ impl ValidityPredicateInfo for TrivialValidityPredicateCircuit { } } -impl ValidityPredicateCircuit for TrivialValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; -} - vp_circuit_impl!(TrivialValidityPredicateCircuit); #[cfg(test)] @@ -110,7 +152,7 @@ pub mod tests { #[test] fn test_halo2_trivial_vp_circuit() { - use crate::circuit::vp_circuit::ValidityPredicateInfo; + use crate::circuit::vp_circuit::ValidityPredicateCircuit; use halo2_proofs::dev::MockProver; use rand::rngs::OsRng; diff --git a/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs b/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs index eb8691bf..b19ccc76 100644 --- a/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs +++ b/taiga_halo2/src/circuit/vp_examples/cascade_intent.rs @@ -11,9 +11,8 @@ use crate::{ target_note_variable::{get_is_input_note_flag, get_owned_note_variable}, }, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, }, constant::{NUM_NOTE, SETUP_PARAMS_MAP}, @@ -54,32 +53,7 @@ impl CascadeIntentValidityPredicateCircuit { } } -impl ValidityPredicateInfo for CascadeIntentValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let padding = ValidityPredicatePublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for CascadeIntentValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints fn custom_constraints( &self, @@ -133,6 +107,28 @@ impl ValidityPredicateCircuit for CascadeIntentValidityPredicateCircuit { Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + let padding = ValidityPredicatePublicInputs::get_public_input_padding( + public_inputs.len(), + &RandomSeed::random(&mut rng), + ); + public_inputs.extend(padding); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(CascadeIntentValidityPredicateCircuit); diff --git a/taiga_halo2/src/circuit/vp_examples/field_addition.rs b/taiga_halo2/src/circuit/vp_examples/field_addition.rs index e452e0ec..b220f5ec 100644 --- a/taiga_halo2/src/circuit/vp_examples/field_addition.rs +++ b/taiga_halo2/src/circuit/vp_examples/field_addition.rs @@ -5,9 +5,8 @@ use crate::{ assign_free_advice, }, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, }, constant::{NUM_NOTE, SETUP_PARAMS_MAP, VP_CIRCUIT_CUSTOM_PUBLIC_INPUT_BEGIN_IDX}, @@ -33,33 +32,7 @@ struct FieldAdditionValidityPredicateCircuit { b: pallas::Base, } -impl ValidityPredicateInfo for FieldAdditionValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - public_inputs.push(self.a + self.b); - let padding = ValidityPredicatePublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for FieldAdditionValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints // Note: the trivial vp doesn't constrain on input_note_variables and output_note_variables fn custom_constraints( @@ -93,6 +66,29 @@ impl ValidityPredicateCircuit for FieldAdditionValidityPredicateCircuit { Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + public_inputs.push(self.a + self.b); + let padding = ValidityPredicatePublicInputs::get_public_input_padding( + public_inputs.len(), + &RandomSeed::random(&mut rng), + ); + public_inputs.extend(padding); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(FieldAdditionValidityPredicateCircuit); 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 cb26dc98..63556c09 100644 --- a/taiga_halo2/src/circuit/vp_examples/or_relation_intent.rs +++ b/taiga_halo2/src/circuit/vp_examples/or_relation_intent.rs @@ -10,9 +10,8 @@ use crate::{ target_note_variable::{get_is_input_note_flag, get_owned_note_variable}, }, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, vp_examples::token::{transfrom_token_name_to_token_property, TOKEN_VK}, }, @@ -80,32 +79,7 @@ impl OrRelationIntentValidityPredicateCircuit { } } -impl ValidityPredicateInfo for OrRelationIntentValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let padding = ValidityPredicatePublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for OrRelationIntentValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints fn custom_constraints( &self, @@ -170,7 +144,7 @@ impl ValidityPredicateCircuit for OrRelationIntentValidityPredicateCircuit { // Encode the app_data_static of intent note let encoded_app_data_static = poseidon_hash_gadget( - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config, layouter.namespace(|| "encode app_data_static"), [ token_property_1.clone(), @@ -255,6 +229,28 @@ impl ValidityPredicateCircuit for OrRelationIntentValidityPredicateCircuit { Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + let padding = ValidityPredicatePublicInputs::get_public_input_padding( + public_inputs.len(), + &RandomSeed::random(&mut rng), + ); + public_inputs.extend(padding); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(OrRelationIntentValidityPredicateCircuit); 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 fe792974..9d8118ac 100644 --- a/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs +++ b/taiga_halo2/src/circuit/vp_examples/partial_fulfillment_intent.rs @@ -12,9 +12,8 @@ use crate::{ target_note_variable::{get_is_input_note_flag, get_owned_note_variable}, }, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, vp_examples::token::{transfrom_token_name_to_token_property, Token, TOKEN_VK}, }, @@ -76,32 +75,7 @@ impl PartialFulfillmentIntentValidityPredicateCircuit { } } -impl ValidityPredicateInfo for PartialFulfillmentIntentValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let padding = ValidityPredicatePublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for PartialFulfillmentIntentValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints fn custom_constraints( &self, @@ -162,7 +136,7 @@ impl ValidityPredicateCircuit for PartialFulfillmentIntentValidityPredicateCircu // Encode the app_data_static of intent note let encoded_app_data_static = poseidon_hash_gadget( - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config, layouter.namespace(|| "app_data_static encoding"), [ sold_token.clone(), @@ -408,6 +382,28 @@ impl ValidityPredicateCircuit for PartialFulfillmentIntentValidityPredicateCircu Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + let padding = ValidityPredicatePublicInputs::get_public_input_padding( + public_inputs.len(), + &RandomSeed::random(&mut rng), + ); + public_inputs.extend(padding); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(PartialFulfillmentIntentValidityPredicateCircuit); diff --git a/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs b/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs index bc911fe1..6fe7f15b 100644 --- a/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs +++ b/taiga_halo2/src/circuit/vp_examples/receiver_vp.rs @@ -6,9 +6,8 @@ use crate::{ }, note_encryption_circuit::note_encryption_gadget, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, vp_examples::signature_verification::COMPRESSED_TOKEN_AUTH_VK, }, @@ -67,60 +66,7 @@ impl Default for ReceiverValidityPredicateCircuit { } } -impl ValidityPredicateInfo for ReceiverValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let custom_public_input_padding = - ValidityPredicatePublicInputs::get_custom_public_input_padding( - public_inputs.len(), - &RandomSeed::random(rng), - ); - public_inputs.extend(custom_public_input_padding.iter()); - assert_eq!(NUM_NOTE, 2); - let target_note = - if self.get_owned_note_pub_id() == self.get_output_notes()[0].commitment().get_x() { - self.get_output_notes()[0] - } else { - self.get_output_notes()[1] - }; - let message = vec![ - target_note.note_type.app_vk, - target_note.note_type.app_data_static, - target_note.app_data_dynamic, - pallas::Base::from(target_note.value), - target_note.rho.inner(), - target_note.get_nk_commitment(), - target_note.psi, - target_note.rcm, - ]; - let plaintext = NotePlaintext::padding(&message); - let key = SecretKey::from_dh_exchange(&self.rcv_pk, &mod_r_p(self.sk)); - let cipher = NoteCiphertext::encrypt(&plaintext, &key, &self.nonce); - cipher.inner().iter().for_each(|&c| public_inputs.push(c)); - - let generator = GENERATOR.to_curve(); - let pk = generator * mod_r_p(self.sk); - let pk_coord = pk.to_affine().coordinates().unwrap(); - public_inputs.push(*pk_coord.x()); - public_inputs.push(*pk_coord.y()); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for ReceiverValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints fn custom_constraints( &self, @@ -141,7 +87,7 @@ impl ValidityPredicateCircuit for ReceiverValidityPredicateCircuit { )?; // Construct an ECC chip - let ecc_chip = EccChip::construct(config.get_note_config().ecc_config); + let ecc_chip = EccChip::construct(config.note_conifg.ecc_config); let rcv_pk = NonIdentityPoint::new( ecc_chip.clone(), @@ -170,7 +116,7 @@ impl ValidityPredicateCircuit for ReceiverValidityPredicateCircuit { // Decode the app_data_dynamic, and check the app_data_dynamic encoding let encoded_app_data_dynamic = poseidon_hash_gadget( - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config.clone(), layouter.namespace(|| "app_data_dynamic encoding"), [ rcv_pk.inner().x(), @@ -257,7 +203,7 @@ impl ValidityPredicateCircuit for ReceiverValidityPredicateCircuit { layouter.namespace(|| "note encryption"), config.advices[0], config.instances, - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config, add_chip, ecc_chip, nonce, @@ -268,6 +214,56 @@ impl ValidityPredicateCircuit for ReceiverValidityPredicateCircuit { Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + let custom_public_input_padding = + ValidityPredicatePublicInputs::get_custom_public_input_padding( + public_inputs.len(), + &RandomSeed::random(rng), + ); + public_inputs.extend(custom_public_input_padding.iter()); + assert_eq!(NUM_NOTE, 2); + let target_note = + if self.get_owned_note_pub_id() == self.get_output_notes()[0].commitment().get_x() { + self.get_output_notes()[0] + } else { + self.get_output_notes()[1] + }; + let message = vec![ + target_note.note_type.app_vk, + target_note.note_type.app_data_static, + target_note.app_data_dynamic, + pallas::Base::from(target_note.value), + target_note.rho.inner(), + target_note.get_nk_commitment(), + target_note.psi, + target_note.rcm, + ]; + let plaintext = NotePlaintext::padding(&message); + let key = SecretKey::from_dh_exchange(&self.rcv_pk, &mod_r_p(self.sk)); + let cipher = NoteCiphertext::encrypt(&plaintext, &key, &self.nonce); + cipher.inner().iter().for_each(|&c| public_inputs.push(c)); + + let generator = GENERATOR.to_curve(); + let pk = generator * mod_r_p(self.sk); + let pk_coord = pk.to_affine().coordinates().unwrap(); + public_inputs.push(*pk_coord.x()); + public_inputs.push(*pk_coord.y()); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(ReceiverValidityPredicateCircuit); diff --git a/taiga_halo2/src/circuit/vp_examples/signature_verification.rs b/taiga_halo2/src/circuit/vp_examples/signature_verification.rs index 3b078985..620628b9 100644 --- a/taiga_halo2/src/circuit/vp_examples/signature_verification.rs +++ b/taiga_halo2/src/circuit/vp_examples/signature_verification.rs @@ -5,9 +5,8 @@ use crate::{ target_note_variable::get_owned_note_variable, }, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, }, constant::{TaigaFixedBasesFull, NUM_NOTE, SETUP_PARAMS_MAP}, @@ -149,32 +148,7 @@ impl SignatureVerificationValidityPredicateCircuit { } } -impl ValidityPredicateInfo for SignatureVerificationValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let padding = ValidityPredicatePublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for SignatureVerificationValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints fn custom_constraints( &self, @@ -183,7 +157,7 @@ impl ValidityPredicateCircuit for SignatureVerificationValidityPredicateCircuit basic_variables: BasicValidityPredicateVariables, ) -> Result<(), Error> { // Construct an ECC chip - let ecc_chip = EccChip::construct(config.get_note_config().ecc_config); + let ecc_chip = EccChip::construct(config.note_conifg.ecc_config); let pk = NonIdentityPoint::new( ecc_chip.clone(), @@ -213,7 +187,7 @@ impl ValidityPredicateCircuit for SignatureVerificationValidityPredicateCircuit // Decode the app_data_dynamic, and check the app_data_dynamic encoding let encoded_app_data_dynamic = poseidon_hash_gadget( - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config.clone(), layouter.namespace(|| "app_data_dynamic encoding"), [pk.inner().x(), pk.inner().y(), auth_vp_vk, receiver_vp_vk], )?; @@ -248,7 +222,7 @@ impl ValidityPredicateCircuit for SignatureVerificationValidityPredicateCircuit let cms = basic_variables.get_output_note_cms(); assert_eq!(NUM_NOTE, 2); let h = poseidon_hash_gadget( - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config, layouter.namespace(|| "Poseidon_hash(r, P, m)"), [ r.inner().x(), @@ -275,6 +249,28 @@ impl ValidityPredicateCircuit for SignatureVerificationValidityPredicateCircuit Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + let padding = ValidityPredicatePublicInputs::get_public_input_padding( + public_inputs.len(), + &RandomSeed::random(&mut rng), + ); + public_inputs.extend(padding); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(SignatureVerificationValidityPredicateCircuit); diff --git a/taiga_halo2/src/circuit/vp_examples/token.rs b/taiga_halo2/src/circuit/vp_examples/token.rs index 53121334..b57f05c3 100644 --- a/taiga_halo2/src/circuit/vp_examples/token.rs +++ b/taiga_halo2/src/circuit/vp_examples/token.rs @@ -5,9 +5,8 @@ use crate::{ target_note_variable::get_owned_note_variable, }, vp_circuit::{ - BasicValidityPredicateVariables, GeneralVerificationValidityPredicateConfig, - VPVerifyingInfo, ValidityPredicateCircuit, ValidityPredicateConfig, - ValidityPredicateInfo, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, + BasicValidityPredicateVariables, VPVerifyingInfo, ValidityPredicateCircuit, + ValidityPredicateConfig, ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo, }, vp_examples::receiver_vp::{ReceiverValidityPredicateCircuit, COMPRESSED_RECEIVER_VK}, vp_examples::signature_verification::{ @@ -93,32 +92,7 @@ impl Default for TokenValidityPredicateCircuit { } } -impl ValidityPredicateInfo for TokenValidityPredicateCircuit { - fn get_input_notes(&self) -> &[Note; NUM_NOTE] { - &self.input_notes - } - - fn get_output_notes(&self) -> &[Note; NUM_NOTE] { - &self.output_notes - } - - fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { - let mut public_inputs = self.get_mandatory_public_inputs(); - let padding = ValidityPredicatePublicInputs::get_public_input_padding( - public_inputs.len(), - &RandomSeed::random(&mut rng), - ); - public_inputs.extend(padding); - public_inputs.into() - } - - fn get_owned_note_pub_id(&self) -> pallas::Base { - self.owned_note_pub_id - } -} - impl ValidityPredicateCircuit for TokenValidityPredicateCircuit { - type VPConfig = GeneralVerificationValidityPredicateConfig; // Add custom constraints fn custom_constraints( &self, @@ -151,7 +125,7 @@ impl ValidityPredicateCircuit for TokenValidityPredicateCircuit { )?; // Construct an ECC chip - let ecc_chip = EccChip::construct(config.get_note_config().ecc_config); + let ecc_chip = EccChip::construct(config.note_conifg.ecc_config); let pk = NonIdentityPoint::new( ecc_chip, @@ -181,7 +155,7 @@ impl ValidityPredicateCircuit for TokenValidityPredicateCircuit { // Decode the app_data_dynamic, and check the app_data_dynamic encoding let encoded_app_data_dynamic = poseidon_hash_gadget( - config.get_note_config().poseidon_config, + config.note_conifg.poseidon_config, layouter.namespace(|| "app_data_dynamic encoding"), [pk.inner().x(), pk.inner().y(), auth_vp_vk, receiver_vp_vk], )?; @@ -215,6 +189,28 @@ impl ValidityPredicateCircuit for TokenValidityPredicateCircuit { Ok(()) } + + fn get_input_notes(&self) -> &[Note; NUM_NOTE] { + &self.input_notes + } + + fn get_output_notes(&self) -> &[Note; NUM_NOTE] { + &self.output_notes + } + + fn get_public_inputs(&self, mut rng: impl RngCore) -> ValidityPredicatePublicInputs { + let mut public_inputs = self.get_mandatory_public_inputs(); + let padding = ValidityPredicatePublicInputs::get_public_input_padding( + public_inputs.len(), + &RandomSeed::random(&mut rng), + ); + public_inputs.extend(padding); + public_inputs.into() + } + + fn get_owned_note_pub_id(&self) -> pallas::Base { + self.owned_note_pub_id + } } vp_circuit_impl!(TokenValidityPredicateCircuit); diff --git a/taiga_halo2/src/merkle_tree.rs b/taiga_halo2/src/merkle_tree.rs index ceb27235..752618a0 100644 --- a/taiga_halo2/src/merkle_tree.rs +++ b/taiga_halo2/src/merkle_tree.rs @@ -2,8 +2,6 @@ use std::hash::{Hash, Hasher}; use crate::utils::poseidon_hash; use crate::{constant::TAIGA_COMMITMENT_TREE_DEPTH, note::Note}; -use borsh::{BorshDeserialize, BorshSerialize}; -use ff::PrimeField; use halo2_proofs::arithmetic::Field; use pasta_curves::pallas; use rand::{Rng, RngCore}; @@ -11,7 +9,18 @@ use rand::{Rng, RngCore}; use crate::merkle_tree::LR::{L, R}; use rand::distributions::{Distribution, Standard}; -#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Default, BorshSerialize, BorshDeserialize)] +#[cfg(feature = "borsh")] +use ff::PrimeField; + +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Default)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum LR { R, #[default] @@ -44,7 +53,9 @@ impl Distribution for Standard { /// A path from a position in a particular commitment tree to the root of that tree. /// In Orchard merkle tree, they are using MerkleCRH(layer, left, right), where MerkleCRH is a sinsemilla. We are using poseidon_hash(left, right). -#[derive(Clone, Debug, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MerklePath { merkle_path: Vec<(Node, LR)>, } @@ -92,6 +103,7 @@ impl Default for MerklePath { /// A node within the Sapling commitment tree. #[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Node(pallas::Base); impl Node { @@ -116,6 +128,7 @@ impl Node { } } +#[cfg(feature = "borsh")] impl BorshSerialize for Node { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { writer.write_all(&self.0.to_repr())?; @@ -123,6 +136,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]; diff --git a/taiga_halo2/src/note.rs b/taiga_halo2/src/note.rs index d6c5128e..aee6b11b 100644 --- a/taiga_halo2/src/note.rs +++ b/taiga_halo2/src/note.rs @@ -1,6 +1,6 @@ use crate::{ circuit::{ - vp_circuit::ValidityPredicateVerifyingInfo, + vp_circuit::ValidityPredicate, vp_examples::{TrivialValidityPredicateCircuit, COMPRESSED_TRIVIAL_VP_VK}, }, constant::{ @@ -14,8 +14,6 @@ use crate::{ }; use bitvec::{array::BitArray, order::Lsb0}; use blake2b_simd::Params as Blake2bParams; -use borsh::{BorshDeserialize, BorshSerialize}; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use core::iter; use ff::{FromUniformBytes, PrimeField}; use halo2_proofs::arithmetic::Field; @@ -24,13 +22,20 @@ use pasta_curves::{ pallas, }; use rand::RngCore; -use std::{ - hash::{Hash, Hasher}, - io, -}; +#[cfg(feature = "nif")] +use rustler::{NifStruct, NifTuple}; +use std::hash::{Hash, Hasher}; + +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; /// A commitment to a note. -#[derive(Copy, Debug, Clone)] +#[derive(Copy, Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "nif", derive(NifTuple))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct NoteCommitment(pallas::Point); impl NoteCommitment { @@ -53,8 +58,37 @@ impl Default for NoteCommitment { } } +#[cfg(feature = "borsh")] +impl BorshSerialize for NoteCommitment { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + writer.write_all(&self.0.to_bytes())?; + Ok(()) + } +} + +#[cfg(feature = "borsh")] +impl BorshDeserialize for NoteCommitment { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let mut repr = [0u8; 32]; + reader.read_exact(&mut repr)?; + let value = Option::from(pallas::Point::from_bytes(&repr)).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "Node value not in field") + })?; + Ok(Self(value)) + } +} + +impl Hash for NoteCommitment { + fn hash(&self, state: &mut H) { + self.0.to_bytes().as_ref().hash(state); + } +} + /// A note #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.Note")] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Note { pub note_type: NoteType, /// app_data_dynamic is the data defined in application vp and will NOT be used to derive type @@ -75,7 +109,10 @@ pub struct Note { } /// The parameters in the NoteType are used to derive note type. -#[derive(Debug, Clone, Copy, Default, Eq)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.NoteType")] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct NoteType { /// app_vk is the compressed verifying key of VP pub app_vk: pallas::Base, @@ -83,12 +120,6 @@ pub struct NoteType { pub app_data_static: pallas::Base, } -impl PartialEq for NoteType { - fn eq(&self, other: &Self) -> bool { - self.app_vk == other.app_vk && self.app_data_static == other.app_data_static - } -} - #[derive(Copy, Clone, Debug, Default)] pub struct RandomSeed([u8; 32]); @@ -96,15 +127,15 @@ pub struct RandomSeed([u8; 32]); pub struct InputNoteProvingInfo { pub note: Note, pub merkle_path: MerklePath, - app_vp_verifying_info: Box, - app_vp_verifying_info_dynamic: Vec>, + application_vp: Box, + dynamic_vps: Vec>, } #[derive(Clone)] pub struct OutputNoteProvingInfo { pub note: Note, - app_vp_verifying_info: Box, - app_vp_verifying_info_dynamic: Vec>, + application_vp: Box, + dynamic_vps: Vec>, } impl Note { @@ -285,8 +316,10 @@ impl Note { } } +#[cfg(feature = "borsh")] impl BorshSerialize for Note { fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + use byteorder::{LittleEndian, WriteBytesExt}; // Write app_vk writer.write_all(&self.note_type.app_vk.to_repr())?; // Write app_data_static @@ -319,8 +352,11 @@ impl BorshSerialize for Note { } } +#[cfg(feature = "borsh")] impl BorshDeserialize for Note { - fn deserialize_reader(reader: &mut R) -> io::Result { + fn deserialize_reader(reader: &mut R) -> std::io::Result { + use byteorder::{LittleEndian, ReadBytesExt}; + use std::io; // Read app_vk let mut app_vk_bytes = [0u8; 32]; reader.read_exact(&mut app_vk_bytes)?; @@ -466,25 +502,23 @@ impl InputNoteProvingInfo { pub fn new( note: Note, merkle_path: MerklePath, - app_vp_verifying_info: Box, - app_vp_verifying_info_dynamic: Vec>, + application_vp: Box, + dynamic_vps: Vec>, ) -> Self { Self { note, merkle_path, - app_vp_verifying_info, - app_vp_verifying_info_dynamic, + application_vp, + dynamic_vps, } } - pub fn get_app_vp_verifying_info(&self) -> Box { - self.app_vp_verifying_info.clone() + pub fn get_application_vp(&self) -> Box { + self.application_vp.clone() } - pub fn get_app_vp_verifying_info_dynamic( - &self, - ) -> Vec> { - self.app_vp_verifying_info_dynamic.clone() + pub fn get_dynamic_vps(&self) -> Vec> { + self.dynamic_vps.clone() } pub fn create_padding_note_proving_info( @@ -505,24 +539,22 @@ impl InputNoteProvingInfo { impl OutputNoteProvingInfo { pub fn new( note: Note, - app_vp_verifying_info: Box, - app_vp_verifying_info_dynamic: Vec>, + application_vp: Box, + dynamic_vps: Vec>, ) -> Self { Self { note, - app_vp_verifying_info, - app_vp_verifying_info_dynamic, + application_vp, + dynamic_vps, } } - pub fn get_app_vp_verifying_info(&self) -> Box { - self.app_vp_verifying_info.clone() + pub fn get_application_vp(&self) -> Box { + self.application_vp.clone() } - pub fn get_app_vp_verifying_info_dynamic( - &self, - ) -> Vec> { - self.app_vp_verifying_info_dynamic.clone() + pub fn get_dynamic_vps(&self) -> Vec> { + self.dynamic_vps.clone() } pub fn create_padding_note_proving_info( @@ -593,14 +625,9 @@ pub mod tests { pub fn random_input_proving_info(mut rng: R) -> InputNoteProvingInfo { let note = random_input_note(&mut rng); let merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); - let app_vp_verifying_info = Box::new(random_trivial_vp_circuit(&mut rng)); - let app_vp_verifying_info_dynamic = vec![]; - InputNoteProvingInfo::new( - note, - merkle_path, - app_vp_verifying_info, - app_vp_verifying_info_dynamic, - ) + let application_vp = Box::new(random_trivial_vp_circuit(&mut rng)); + let dynamic_vps = vec![]; + InputNoteProvingInfo::new(note, merkle_path, application_vp, dynamic_vps) } pub fn random_output_proving_info( @@ -608,19 +635,22 @@ pub mod tests { rho: Nullifier, ) -> OutputNoteProvingInfo { let note = random_output_note(&mut rng, rho); - let app_vp_verifying_info = Box::new(random_trivial_vp_circuit(&mut rng)); - let app_vp_verifying_info_dynamic = vec![]; + let application_vp = Box::new(random_trivial_vp_circuit(&mut rng)); + let dynamic_vps = vec![]; OutputNoteProvingInfo { note, - app_vp_verifying_info, - app_vp_verifying_info_dynamic, + application_vp, + dynamic_vps, } } + #[cfg(feature = "borsh")] #[test] - fn note_serialization_test() { + fn note_borsh_serialization_test() { use borsh::{BorshDeserialize, BorshSerialize}; use rand::rngs::OsRng; + + use crate::note::NoteCommitment; let mut rng = OsRng; let input_note = random_input_note(&mut rng); @@ -640,5 +670,25 @@ pub mod tests { let de_note: Note = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); assert_eq!(output_note, de_note); } + + let icm = input_note.commitment(); + { + // BorshSerialize + let borsh = icm.try_to_vec().unwrap(); + // BorshDeserialize + let de_icm: NoteCommitment = + BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); + assert_eq!(icm, de_icm); + } + + let ocm = output_note.commitment(); + { + // BorshSerialize + let borsh = ocm.try_to_vec().unwrap(); + // BorshDeserialize + let de_ocm: NoteCommitment = + BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); + assert_eq!(ocm, de_ocm); + } } } diff --git a/taiga_halo2/src/nullifier.rs b/taiga_halo2/src/nullifier.rs index 185fc469..6c512b0d 100644 --- a/taiga_halo2/src/nullifier.rs +++ b/taiga_halo2/src/nullifier.rs @@ -5,20 +5,31 @@ use crate::{ note::NoteCommitment, utils::{extract_p, mod_r_p, prf_nf}, }; -use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::arithmetic::Field; use pasta_curves::group::cofactor::CofactorCurveAffine; use pasta_curves::group::ff::PrimeField; use pasta_curves::pallas; use rand::RngCore; +#[cfg(feature = "nif")] +use rustler::{NifTaggedEnum, NifTuple}; use subtle::CtOption; +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; + /// The unique nullifier. #[derive(Copy, Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "nif", derive(NifTuple))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Nullifier(pallas::Base); /// The NullifierKeyContainer contains the nullifier_key or the nullifier_key commitment #[derive(Copy, Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "nif", derive(NifTaggedEnum))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum NullifierKeyContainer { // The NullifierKeyContainer::Commitment is the commitment of NullifierKeyContainer::Key `nk_com = Commitment(nk, 0)` Commitment(pallas::Base), @@ -71,6 +82,7 @@ impl Default for Nullifier { } } +#[cfg(feature = "borsh")] impl BorshSerialize for Nullifier { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { writer.write_all(&self.0.to_repr())?; @@ -78,6 +90,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]; diff --git a/taiga_halo2/src/proof.rs b/taiga_halo2/src/proof.rs index c6874efe..aeff0861 100644 --- a/taiga_halo2/src/proof.rs +++ b/taiga_halo2/src/proof.rs @@ -1,4 +1,3 @@ -use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::{ plonk::{self, Circuit, ProvingKey, SingleVerifier, VerifyingKey}, poly::commitment::Params, @@ -6,8 +5,19 @@ use halo2_proofs::{ }; use pasta_curves::{pallas, vesta}; use rand::RngCore; +#[cfg(feature = "nif")] +use rustler::NifTuple; + +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "nif", derive(NifTuple))] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Proof(Vec); impl Proof { diff --git a/taiga_halo2/src/shielded_ptx.rs b/taiga_halo2/src/shielded_ptx.rs index dd277d9e..f404515f 100644 --- a/taiga_halo2/src/shielded_ptx.rs +++ b/taiga_halo2/src/shielded_ptx.rs @@ -1,5 +1,5 @@ use crate::action::{ActionInfo, ActionInstance}; -use crate::circuit::vp_circuit::{VPVerifyingInfo, ValidityPredicateVerifyingInfo}; +use crate::circuit::vp_circuit::{VPVerifyingInfo, ValidityPredicate}; use crate::constant::{ ACTION_CIRCUIT_PARAMS_SIZE, ACTION_PROVING_KEY, ACTION_VERIFYING_KEY, NUM_NOTE, SETUP_PARAMS_MAP, @@ -10,25 +10,41 @@ use crate::note::{InputNoteProvingInfo, OutputNoteProvingInfo}; use crate::nullifier::Nullifier; use crate::proof::Proof; use crate::value_commitment::ValueCommitment; -use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::plonk::Error; use pasta_curves::pallas; use rand::RngCore; +#[cfg(feature = "nif")] +use rustler::{Decoder, Encoder, Env, NifResult, NifStruct, Term}; + +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ShieldedPartialTransaction { actions: [ActionVerifyingInfo; NUM_NOTE], inputs: [NoteVPVerifyingInfoSet; NUM_NOTE], outputs: [NoteVPVerifyingInfoSet; NUM_NOTE], } -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.Action.VerifyingInfo")] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ActionVerifyingInfo { action_proof: Proof, action_instance: ActionInstance, } -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.Note.VerifyingInfo")] pub struct NoteVPVerifyingInfoSet { app_vp_verifying_info: VPVerifyingInfo, app_dynamic_vp_verifying_info: Vec, @@ -36,6 +52,16 @@ pub struct NoteVPVerifyingInfoSet { // When the verifier proof is added, we may need to reconsider the structure of `VPVerifyingInfo` } +// 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 { + actions: Vec, + inputs: Vec, + outputs: Vec, +} + impl ShieldedPartialTransaction { pub fn build( input_info: [InputNoteProvingInfo; NUM_NOTE], @@ -46,8 +72,8 @@ impl ShieldedPartialTransaction { .iter() .map(|input_note| { NoteVPVerifyingInfoSet::build( - input_note.get_app_vp_verifying_info(), - input_note.get_app_vp_verifying_info_dynamic(), + input_note.get_application_vp(), + input_note.get_dynamic_vps(), ) }) .collect(); @@ -55,8 +81,8 @@ impl ShieldedPartialTransaction { .iter() .map(|output_note| { NoteVPVerifyingInfoSet::build( - output_note.get_app_vp_verifying_info(), - output_note.get_app_vp_verifying_info_dynamic(), + output_note.get_application_vp(), + output_note.get_dynamic_vps(), ) }) .collect(); @@ -163,6 +189,28 @@ impl ShieldedPartialTransaction { } Ok(()) } + + // Conversion to the generic length proxy + fn to_proxy(&self) -> ShieldedPartialTransactionProxy { + ShieldedPartialTransactionProxy { + actions: self.actions.to_vec(), + inputs: self.inputs.to_vec(), + outputs: self.outputs.to_vec(), + } + } +} + +impl ShieldedPartialTransactionProxy { + fn to_concrete(&self) -> Option { + let actions = self.actions.clone().try_into().ok()?; + let inputs = self.inputs.clone().try_into().ok()?; + let outputs = self.outputs.clone().try_into().ok()?; + Some(ShieldedPartialTransaction { + actions, + inputs, + outputs, + }) + } } impl Executable for ShieldedPartialTransaction { @@ -202,6 +250,7 @@ impl Executable for ShieldedPartialTransaction { } } +#[cfg(feature = "borsh")] impl BorshSerialize for ShieldedPartialTransaction { fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { for action in self.actions.iter() { @@ -220,6 +269,7 @@ impl BorshSerialize for ShieldedPartialTransaction { } } +#[cfg(feature = "borsh")] impl BorshDeserialize for ShieldedPartialTransaction { fn deserialize_reader(reader: &mut R) -> std::io::Result { let actions: Vec<_> = (0..NUM_NOTE) @@ -238,6 +288,23 @@ impl BorshDeserialize for ShieldedPartialTransaction { }) } } + +#[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 ActionVerifyingInfo { pub fn create(action_info: ActionInfo, mut rng: R) -> Result { let (action_instance, circuit) = action_info.build(); @@ -278,12 +345,12 @@ impl NoteVPVerifyingInfoSet { } pub fn build( - app_vp_verifying_info: Box, - app_vp_verifying_info_dynamic: Vec>, + application_vp: Box, + dynamic_vps: Vec>, ) -> Self { - let app_vp_verifying_info = app_vp_verifying_info.get_verifying_info(); + let app_vp_verifying_info = application_vp.get_verifying_info(); - let app_dynamic_vp_verifying_info = app_vp_verifying_info_dynamic + let app_dynamic_vp_verifying_info = dynamic_vps .into_iter() .map(|verifying_info| verifying_info.get_verifying_info()) .collect(); @@ -328,7 +395,7 @@ impl NoteVPVerifyingInfoSet { #[cfg(test)] pub mod testing { use crate::{ - circuit::vp_circuit::ValidityPredicateVerifyingInfo, + circuit::vp_circuit::{ValidityPredicate, ValidityPredicateVerifyingInfo}, circuit::vp_examples::TrivialValidityPredicateCircuit, constant::TAIGA_COMMITMENT_TREE_DEPTH, merkle_tree::MerklePath, @@ -443,43 +510,36 @@ pub mod testing { input_notes: [input_note_1, input_note_2], output_notes: [output_note_1, output_note_2], }; - let input_app_vp_verifying_info_1 = Box::new(trivial_vp_circuit.clone()); - let trivial_app_logic_1: Box = - Box::new(trivial_vp_circuit.clone()); + let input_application_vp_1 = Box::new(trivial_vp_circuit.clone()); + let trivial_app_logic_1: Box = Box::new(trivial_vp_circuit.clone()); let trivial_app_logic_2 = Box::new(trivial_vp_circuit.clone()); - let trivial_app_vp_verifying_info_dynamic = vec![trivial_app_logic_1, trivial_app_logic_2]; + let trivial_dynamic_vps = vec![trivial_app_logic_1, trivial_app_logic_2]; let input_note_proving_info_1 = InputNoteProvingInfo::new( input_note_1, merkle_path.clone(), - input_app_vp_verifying_info_1, - trivial_app_vp_verifying_info_dynamic.clone(), + input_application_vp_1, + trivial_dynamic_vps.clone(), ); // The following notes use empty logic vps and use app_data_dynamic with pallas::Base::zero() by default. trivial_vp_circuit.owned_note_pub_id = input_note_2.get_nf().unwrap().inner(); - let input_app_vp_verifying_info_2 = Box::new(trivial_vp_circuit.clone()); - let app_vp_verifying_info_dynamic = vec![]; + let input_application_vp_2 = Box::new(trivial_vp_circuit.clone()); + let dynamic_vps = vec![]; let input_note_proving_info_2 = InputNoteProvingInfo::new( input_note_2, merkle_path, - input_app_vp_verifying_info_2, - app_vp_verifying_info_dynamic.clone(), + input_application_vp_2, + dynamic_vps.clone(), ); trivial_vp_circuit.owned_note_pub_id = output_note_1.commitment().get_x(); - let output_app_vp_verifying_info_1 = Box::new(trivial_vp_circuit.clone()); - let output_note_proving_info_1 = OutputNoteProvingInfo::new( - output_note_1, - output_app_vp_verifying_info_1, - app_vp_verifying_info_dynamic.clone(), - ); + let output_application_vp_1 = Box::new(trivial_vp_circuit.clone()); + let output_note_proving_info_1 = + OutputNoteProvingInfo::new(output_note_1, output_application_vp_1, dynamic_vps.clone()); trivial_vp_circuit.owned_note_pub_id = output_note_2.commitment().get_x(); - let output_app_vp_verifying_info_2 = Box::new(trivial_vp_circuit); - let output_note_proving_info_2 = OutputNoteProvingInfo::new( - output_note_2, - output_app_vp_verifying_info_2, - app_vp_verifying_info_dynamic, - ); + let output_application_vp_2 = Box::new(trivial_vp_circuit); + let output_note_proving_info_2 = + OutputNoteProvingInfo::new(output_note_2, output_application_vp_2, dynamic_vps); // Create shielded partial tx ShieldedPartialTransaction::build( diff --git a/taiga_halo2/src/transaction.rs b/taiga_halo2/src/transaction.rs index dd21b9ba..ab6bb348 100644 --- a/taiga_halo2/src/transaction.rs +++ b/taiga_halo2/src/transaction.rs @@ -7,14 +7,25 @@ use crate::shielded_ptx::ShieldedPartialTransaction; use crate::transparent_ptx::{OutputResource, TransparentPartialTransaction}; use crate::value_commitment::ValueCommitment; use blake2b_simd::Params as Blake2bParams; -use borsh::{BorshDeserialize, BorshSerialize}; use pasta_curves::{ group::{ff::PrimeField, Group}, pallas, }; use rand::{CryptoRng, RngCore}; +#[cfg(feature = "nif")] +use rustler::types::atom; +#[cfg(feature = "nif")] +use rustler::{atoms, Decoder, Env, NifRecord, NifResult, NifStruct, Term}; + +#[cfg(feature = "serde")] +use serde; -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Transaction { // TODO: Other parameters to be added. shielded_ptx_bundle: Option, @@ -23,25 +34,35 @@ pub struct Transaction { signature: InProgressBindingSignature, } -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InProgressBindingSignature { Authorized(BindingSignature), Unauthorized(BindingSigningKey), } -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "nif", derive(NifRecord))] +#[cfg_attr(feature = "nif", tag = "bundle")] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ShieldedPartialTxBundle { partial_txs: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "nif", derive(NifStruct))] +#[cfg_attr(feature = "nif", module = "Taiga.Transaction.Result")] pub struct ShieldedResult { anchors: Vec, nullifiers: Vec, output_cms: Vec, } -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TransparentPartialTxBundle { partial_txs: Vec, } @@ -198,6 +219,51 @@ impl Transaction { } } +#[cfg(feature = "nif")] +atoms! { transaction } + +#[cfg(feature = "nif")] +impl rustler::Encoder for Transaction { + fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { + ( + transaction().encode(env), + self.shielded_ptx_bundle.encode(env), + self.transparent_ptx_bundle + .try_to_vec() + .unwrap_or(vec![]) + .encode(env), + self.signature.try_to_vec().unwrap_or(vec![]).encode(env), + ) + .encode(env) + } +} + +#[cfg(feature = "nif")] +impl<'a> Decoder<'a> for Transaction { + fn decode(term: Term<'a>) -> NifResult { + let (term, shielded_ptx_bundle, transparent_bytes, sig_bytes): ( + atom::Atom, + Option, + Vec, + Vec, + ) = term.decode()?; + if term == transaction() { + let transparent_ptx_bundle = + BorshDeserialize::deserialize(&mut transparent_bytes.as_slice()) + .map_err(|_e| rustler::Error::Atom("Failure to decode"))?; + let signature = BorshDeserialize::deserialize(&mut sig_bytes.as_slice()) + .map_err(|_e| rustler::Error::Atom("Failure to decode"))?; + Ok(Transaction { + shielded_ptx_bundle, + signature, + transparent_ptx_bundle, + }) + } else { + Err(rustler::Error::BadArg) + } + } +} + impl ShieldedPartialTxBundle { pub fn new() -> Self { Self { @@ -344,7 +410,6 @@ pub mod testing { let rng = OsRng; - // Create shielded partial tx bundle let (shielded_ptx_bundle, r_vec) = create_shielded_ptx_bundle(2); // TODO: add transparent_ptx_bundle test let transparent_ptx_bundle = None; @@ -356,9 +421,12 @@ pub mod testing { ); let (shielded_ret, _) = tx.execute().unwrap(); - let borsh = tx.try_to_vec().unwrap(); - let de_tx: Transaction = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); - let (de_shielded_ret, _) = de_tx.execute().unwrap(); - assert_eq!(shielded_ret, de_shielded_ret); + #[cfg(feature = "borsh")] + { + let borsh = tx.try_to_vec().unwrap(); + let de_tx: Transaction = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); + let (de_shielded_ret, _) = de_tx.execute().unwrap(); + assert_eq!(shielded_ret, de_shielded_ret); + } } } diff --git a/taiga_halo2/src/transparent_ptx.rs b/taiga_halo2/src/transparent_ptx.rs index 2bac5a3a..0a6a88a2 100644 --- a/taiga_halo2/src/transparent_ptx.rs +++ b/taiga_halo2/src/transparent_ptx.rs @@ -2,10 +2,17 @@ use crate::{ error::TransactionError, executable::Executable, nullifier::Nullifier, value_commitment::ValueCommitment, }; -use borsh::{BorshDeserialize, BorshSerialize}; use pasta_curves::pallas; -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[cfg(feature = "serde")] +use serde; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TransparentPartialTransaction { pub inputs: Vec, pub outputs: Vec, @@ -34,7 +41,9 @@ impl Executable for TransparentPartialTransaction { } } -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InputResource { pub resource_logic: ResourceLogic, pub prefix: ContentHash, @@ -43,21 +52,31 @@ pub struct InputResource { pub resource_data_dynamic: ResourceDataDynamic, } -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct OutputResource { pub resource_logic: ResourceLogic, pub resource_data_static: ResourceDataStatic, pub resource_data_dynamic: ResourceDataDynamic, } -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ResourceLogic {} -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ContentHash {} -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ResourceDataStatic {} -#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ResourceDataDynamic {} diff --git a/taiga_halo2/src/value_commitment.rs b/taiga_halo2/src/value_commitment.rs index 1b7206b9..444746b0 100644 --- a/taiga_halo2/src/value_commitment.rs +++ b/taiga_halo2/src/value_commitment.rs @@ -4,9 +4,16 @@ use halo2_proofs::arithmetic::CurveAffine; use pasta_curves::group::cofactor::CofactorCurveAffine; use pasta_curves::group::{Curve, Group, GroupEncoding}; use pasta_curves::pallas; +#[cfg(feature = "nif")] +use rustler::NifTuple; use subtle::CtOption; +#[cfg(feature = "serde")] +use serde; + #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "nif", derive(NifTuple))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ValueCommitment(pallas::Point); impl ValueCommitment {