From d5687a558e48ff49eaf3914189b0598aa4ce7f27 Mon Sep 17 00:00:00 2001 From: Gary Yu Date: Thu, 20 Jun 2019 22:05:02 +0800 Subject: [PATCH 1/4] schnorr signature batch verification --- chain/src/txhashset/txhashset.rs | 33 ++++++++++++++++++++----------- core/src/core/transaction.rs | 34 +++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 83eba6967d..2bb162e52c 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -1323,6 +1323,7 @@ impl<'a> Extension<'a> { let mut kern_count = 0; let total_kernels = pmmr::n_leaves(self.kernel_pmmr.unpruned_size()); + let mut tx_kernels: Vec = Vec::with_capacity(1_024); for n in 1..self.kernel_pmmr.unpruned_size() + 1 { if pmmr::is_leaf(n) { let kernel = self @@ -1330,21 +1331,31 @@ impl<'a> Extension<'a> { .get_data(n) .ok_or::(ErrorKind::TxKernelNotFound.into())?; - kernel.verify()?; + tx_kernels.push(kernel.kernel); kern_count += 1; - if kern_count % 20 == 0 { + // batch on every 1024 kernels + if kern_count & 0x0400 == 0 { + TxKernel::batch_verify(&tx_kernels)?; + tx_kernels.clear(); status.on_validation(kern_count, total_kernels, 0, 0); - } - if kern_count % 1_000 == 0 { - debug!( - "txhashset: verify_kernel_signatures: verified {} signatures", - kern_count, - ); + if kern_count & 0x2000 == 0 { + debug!( + "txhashset: verify_kernel_signatures: verified {} signatures", + kern_count, + ); + } } } } + // remaining part which not full of 1024 kernels + if tx_kernels.len() > 0 { + TxKernel::batch_verify(&tx_kernels)?; + tx_kernels.clear(); + status.on_validation(kern_count, total_kernels, 0, 0); + } + debug!( "txhashset: verified {} kernel signatures, pmmr size {}, took {}s", kern_count, @@ -1358,8 +1369,8 @@ impl<'a> Extension<'a> { fn verify_rangeproofs(&self, status: &dyn TxHashsetWriteStatus) -> Result<(), Error> { let now = Instant::now(); - let mut commits: Vec = vec![]; - let mut proofs: Vec = vec![]; + let mut commits: Vec = Vec::with_capacity(1_000); + let mut proofs: Vec = Vec::with_capacity(1_000); let mut proof_count = 0; let total_rproofs = pmmr::n_leaves(self.output_pmmr.unpruned_size()); @@ -1390,7 +1401,7 @@ impl<'a> Extension<'a> { ); } - if proof_count % 20 == 0 { + if proof_count % 1_000 == 0 { status.on_validation(0, 0, proof_count, total_rproofs); } } diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 8fd4172c6c..a518f6f817 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -306,6 +306,34 @@ impl TxKernel { Ok(()) } + /// Batch verification. + pub fn batch_verify(tx_kernels: &Vec) -> Result<(), Error> { + let len = tx_kernels.len(); + let mut sigs: Vec = Vec::with_capacity(len); + let mut pubkeys: Vec = Vec::with_capacity(len); + let mut msgs: Vec = Vec::with_capacity(len); + + let secp = static_secp_instance(); + let secp = secp.lock(); + + for tx_kernel in tx_kernels { + if tx_kernel.is_coinbase() && tx_kernel.fee != 0 + || !tx_kernel.is_height_locked() && tx_kernel.lock_height != 0 + { + return Err(Error::InvalidKernelFeatures); + } + sigs.push(tx_kernel.excess_sig); + pubkeys.push(tx_kernel.excess.to_pubkey(&secp)?); + msgs.push(tx_kernel.msg_to_sign()?); + } + + if !secp::aggsig::verify_batch(&secp, &sigs, &msgs, &pubkeys) { + return Err(Error::IncorrectSignature); + } + + Ok(()) + } + /// Build an empty tx kernel with zero values. pub fn empty() -> TxKernel { TxKernel { @@ -753,11 +781,7 @@ impl TransactionBody { }; // Verify the unverified tx kernels. - // No ability to batch verify these right now - // so just do them individually. - for x in &kernels { - x.verify()?; - } + TxKernel::batch_verify(&kernels)?; // Cache the successful verification results for the new outputs and kernels. { From bb9e1b19a9b1cd56cad81be693af21456056b233 Mon Sep 17 00:00:00 2001 From: Gary Yu Date: Tue, 27 Aug 2019 18:40:13 +0800 Subject: [PATCH 2/4] Split the fee and lock_height verification out from the batch signature verification --- chain/src/txhashset/txhashset.rs | 28 ++++++++++------------------ core/src/core/transaction.rs | 24 ++++++++++++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 2bb162e52c..3bd4891a59 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -1320,10 +1320,11 @@ impl<'a> Extension<'a> { fn verify_kernel_signatures(&self, status: &dyn TxHashsetWriteStatus) -> Result<(), Error> { let now = Instant::now(); + const KERNEL_BATCH_SIZE: usize = 5_000; let mut kern_count = 0; let total_kernels = pmmr::n_leaves(self.kernel_pmmr.unpruned_size()); - let mut tx_kernels: Vec = Vec::with_capacity(1_024); + let mut tx_kernels: Vec = Vec::with_capacity(KERNEL_BATCH_SIZE); for n in 1..self.kernel_pmmr.unpruned_size() + 1 { if pmmr::is_leaf(n) { let kernel = self @@ -1332,30 +1333,21 @@ impl<'a> Extension<'a> { .ok_or::(ErrorKind::TxKernelNotFound.into())?; tx_kernels.push(kernel.kernel); - kern_count += 1; - // batch on every 1024 kernels - if kern_count & 0x0400 == 0 { - TxKernel::batch_verify(&tx_kernels)?; + if tx_kernels.len() >= KERNEL_BATCH_SIZE || n >= self.kernel_pmmr.unpruned_size() { + TxKernel::fee_height_verify(&tx_kernels)?; + TxKernel::batch_sig_verify(&tx_kernels)?; + kern_count += tx_kernels.len() as u64; tx_kernels.clear(); status.on_validation(kern_count, total_kernels, 0, 0); - if kern_count & 0x2000 == 0 { - debug!( - "txhashset: verify_kernel_signatures: verified {} signatures", - kern_count, - ); - } + debug!( + "txhashset: verify_kernel_signatures: verified {} signatures", + kern_count, + ); } } } - // remaining part which not full of 1024 kernels - if tx_kernels.len() > 0 { - TxKernel::batch_verify(&tx_kernels)?; - tx_kernels.clear(); - status.on_validation(kern_count, total_kernels, 0, 0); - } - debug!( "txhashset: verified {} kernel signatures, pmmr size {}, took {}s", kern_count, diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index a518f6f817..ead18cc331 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -306,8 +306,20 @@ impl TxKernel { Ok(()) } - /// Batch verification. - pub fn batch_verify(tx_kernels: &Vec) -> Result<(), Error> { + /// Fee and lock_height verification + pub fn fee_height_verify(tx_kernels: &Vec) -> Result<(), Error> { + for tx_kernel in tx_kernels { + if tx_kernel.is_coinbase() && tx_kernel.fee != 0 + || !tx_kernel.is_height_locked() && tx_kernel.lock_height != 0 + { + return Err(Error::InvalidKernelFeatures); + } + } + Ok(()) + } + + /// Batch signature verification. + pub fn batch_sig_verify(tx_kernels: &Vec) -> Result<(), Error> { let len = tx_kernels.len(); let mut sigs: Vec = Vec::with_capacity(len); let mut pubkeys: Vec = Vec::with_capacity(len); @@ -317,11 +329,6 @@ impl TxKernel { let secp = secp.lock(); for tx_kernel in tx_kernels { - if tx_kernel.is_coinbase() && tx_kernel.fee != 0 - || !tx_kernel.is_height_locked() && tx_kernel.lock_height != 0 - { - return Err(Error::InvalidKernelFeatures); - } sigs.push(tx_kernel.excess_sig); pubkeys.push(tx_kernel.excess.to_pubkey(&secp)?); msgs.push(tx_kernel.msg_to_sign()?); @@ -781,7 +788,8 @@ impl TransactionBody { }; // Verify the unverified tx kernels. - TxKernel::batch_verify(&kernels)?; + TxKernel::fee_height_verify(&kernels)?; + TxKernel::batch_sig_verify(&kernels)?; // Cache the successful verification results for the new outputs and kernels. { From 9d11b7d246d6a537b4fb033b6f644dce872badc2 Mon Sep 17 00:00:00 2001 From: Gary Yu Date: Tue, 27 Aug 2019 20:14:38 +0800 Subject: [PATCH 3/4] Remove the new added fee_height_verify to comply with #2859 --- chain/src/txhashset/txhashset.rs | 1 - core/src/core/transaction.rs | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 3f42f0a98b..7b7d0cd332 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -1354,7 +1354,6 @@ impl<'a> Extension<'a> { tx_kernels.push(kernel.kernel); if tx_kernels.len() >= KERNEL_BATCH_SIZE || n >= self.kernel_pmmr.unpruned_size() { - TxKernel::fee_height_verify(&tx_kernels)?; TxKernel::batch_sig_verify(&tx_kernels)?; kern_count += tx_kernels.len() as u64; tx_kernels.clear(); diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 2e575676c7..5a21b96161 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -382,18 +382,6 @@ impl TxKernel { Ok(()) } - /// Fee and lock_height verification - pub fn fee_height_verify(tx_kernels: &Vec) -> Result<(), Error> { - for tx_kernel in tx_kernels { - if tx_kernel.is_coinbase() && tx_kernel.fee != 0 - || !tx_kernel.is_height_locked() && tx_kernel.lock_height != 0 - { - return Err(Error::InvalidKernelFeatures); - } - } - Ok(()) - } - /// Batch signature verification. pub fn batch_sig_verify(tx_kernels: &Vec) -> Result<(), Error> { let len = tx_kernels.len(); @@ -887,7 +875,6 @@ impl TransactionBody { }; // Verify the unverified tx kernels. - TxKernel::fee_height_verify(&kernels)?; TxKernel::batch_sig_verify(&kernels)?; // Cache the successful verification results for the new outputs and kernels. From 2fbf1b458e24768939e08b3f4fa220aa7112264c Mon Sep 17 00:00:00 2001 From: Gary Yu Date: Tue, 27 Aug 2019 21:01:41 +0800 Subject: [PATCH 4/4] fix: the last n could not be leaf? --- chain/src/txhashset/txhashset.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 7b7d0cd332..6f2f440ad6 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -1352,17 +1352,17 @@ impl<'a> Extension<'a> { .ok_or::(ErrorKind::TxKernelNotFound.into())?; tx_kernels.push(kernel.kernel); + } - if tx_kernels.len() >= KERNEL_BATCH_SIZE || n >= self.kernel_pmmr.unpruned_size() { - TxKernel::batch_sig_verify(&tx_kernels)?; - kern_count += tx_kernels.len() as u64; - tx_kernels.clear(); - status.on_validation(kern_count, total_kernels, 0, 0); - debug!( - "txhashset: verify_kernel_signatures: verified {} signatures", - kern_count, - ); - } + if tx_kernels.len() >= KERNEL_BATCH_SIZE || n >= self.kernel_pmmr.unpruned_size() { + TxKernel::batch_sig_verify(&tx_kernels)?; + kern_count += tx_kernels.len() as u64; + tx_kernels.clear(); + status.on_validation(kern_count, total_kernels, 0, 0); + debug!( + "txhashset: verify_kernel_signatures: verified {} signatures", + kern_count, + ); } }