From 79887986a994bd73cf669abcaf6a7c019cf31e72 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 28 Mar 2019 18:33:18 +0300 Subject: [PATCH] half stubbed join split verification --- chain/src/lib.rs | 2 +- verification/src/accept_transaction.rs | 26 ++++-- verification/src/error.rs | 2 + verification/src/sprout.rs | 114 +++++++++++++++++++++---- 4 files changed, 118 insertions(+), 26 deletions(-) diff --git a/chain/src/lib.rs b/chain/src/lib.rs index fd02afab..1abd4901 100644 --- a/chain/src/lib.rs +++ b/chain/src/lib.rs @@ -34,7 +34,7 @@ pub use transaction::{OVERWINTER_TX_VERSION_GROUP_ID, SAPLING_TX_VERSION_GROUP_I pub use block::Block; pub use block_header::BlockHeader; pub use solution::EquihashSolution; -pub use join_split::{JoinSplit, JoinSplitDescription}; +pub use join_split::{JoinSplit, JoinSplitDescription, JoinSplitProof}; pub use merkle_root::{merkle_root, merkle_node_hash}; pub use sapling::{Sapling, SaplingSpendDescription, SaplingOutputDescription}; pub use transaction::{Transaction, TransactionInput, TransactionOutput, OutPoint}; diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index ac274129..c794d9f9 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -55,7 +55,7 @@ impl<'a> TransactionAcceptor<'a> { overspent: TransactionOverspent::new(transaction, output_store), double_spent: TransactionDoubleSpend::new(transaction, output_store), eval: TransactionEval::new(transaction, output_store, consensus, verification_level, height, time, deployments), - join_split: JoinSplitVerification::new(transaction, nullifier_tracker), + join_split: JoinSplitVerification::new(consensus, transaction, nullifier_tracker), sapling: SaplingVerification::new( nullifier_tracker, consensus.sapling_spend_verifying_key, @@ -125,7 +125,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> { sigops: TransactionSigops::new(transaction, output_store, consensus, max_block_sigops, time), double_spent: TransactionDoubleSpend::new(transaction, output_store), eval: TransactionEval::new(transaction, output_store, consensus, VerificationLevel::Full, height, time, deployments), - join_split: JoinSplitVerification::new(transaction, nullifier_tracker), + join_split: JoinSplitVerification::new(consensus, transaction, nullifier_tracker), sapling: SaplingVerification::new( nullifier_tracker, consensus.sapling_spend_verifying_key, @@ -571,14 +571,26 @@ impl<'a> TransactionVersion<'a> { /// Check the joinsplit proof of the transaction pub struct JoinSplitProof<'a> { - _transaction: CanonTransaction<'a>, + transaction: CanonTransaction<'a>, + consensus_params: &'a ConsensusParams, } impl<'a> JoinSplitProof<'a> { - fn new(transaction: CanonTransaction<'a>) -> Self { JoinSplitProof { _transaction: transaction }} + fn new(transaction: CanonTransaction<'a>, consensus_params: &'a ConsensusParams) -> Self { + JoinSplitProof { + transaction: transaction, + consensus_params: consensus_params, + } + } fn check(&self) -> Result<(), TransactionError> { - // TODO: Zero-knowledge proof + use sprout; + + if let Some(ref join_split) = self.transaction.raw.join_split { + sprout::verify(&[0u8;32], &join_split, &self.consensus_params.joinsplit_verification_key) + .map_err(|e| TransactionError::InvalidJoinSplit(e.index()))?; + } + Ok(()) } } @@ -618,11 +630,11 @@ pub struct JoinSplitVerification<'a> { } impl<'a> JoinSplitVerification<'a> { - pub fn new(transaction: CanonTransaction<'a>, tracker: &'a NullifierTracker) + pub fn new(consensus_params: &'a ConsensusParams, transaction: CanonTransaction<'a>, tracker: &'a NullifierTracker) -> Self { JoinSplitVerification { - proof: JoinSplitProof::new(transaction), + proof: JoinSplitProof::new(transaction, consensus_params), nullifiers: JoinSplitNullifiers::new(tracker, transaction), } } diff --git a/verification/src/error.rs b/verification/src/error.rs index 365ce5fc..71b4d060 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -150,5 +150,7 @@ pub enum TransactionError { Expired, /// Transaction overwintered flag is invalid. InvalidOverwintered, + /// Invalid joinsplit statement + InvalidJoinSplit(usize), } diff --git a/verification/src/sprout.rs b/verification/src/sprout.rs index 11f2868f..0d450390 100644 --- a/verification/src/sprout.rs +++ b/verification/src/sprout.rs @@ -1,6 +1,36 @@ +#![allow(dead_code)] + +use chain::{JoinSplit, JoinSplitProof}; +use crypto::{BnU256, Pghr13Proof, pghr13_verify}; + +/// Join split verification error kind +pub enum ErrorKind { + /// Invalid join split zkp statement + InvalidProof, + /// Invalid raw bytes econding of proof + InvalidEncoding, +} + +/// Join split verification error +pub struct Error { + index: usize, + kind: ErrorKind, +} + +impl Error { + pub fn proof(idx: usize) -> Self { + Error { kind: ErrorKind::InvalidProof, index: idx } + } + + pub fn encoding(idx: usize) -> Self { + Error { kind: ErrorKind::InvalidEncoding, index: idx } + } + + pub fn index(&self) -> usize { self.index } +} // blake2 hash of (random_seed, nullifier[0], nullifier[1], pub_key_hash) with 'ZcashComputehSig' personal token -pub fn compute_hsig(random_seed: [u8; 32], nullifiers: [[u8; 32]; 2], pub_key_hash: [u8; 32]) -> [u8; 32] { +pub fn compute_hsig(random_seed: &[u8; 32], nullifiers: &[[u8; 32]; 2], pub_key_hash: &[u8; 32]) -> [u8; 32] { use crypto::blake2::Params; let res = Params::new() @@ -18,6 +48,50 @@ pub fn compute_hsig(random_seed: [u8; 32], nullifiers: [[u8; 32]; 2], pub_key_ha result } +pub fn verify( + root: &[u8; 32], + join_split: &JoinSplit, + verifying_key: &crypto::Pghr13VerifyingKey, +) -> Result<(), Error> +{ + let mut desc_index = 0; + for desc in join_split.descriptions.iter() { + + match desc.zkproof { + JoinSplitProof::PHGR(ref proof_raw) => { + let hsig = compute_hsig(&desc.random_seed, &desc.nullifiers, &join_split.pubkey.into()); + + let mut input = Input::new(1024); + input.push_hash(&root); + input.push_hash(&hsig); + + input.push_hash(&desc.nullifiers[0]); + input.push_hash(&desc.macs[0]); + + input.push_hash(&desc.nullifiers[1]); + input.push_hash(&desc.macs[1]); + + input.push_hash(&desc.commitments[0]); + input.push_hash(&desc.commitments[1]); + + input.push_u64(desc.value_pub_old); + input.push_u64(desc.value_pub_new); + + let proof = Pghr13Proof::from_raw(proof_raw).map_err(|_| Error::encoding(desc_index))?; + + if !pghr13_verify(verifying_key, &input.into_frs(), &proof) { + return Err(Error::proof(desc_index)); + } + }, + _ => continue, + } + + desc_index += 1; + } + + Ok(()) +} + #[derive(Debug, Clone)] pub struct Input { bits: bitvec::BitVec, @@ -28,12 +102,16 @@ impl Input { Input { bits: bitvec::BitVec::with_capacity(size) } } - fn push_u256(&mut self, val: crypto::BnU256) { + fn push_u256(&mut self, val: BnU256) { for i in 0..256 { - self.bits.push(val.get_bit(i).expect("for 0..256 index range will always return some; qeed")) + self.bits.push(val.get_bit(255-i).expect("for 0..256 index range will always return some; qeed")) } } + fn push_hash(&mut self, val: &[u8; 32]) { + self.push_u256(BnU256::from_slice(&val[..]).expect("has 32 elements in slice; qed")) + } + fn push_u64(&mut self, val: u64) { for i in 0..64 { self.bits.push(val & (1 << (63-i)) > 0) @@ -81,48 +159,48 @@ mod tests { fn test_vectors() { assert_eq!( compute_hsig( - hash("6161616161616161616161616161616161616161616161616161616161616161"), - [ + &hash("6161616161616161616161616161616161616161616161616161616161616161"), + &[ hash("6262626262626262626262626262626262626262626262626262626262626262"), hash("6363636363636363636363636363636363636363636363636363636363636363"), ], - hash("6464646464646464646464646464646464646464646464646464646464646464"), + &hash("6464646464646464646464646464646464646464646464646464646464646464"), ), hash("a8cba69f1fa329c055756b4af900f8a00b61e44f4cb8a1824ceb58b90a5b8113"), ); assert_eq!( compute_hsig( - hash("0000000000000000000000000000000000000000000000000000000000000000"), - [ + &hash("0000000000000000000000000000000000000000000000000000000000000000"), + &[ hash("0000000000000000000000000000000000000000000000000000000000000000"), hash("0000000000000000000000000000000000000000000000000000000000000000"), ], - hash("0000000000000000000000000000000000000000000000000000000000000000"), + &hash("0000000000000000000000000000000000000000000000000000000000000000"), ), hash("697322276b5dd93b12fb1fcbd2144b2960f24c73aac6c6a0811447be1e7f1e19"), ); assert_eq!( compute_hsig( - hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), - [ + &hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), + &[ hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), ], - hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), + &hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), ), hash("b61110ec162693bc3d9ca7fb0eec3afd2e278e2f41394b3ff11d7cb761ad4b27"), ); assert_eq!( compute_hsig( - hash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), - [ + &hash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &[ hash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), hash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), ], - hash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &hash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), ), hash("4961048919f0ca79d49c9378c36a91a8767060001f4212fe6f7d426f3ccf9f32"), ); @@ -130,12 +208,12 @@ mod tests { assert_eq!( compute_hsig( - hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), - [ + &hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), + &[ hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), ], - hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), + &hash("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"), ), hash("b61110ec162693bc3d9ca7fb0eec3afd2e278e2f41394b3ff11d7cb761ad4b27"), );