Skip to content

Commit

Permalink
transparent execution: verify shielded VPs transparently (#248)
Browse files Browse the repository at this point in the history
* move vp_bytecode out of borsh feature

* transparent vp execute

* check nf, cm, and owned_id in verify_transparently process

* fix typo
  • Loading branch information
XuyangSong authored Nov 17, 2023
1 parent d4f2ae0 commit cba5f58
Show file tree
Hide file tree
Showing 19 changed files with 333 additions and 150 deletions.
28 changes: 24 additions & 4 deletions taiga_halo2/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ pub struct ActionPublicInputs {
}

/// The information to build ActionPublicInputs and ActionCircuit.
#[derive(Clone)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
pub struct ActionInfo {
input_note: Note,
input_merkle_path: MerklePath,
Expand Down Expand Up @@ -157,17 +159,35 @@ impl ActionInfo {
self.rseed.get_vp_cm_r(PRF_EXPAND_OUTPUT_VP_CM_R)
}

// Only used in transparent scenario: the achor is untrusted, recalculate root when executing it transparently.
pub fn calculate_root(&self) -> Anchor {
self.input_note.calculate_root(&self.input_merkle_path)
}

// Get value commitment
pub fn get_value_commitment(&self, blind_r: &pallas::Scalar) -> ValueCommitment {
ValueCommitment::commit(&self.input_note, &self.output_note, blind_r)
}

pub fn get_input_note_nullifer(&self) -> Nullifier {
self.input_note.get_nf().unwrap()
}

pub fn get_output_note_cm(&self) -> NoteCommitment {
self.output_note.commitment()
}

pub fn build(&self) -> (ActionPublicInputs, ActionCircuit) {
let nf = self.input_note.get_nf().unwrap();
let nf = self.get_input_note_nullifer();
assert_eq!(
nf, self.output_note.rho,
"The nf of input note should be equal to the rho of output note"
);

let cm = self.output_note.commitment();
let cm = self.get_output_note_cm();

let rcv = self.get_rcv();
let cv_net = ValueCommitment::new(&self.input_note, &self.output_note, &rcv);
let cv_net = self.get_value_commitment(&rcv);

let input_vp_cm_r = self.get_input_vp_com_r();
let input_vp_commitment =
Expand Down
1 change: 0 additions & 1 deletion taiga_halo2/src/circuit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ pub mod hash_to_curve;
pub mod note_commitment;
pub mod note_encryption_circuit;
mod vamp_ir_utils;
#[cfg(feature = "borsh")]
pub mod vp_bytecode;
pub mod vp_examples;
129 changes: 114 additions & 15 deletions taiga_halo2/src/circuit/vp_bytecode.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
use crate::circuit::{
vp_circuit::{VPVerifyingInfo, ValidityPredicateVerifyingInfo, VampIRValidityPredicateCircuit},
vp_examples::TrivialValidityPredicateCircuit,
};
#[cfg(feature = "borsh")]
use crate::circuit::vp_examples::TrivialValidityPredicateCircuit;
use crate::error::TransactionError;
use crate::shielded_ptx::NoteVPVerifyingInfoSet;
use crate::{
circuit::vp_circuit::{
VPVerifyingInfo, ValidityPredicateVerifyingInfo, VampIRValidityPredicateCircuit,
},
constant::{
VP_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX, VP_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX,
VP_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX, VP_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX,
VP_CIRCUIT_OWNED_NOTE_PUB_ID_PUBLIC_INPUT_IDX,
},
note::NoteCommitment,
nullifier::Nullifier,
};

#[cfg(feature = "borsh")]
use borsh::{BorshDeserialize, BorshSerialize};
use pasta_curves::pallas;
#[cfg(feature = "serde")]
use serde;
use std::path::PathBuf;

#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ValidityPredicateRepresentation {
// vampir has a unified circuit representation.
Expand All @@ -19,14 +34,16 @@ pub enum ValidityPredicateRepresentation {
// TODO: add other vp types here if needed
}

#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ValidityPredicateByteCode {
circuit: ValidityPredicateRepresentation,
inputs: Vec<u8>,
}

#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ApplicationByteCode {
app_vp_bytecode: ValidityPredicateByteCode,
Expand All @@ -38,7 +55,7 @@ impl ValidityPredicateByteCode {
Self { circuit, inputs }
}

pub fn generate_proof(self) -> VPVerifyingInfo {
pub fn generate_proof(self) -> Result<VPVerifyingInfo, TransactionError> {
match self.circuit {
ValidityPredicateRepresentation::VampIR(circuit) => {
// TDDO: use the file_name api atm,
Expand All @@ -50,14 +67,74 @@ impl ValidityPredicateByteCode {
&vamp_ir_circuit_file,
&inputs_file,
);
vp_circuit.get_verifying_info()
Ok(vp_circuit.get_verifying_info())
}
#[cfg(feature = "borsh")]
ValidityPredicateRepresentation::Trivial => {
let vp = TrivialValidityPredicateCircuit::from_bytes(self.inputs);
vp.get_verifying_info()
let vp = TrivialValidityPredicateCircuit::from_bytes(&self.inputs);
Ok(vp.get_verifying_info())
}
#[allow(unreachable_patterns)]
_ => Err(TransactionError::InvalidValidityPredicateRepresentation),
}
}

// Verify vp circuit transparently and return owned note PubID for further checking
pub fn verify_transparently(
&self,
action_nfs: &[Nullifier],
action_cms: &[NoteCommitment],
) -> Result<pallas::Base, TransactionError> {
// check VP transparently
let public_inputs = match &self.circuit {
ValidityPredicateRepresentation::VampIR(circuit) => {
// TDDO: use the file_name api atm,
// request vamp_ir to provide a api to generate circuit from bytes.
let vamp_ir_circuit_file =
PathBuf::from(String::from_utf8_lossy(circuit).to_string());
let inputs_file = PathBuf::from(String::from_utf8_lossy(&self.inputs).to_string());
let vp_circuit = VampIRValidityPredicateCircuit::from_vamp_ir_file(
&vamp_ir_circuit_file,
&inputs_file,
);
vp_circuit.verify_transparently()?
}
#[cfg(feature = "borsh")]
ValidityPredicateRepresentation::Trivial => {
let vp = TrivialValidityPredicateCircuit::from_bytes(&self.inputs);
vp.verify_transparently()?
}
#[allow(unreachable_patterns)]
_ => return Err(TransactionError::InvalidValidityPredicateRepresentation),
};

// check nullifiers
// Check the vp actually uses the input notes from action circuits.
let vp_nfs = [
public_inputs.get_from_index(VP_CIRCUIT_NULLIFIER_ONE_PUBLIC_INPUT_IDX),
public_inputs.get_from_index(VP_CIRCUIT_NULLIFIER_TWO_PUBLIC_INPUT_IDX),
];

if !((action_nfs[0].inner() == vp_nfs[0] && action_nfs[1].inner() == vp_nfs[1])
|| (action_nfs[0].inner() == vp_nfs[1] && action_nfs[1].inner() == vp_nfs[0]))
{
return Err(TransactionError::InconsistentNullifier);
}

// check note_commitments
// Check the vp actually uses the output notes from action circuits.
let vp_cms = [
public_inputs.get_from_index(VP_CIRCUIT_OUTPUT_CM_ONE_PUBLIC_INPUT_IDX),
public_inputs.get_from_index(VP_CIRCUIT_OUTPUT_CM_TWO_PUBLIC_INPUT_IDX),
];
if !((action_cms[0].inner() == vp_cms[0] && action_cms[1].inner() == vp_cms[1])
|| (action_cms[0].inner() == vp_cms[1] && action_cms[1].inner() == vp_cms[0]))
{
return Err(TransactionError::InconsistentOutputNoteCommitment);
}

Ok(public_inputs.get_from_index(VP_CIRCUIT_OWNED_NOTE_PUB_ID_PUBLIC_INPUT_IDX))
}
}

impl ApplicationByteCode {
Expand All @@ -71,14 +148,36 @@ impl ApplicationByteCode {
}
}

pub fn generate_proofs(self) -> NoteVPVerifyingInfoSet {
let app_vp_verifying_info = self.app_vp_bytecode.generate_proof();
pub fn generate_proofs(self) -> Result<NoteVPVerifyingInfoSet, TransactionError> {
let app_vp_verifying_info = self.app_vp_bytecode.generate_proof()?;

let app_dynamic_vp_verifying_info = self
let app_dynamic_vp_verifying_info: Result<Vec<_>, _> = self
.dynamic_vp_bytecode
.into_iter()
.map(|bytecode| bytecode.generate_proof())
.collect();
NoteVPVerifyingInfoSet::new(app_vp_verifying_info, app_dynamic_vp_verifying_info)
Ok(NoteVPVerifyingInfoSet::new(
app_vp_verifying_info,
app_dynamic_vp_verifying_info?,
))
}

// Verify vp circuits transparently and return owned note PubID for further checking
pub fn verify_transparently(
&self,
action_nfs: &[Nullifier],
action_cms: &[NoteCommitment],
) -> Result<pallas::Base, TransactionError> {
let owned_note_id = self
.app_vp_bytecode
.verify_transparently(action_nfs, action_cms)?;
for dynamic_vp in self.dynamic_vp_bytecode.iter() {
let id = dynamic_vp.verify_transparently(action_nfs, action_cms)?;
// check: the app_vp and dynamic_vps belong to the note
if id != owned_note_id {
return Err(TransactionError::InconsistentOwnedNotePubID);
}
}
Ok(owned_note_id)
}
}
31 changes: 31 additions & 0 deletions taiga_halo2/src/circuit/vp_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::{
VP_CIRCUIT_OWNED_NOTE_PUB_ID_PUBLIC_INPUT_IDX, VP_CIRCUIT_PARAMS_SIZE,
VP_CIRCUIT_PUBLIC_INPUT_NUM,
},
error::TransactionError,
note::{Note, NoteCommitment, RandomSeed},
note_encryption::{NoteCiphertext, SecretKey},
proof::Proof,
Expand Down Expand Up @@ -425,6 +426,7 @@ impl ValidityPredicateConfig {

pub trait ValidityPredicateVerifyingInfo: DynClone {
fn get_verifying_info(&self) -> VPVerifyingInfo;
fn verify_transparently(&self) -> Result<ValidityPredicatePublicInputs, TransactionError>;
fn get_vp_vk(&self) -> ValidityPredicateVerifyingKey;
}

Expand Down Expand Up @@ -776,6 +778,19 @@ macro_rules! vp_verifying_info_impl {
}
}

fn verify_transparently(
&self,
) -> Result<ValidityPredicatePublicInputs, TransactionError> {
use halo2_proofs::dev::MockProver;
let mut rng = OsRng;
let public_inputs = self.get_public_inputs(&mut rng);
let prover =
MockProver::<pallas::Base>::run(15, self, vec![public_inputs.to_vec()])
.unwrap();
prover.verify().unwrap();
Ok(public_inputs)
}

fn get_vp_vk(&self) -> ValidityPredicateVerifyingKey {
let params = SETUP_PARAMS_MAP.get(&15).unwrap();
let vk = keygen_vk(params, self).expect("keygen_vk should not fail");
Expand Down Expand Up @@ -912,6 +927,22 @@ impl ValidityPredicateVerifyingInfo for VampIRValidityPredicateCircuit {
}
}

fn verify_transparently(&self) -> Result<ValidityPredicatePublicInputs, TransactionError> {
use halo2_proofs::dev::MockProver;
let mut rng = OsRng;
let mut public_inputs = self.public_inputs.clone();
let rseed = RandomSeed::random(&mut rng);
public_inputs.extend(ValidityPredicatePublicInputs::get_public_input_padding(
self.public_inputs.len(),
&rseed,
));
let prover =
MockProver::<pallas::Base>::run(15, &self.circuit, vec![public_inputs.to_vec()])
.unwrap();
prover.verify().unwrap();
Ok(ValidityPredicatePublicInputs::from(public_inputs))
}

fn get_vp_vk(&self) -> ValidityPredicateVerifyingKey {
let vk = keygen_vk(&self.params, &self.circuit).expect("keygen_vk should not fail");
ValidityPredicateVerifyingKey::from_vk(vk)
Expand Down
13 changes: 12 additions & 1 deletion taiga_halo2/src/circuit/vp_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
ValidityPredicatePublicInputs, ValidityPredicateVerifyingInfo,
},
constant::{NUM_NOTE, SETUP_PARAMS_MAP, VP_CIRCUIT_PARAMS_SIZE},
error::TransactionError,
note::{Note, RandomSeed},
proof::Proof,
vp_commitment::ValidityPredicateCommitment,
Expand Down Expand Up @@ -100,7 +101,7 @@ impl TrivialValidityPredicateCircuit {

// Only for test
#[cfg(feature = "borsh")]
pub fn from_bytes(bytes: Vec<u8>) -> Self {
pub fn from_bytes(bytes: &Vec<u8>) -> Self {
BorshDeserialize::deserialize(&mut bytes.as_ref()).unwrap()
}

Expand Down Expand Up @@ -232,6 +233,16 @@ impl ValidityPredicateVerifyingInfo for TrivialValidityPredicateCircuit {
}
}

fn verify_transparently(&self) -> Result<ValidityPredicatePublicInputs, TransactionError> {
use halo2_proofs::dev::MockProver;
let mut rng = OsRng;
let public_inputs = self.get_public_inputs(&mut rng);
let prover =
MockProver::<pallas::Base>::run(15, self, vec![public_inputs.to_vec()]).unwrap();
prover.verify().unwrap();
Ok(public_inputs)
}

fn get_vp_vk(&self) -> ValidityPredicateVerifyingKey {
TRIVIAL_VP_VK.clone()
}
Expand Down
1 change: 1 addition & 0 deletions taiga_halo2/src/circuit/vp_examples/cascade_intent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
},
},
constant::{NUM_NOTE, SETUP_PARAMS_MAP},
error::TransactionError,
note::{Note, RandomSeed},
nullifier::Nullifier,
proof::Proof,
Expand Down
1 change: 1 addition & 0 deletions taiga_halo2/src/circuit/vp_examples/field_addition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
},
},
constant::{NUM_NOTE, SETUP_PARAMS_MAP, VP_CIRCUIT_CUSTOM_PUBLIC_INPUT_BEGIN_IDX},
error::TransactionError,
note::{Note, RandomSeed},
proof::Proof,
vp_commitment::ValidityPredicateCommitment,
Expand Down
1 change: 1 addition & 0 deletions taiga_halo2/src/circuit/vp_examples/or_relation_intent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
vp_examples::token::{Token, TOKEN_VK},
},
constant::{NUM_NOTE, SETUP_PARAMS_MAP},
error::TransactionError,
note::{Note, RandomSeed},
nullifier::Nullifier,
proof::Proof,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
},
},
constant::{NUM_NOTE, SETUP_PARAMS_MAP},
error::TransactionError,
note::{Note, RandomSeed},
proof::Proof,
vp_commitment::ValidityPredicateCommitment,
Expand Down
1 change: 1 addition & 0 deletions taiga_halo2/src/circuit/vp_examples/receiver_vp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
vp_examples::signature_verification::COMPRESSED_TOKEN_AUTH_VK,
},
constant::{GENERATOR, NUM_NOTE, SETUP_PARAMS_MAP},
error::TransactionError,
note::{Note, RandomSeed},
note_encryption::{NoteCiphertext, NotePlaintext, SecretKey},
proof::Proof,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
},
},
constant::{TaigaFixedBasesFull, NUM_NOTE, SETUP_PARAMS_MAP},
error::TransactionError,
note::{Note, RandomSeed},
proof::Proof,
utils::{mod_r_p, poseidon_hash_n},
Expand Down
1 change: 1 addition & 0 deletions taiga_halo2/src/circuit/vp_examples/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
VP_CIRCUIT_FIRST_DYNAMIC_VP_CM_2, VP_CIRCUIT_SECOND_DYNAMIC_VP_CM_1,
VP_CIRCUIT_SECOND_DYNAMIC_VP_CM_2,
},
error::TransactionError,
note::{Note, NoteValidityPredicates, RandomSeed},
nullifier::Nullifier,
proof::Proof,
Expand Down
Loading

0 comments on commit cba5f58

Please sign in to comment.