From 939d391e0e732f14ae455f809845f5303271a770 Mon Sep 17 00:00:00 2001 From: Antioch Peverell Date: Thu, 30 Aug 2018 15:44:34 +0100 Subject: [PATCH] cache rangeproof and kernel signature verification results (#1419) * wip - cache rangeproof verification * rustfmt * rustfmt * rename to ok_verifier * rustfmt * wire in caching verifier in more places skip rangeproof and kernel signature verification during deserialization * rustfmt * actually cache verification results via ok verifier * rustfmt * cleanup * pass a cache around, not a verifier * rustfmt * cleanup trait shenanigans * rustfmt * core tests passing * rustfmt * rustfmt * tests passing * rustfmt * verifier cache now takes vecs of data in and out * rustfmt * logging + rustfmt * add docs and comments --- Cargo.lock | 1 + chain/src/chain.rs | 9 +- chain/src/pipe.rs | 22 ++- chain/tests/data_file_integrity.rs | 7 +- chain/tests/mine_simple_chain.rs | 7 +- chain/tests/test_coinbase_maturity.rs | 6 +- core/Cargo.toml | 1 + core/src/core/block.rs | 19 ++- core/src/core/mod.rs | 1 + core/src/core/transaction.rs | 95 ++++++----- core/src/core/verifier_cache.rs | 116 ++++++++++++++ core/src/lib.rs | 1 + core/tests/block.rs | 26 ++- core/tests/core.rs | 217 +++++++++++++++----------- pool/src/pool.rs | 17 +- pool/src/transaction_pool.rs | 20 ++- pool/src/types.rs | 2 +- pool/tests/block_building.rs | 8 +- pool/tests/block_reconciliation.rs | 5 +- pool/tests/coinbase_maturity.rs | 22 +-- pool/tests/common/mod.rs | 7 +- pool/tests/transaction_pool.rs | 9 +- servers/src/common/adapters.rs | 10 +- servers/src/grin/dandelion_monitor.rs | 20 ++- servers/src/grin/server.rs | 21 ++- servers/src/grin/sync.rs | 4 +- servers/src/mining/mine_block.rs | 34 +++- servers/src/mining/stratumserver.rs | 13 +- servers/src/mining/test_miner.rs | 15 +- src/bin/tui/table.rs | 6 +- wallet/src/libtx/build.rs | 19 ++- wallet/src/libtx/slate.rs | 10 +- wallet/src/libwallet/internal/tx.rs | 6 +- wallet/tests/common/testclient.rs | 5 +- 34 files changed, 562 insertions(+), 219 deletions(-) create mode 100644 core/src/core/verifier_cache.rs diff --git a/Cargo.lock b/Cargo.lock index c72eadbea..d5e37d486 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,6 +649,7 @@ dependencies = [ "grin_util 0.3.0", "grin_wallet 0.3.0", "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 987f56d33..567cf9c5f 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -25,6 +25,7 @@ use lmdb; use core::core::hash::{Hash, Hashed}; use core::core::merkle_proof::MerkleProof; use core::core::target::Difficulty; +use core::core::verifier_cache::VerifierCache; use core::core::{Block, BlockHeader, Output, OutputIdentifier, Transaction, TxKernel}; use core::global; use error::{Error, ErrorKind}; @@ -136,7 +137,7 @@ pub struct Chain { txhashset: Arc>, // Recently processed blocks to avoid double-processing block_hashes_cache: Arc>>, - + verifier_cache: Arc>, // POW verification function pow_verifier: fn(&BlockHeader, u8) -> bool, } @@ -154,6 +155,7 @@ impl Chain { adapter: Arc, genesis: Block, pow_verifier: fn(&BlockHeader, u8) -> bool, + verifier_cache: Arc>, ) -> Result { let chain_store = store::ChainStore::new(db_env)?; @@ -182,7 +184,8 @@ impl Chain { head: Arc::new(Mutex::new(head)), orphans: Arc::new(OrphanBlockPool::new()), txhashset: Arc::new(RwLock::new(txhashset)), - pow_verifier: pow_verifier, + pow_verifier, + verifier_cache, block_hashes_cache: Arc::new(RwLock::new(VecDeque::with_capacity(HASHES_CACHE_SIZE))), }) } @@ -219,7 +222,7 @@ impl Chain { let bhash = b.hash(); let mut ctx = self.ctx_from_head(head, opts)?; - let res = pipe::process_block(&b, &mut ctx); + let res = pipe::process_block(&b, &mut ctx, self.verifier_cache.clone()); let add_to_hash_cache = || { // only add to hash cache below if block is definitively accepted diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index 20d031952..ebde26242 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -24,6 +24,7 @@ use chain::OrphanBlockPool; use core::consensus; use core::core::hash::{Hash, Hashed}; use core::core::target::Difficulty; +use core::core::verifier_cache::VerifierCache; use core::core::{Block, BlockHeader}; use core::global; use error::{Error, ErrorKind}; @@ -57,7 +58,11 @@ pub struct BlockContext { /// Runs the block processing pipeline, including validation and finding a /// place for the new block in the chain. Returns the new chain head if /// updated. -pub fn process_block(b: &Block, ctx: &mut BlockContext) -> Result, Error> { +pub fn process_block( + b: &Block, + ctx: &mut BlockContext, + verifier_cache: Arc>, +) -> Result, Error> { // TODO should just take a promise for a block with a full header so we don't // spend resources reading the full block when its header is invalid @@ -95,7 +100,7 @@ pub fn process_block(b: &Block, ctx: &mut BlockContext) -> Result, E // validate the block itself // we can do this now before interacting with the txhashset - let _sums = validate_block(b, ctx)?; + let _sums = validate_block(b, ctx, verifier_cache)?; // header and block both valid, and we have a previous block // so take the lock on the txhashset @@ -307,7 +312,11 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E Ok(()) } -fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> { +fn validate_block( + b: &Block, + ctx: &mut BlockContext, + verifier_cache: Arc>, +) -> Result<(), Error> { if ctx.store.block_exists(&b.hash())? { if b.header.height < ctx.head.height.saturating_sub(50) { return Err(ErrorKind::OldBlock.into()); @@ -316,8 +325,11 @@ fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> { } } let prev = ctx.store.get_block_header(&b.header.previous)?; - b.validate(&prev.total_kernel_offset, &prev.total_kernel_sum) - .map_err(|e| ErrorKind::InvalidBlockProof(e))?; + b.validate( + &prev.total_kernel_offset, + &prev.total_kernel_sum, + verifier_cache, + ).map_err(|e| ErrorKind::InvalidBlockProof(e))?; Ok(()) } diff --git a/chain/tests/data_file_integrity.rs b/chain/tests/data_file_integrity.rs index 545ab2faa..211aad4e2 100644 --- a/chain/tests/data_file_integrity.rs +++ b/chain/tests/data_file_integrity.rs @@ -24,11 +24,12 @@ extern crate rand; use chrono::Duration; use std::fs; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use chain::types::NoopAdapter; use chain::Chain; use core::core::target::Difficulty; +use core::core::verifier_cache::LruVerifierCache; use core::core::{Block, BlockHeader, Transaction}; use core::global::{self, ChainTypes}; use core::pow; @@ -45,6 +46,7 @@ fn setup(dir_name: &str) -> Chain { clean_output_dir(dir_name); global::set_mining_mode(ChainTypes::AutomatedTesting); let genesis_block = pow::mine_genesis_block().unwrap(); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); let db_env = Arc::new(store::new_env(dir_name.to_string())); chain::Chain::init( dir_name.to_string(), @@ -52,10 +54,12 @@ fn setup(dir_name: &str) -> Chain { Arc::new(NoopAdapter {}), genesis_block, pow::verify_size, + verifier_cache, ).unwrap() } fn reload_chain(dir_name: &str) -> Chain { + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); let db_env = Arc::new(store::new_env(dir_name.to_string())); chain::Chain::init( dir_name.to_string(), @@ -63,6 +67,7 @@ fn reload_chain(dir_name: &str) -> Chain { Arc::new(NoopAdapter {}), genesis::genesis_dev(), pow::verify_size, + verifier_cache, ).unwrap() } diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 0ba13fdad..80fc62cb2 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -23,12 +23,13 @@ extern crate rand; use chrono::Duration; use std::fs; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use chain::types::NoopAdapter; use chain::Chain; use core::core::hash::Hashed; use core::core::target::Difficulty; +use core::core::verifier_cache::LruVerifierCache; use core::core::{Block, BlockHeader, OutputFeatures, OutputIdentifier, Transaction}; use core::global::ChainTypes; use core::{consensus, global, pow}; @@ -42,6 +43,7 @@ fn clean_output_dir(dir_name: &str) { fn setup(dir_name: &str, genesis: Block) -> Chain { util::init_test_logger(); clean_output_dir(dir_name); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); let db_env = Arc::new(store::new_env(dir_name.to_string())); chain::Chain::init( dir_name.to_string(), @@ -49,6 +51,7 @@ fn setup(dir_name: &str, genesis: Block) -> Chain { Arc::new(NoopAdapter {}), genesis, pow::verify_size, + verifier_cache, ).unwrap() } @@ -486,12 +489,14 @@ fn actual_diff_iter_output() { global::set_mining_mode(ChainTypes::AutomatedTesting); let genesis_block = pow::mine_genesis_block().unwrap(); let db_env = Arc::new(store::new_env(".grin".to_string())); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); let chain = chain::Chain::init( "../.grin".to_string(), db_env, Arc::new(NoopAdapter {}), genesis_block, pow::verify_size, + verifier_cache, ).unwrap(); let iter = chain.difficulty_iter(); let mut last_time = 0; diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 97f205159..441e93dd0 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -23,12 +23,13 @@ extern crate rand; use chrono::Duration; use std::fs; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use chain::types::NoopAdapter; use chain::ErrorKind; use core::core::target::Difficulty; use core::core::transaction; +use core::core::verifier_cache::LruVerifierCache; use core::global::{self, ChainTypes}; use core::{consensus, pow}; use keychain::{ExtKeychain, Keychain}; @@ -46,6 +47,8 @@ fn test_coinbase_maturity() { let genesis_block = pow::mine_genesis_block().unwrap(); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + let db_env = Arc::new(store::new_env(".grin".to_string())); let chain = chain::Chain::init( ".grin".to_string(), @@ -53,6 +56,7 @@ fn test_coinbase_maturity() { Arc::new(NoopAdapter {}), genesis_block, pow::verify_size, + verifier_cache, ).unwrap(); let prev = chain.head_header().unwrap(); diff --git a/core/Cargo.toml b/core/Cargo.toml index e7aa2a9bc..87d39db50 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,6 +13,7 @@ croaring = "=0.3" failure = "0.1" failure_derive = "0.1" lazy_static = "1" +lru-cache = "0.1" num-bigint = "0.2" rand = "0.3" serde = "1" diff --git a/core/src/core/block.rs b/core/src/core/block.rs index ec0dc5267..2dbf91097 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -19,12 +19,14 @@ use chrono::prelude::{DateTime, NaiveDateTime, Utc}; use std::collections::HashSet; use std::fmt; use std::iter::FromIterator; +use std::sync::{Arc, RwLock}; use consensus::{self, reward, REWARD}; use core::committed::{self, Committed}; use core::compact_block::{CompactBlock, CompactBlockBody}; use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH}; use core::target::Difficulty; +use core::verifier_cache::{LruVerifierCache, VerifierCache}; use core::{ transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Proof, Transaction, TransactionBody, TxKernel, @@ -355,8 +357,15 @@ impl Block { difficulty: Difficulty, reward_output: (Output, TxKernel), ) -> Result { - let mut block = - Block::with_reward(prev, txs, reward_output.0, reward_output.1, difficulty)?; + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + let mut block = Block::with_reward( + prev, + txs, + reward_output.0, + reward_output.1, + difficulty, + verifier_cache, + )?; // Now set the pow on the header so block hashing works as expected. { @@ -430,11 +439,12 @@ impl Block { reward_out: Output, reward_kern: TxKernel, difficulty: Difficulty, + verifier: Arc>, ) -> Result { // A block is just a big transaction, aggregate as such. // Note that aggregation also runs transaction validation // and duplicate commitment checks. - let mut agg_tx = transaction::aggregate(txs)?; + let mut agg_tx = transaction::aggregate(txs, verifier)?; // Now add the reward output and reward kernel to the aggregate tx. // At this point the tx is technically invalid, // but the tx body is valid if we account for the reward (i.e. as a block). @@ -559,8 +569,9 @@ impl Block { &self, prev_kernel_offset: &BlindingFactor, prev_kernel_sum: &Commitment, + verifier: Arc>, ) -> Result<(Commitment), Error> { - self.body.validate(true)?; + self.body.validate(true, verifier)?; self.verify_kernel_lock_heights()?; self.verify_coinbase()?; diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 12c2daba8..da2582f44 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -23,6 +23,7 @@ pub mod merkle_proof; pub mod pmmr; pub mod target; pub mod transaction; +pub mod verifier_cache; use consensus::GRIN_BASE; #[allow(dead_code)] diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index d1cdf922d..32ce2fe66 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -17,10 +17,12 @@ use std::cmp::max; use std::cmp::Ordering; use std::collections::HashSet; +use std::sync::{Arc, RwLock}; use std::{error, fmt}; use consensus::{self, VerifySortOrder}; use core::hash::Hashed; +use core::verifier_cache::VerifierCache; use core::{committed, Committed}; use keychain::{self, BlindingFactor}; use ser::{self, read_multi, PMMRable, Readable, Reader, Writeable, Writer}; @@ -445,33 +447,6 @@ impl TransactionBody { .fold(0, |acc, ref x| max(acc, x.lock_height)) } - /// Verify the kernel signatures. - /// Note: this is expensive. - fn verify_kernel_signatures(&self) -> Result<(), Error> { - for x in &self.kernels { - x.verify()?; - } - Ok(()) - } - - /// Verify all the output rangeproofs. - /// Note: this is expensive. - fn verify_rangeproofs(&self) -> Result<(), Error> { - let mut commits: Vec = vec![]; - let mut proofs: Vec = vec![]; - if self.outputs.len() == 0 { - return Ok(()); - } - // unfortunately these have to be aligned in memory for the underlying - // libsecp call - for x in &self.outputs { - commits.push(x.commit.clone()); - proofs.push(x.proof.clone()); - } - Output::batch_verify_proofs(&commits, &proofs)?; - Ok(()) - } - // Verify the body is not too big in terms of number of inputs|outputs|kernels. fn verify_weight(&self, with_reward: bool) -> Result<(), Error> { // if as_block check the body as if it was a block, with an additional output and @@ -558,12 +533,51 @@ impl TransactionBody { /// Validates all relevant parts of a transaction body. Checks the /// excess value against the signature as well as range proofs for each /// output. - pub fn validate(&self, with_reward: bool) -> Result<(), Error> { + pub fn validate( + &self, + with_reward: bool, + verifier: Arc>, + ) -> Result<(), Error> { self.verify_weight(with_reward)?; self.verify_sorted()?; self.verify_cut_through()?; - self.verify_rangeproofs()?; - self.verify_kernel_signatures()?; + + // Find all the outputs that have not had their rangeproofs verified. + let outputs = { + let mut verifier = verifier.write().unwrap(); + verifier.filter_rangeproof_unverified(&self.outputs) + }; + + // Now batch verify all those unverified rangeproofs + if outputs.len() > 0 { + let mut commits = vec![]; + let mut proofs = vec![]; + for x in &outputs { + commits.push(x.commit); + proofs.push(x.proof); + } + Output::batch_verify_proofs(&commits, &proofs)?; + } + + // Find all the kernels that have not yet been verified. + let kernels = { + let mut verifier = verifier.write().unwrap(); + verifier.filter_kernel_sig_unverified(&self.kernels) + }; + + // Verify the unverified tx kernels. + // No ability to batch verify these right now + // so just do them individually. + for x in &kernels { + x.verify()?; + } + + // Cache the successful verification results for the new outputs and kernels. + { + let mut verifier = verifier.write().unwrap(); + verifier.add_rangeproof_verified(outputs); + verifier.add_kernel_sig_verified(kernels); + } Ok(()) } } @@ -757,8 +771,8 @@ impl Transaction { /// Validates all relevant parts of a fully built transaction. Checks the /// excess value against the signature as well as range proofs for each /// output. - pub fn validate(&self) -> Result<(), Error> { - self.body.validate(false)?; + pub fn validate(&self, verifier: Arc>) -> Result<(), Error> { + self.body.validate(false, verifier)?; self.body.verify_features()?; self.verify_kernel_sums(self.overage(), self.offset)?; Ok(()) @@ -808,7 +822,10 @@ pub fn cut_through(inputs: &mut Vec, outputs: &mut Vec) -> Result } /// Aggregate a vec of txs into a multi-kernel tx with cut_through. -pub fn aggregate(mut txs: Vec) -> Result { +pub fn aggregate( + mut txs: Vec, + verifier: Arc>, +) -> Result { // convenience short-circuiting if txs.is_empty() { return Ok(Transaction::empty()); @@ -854,14 +871,18 @@ pub fn aggregate(mut txs: Vec) -> Result { // The resulting tx could be invalid for a variety of reasons - // * tx too large (too many inputs|outputs|kernels) // * cut-through may have invalidated the sums - tx.validate()?; + tx.validate(verifier)?; Ok(tx) } /// Attempt to deaggregate a multi-kernel transaction based on multiple /// transactions -pub fn deaggregate(mk_tx: Transaction, txs: Vec) -> Result { +pub fn deaggregate( + mk_tx: Transaction, + txs: Vec, + verifier: Arc>, +) -> Result { let mut inputs: Vec = vec![]; let mut outputs: Vec = vec![]; let mut kernels: Vec = vec![]; @@ -870,7 +891,7 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec) -> Result) -> Result) -> Vec; + /// Takes a vec of tx outputs and returns those outputs + /// that have not yet had their rangeproofs verified. + fn filter_rangeproof_unverified(&mut self, outputs: &Vec) -> Vec; + /// Adds a vec of tx kernels to the cache (used in conjunction with the the filter above). + fn add_kernel_sig_verified(&mut self, kernels: Vec); + /// Adds a vec of outputs to the cache (used in conjunction with the the filter above). + fn add_rangeproof_verified(&mut self, outputs: Vec); +} + +/// An implementation of verifier_cache using lru_cache. +/// Caches tx kernels by kernel hash. +/// Caches outputs by output rangeproof hash (rangeproofs are committed to separately). +pub struct LruVerifierCache { + kernel_sig_verification_cache: LruCache, + rangeproof_verification_cache: LruCache, +} + +unsafe impl Sync for LruVerifierCache {} +unsafe impl Send for LruVerifierCache {} + +impl LruVerifierCache { + /// TODO how big should these caches be? + /// They need to be *at least* large enough to cover a maxed out block. + pub fn new() -> LruVerifierCache { + LruVerifierCache { + kernel_sig_verification_cache: LruCache::new(50_000), + rangeproof_verification_cache: LruCache::new(50_000), + } + } +} + +impl VerifierCache for LruVerifierCache { + fn filter_kernel_sig_unverified(&mut self, kernels: &Vec) -> Vec { + let res = kernels + .into_iter() + .filter(|x| { + !*self + .kernel_sig_verification_cache + .get_mut(&x.hash()) + .unwrap_or(&mut false) + }) + .cloned() + .collect::>(); + debug!( + LOGGER, + "lru_verifier_cache: kernel sigs: {}, not cached (must verify): {}", + kernels.len(), + res.len() + ); + res + } + + fn filter_rangeproof_unverified(&mut self, outputs: &Vec) -> Vec { + let res = outputs + .into_iter() + .filter(|x| { + !*self + .rangeproof_verification_cache + .get_mut(&x.proof.hash()) + .unwrap_or(&mut false) + }) + .cloned() + .collect::>(); + debug!( + LOGGER, + "lru_verifier_cache: rangeproofs: {}, not cached (must verify): {}", + outputs.len(), + res.len() + ); + res + } + + fn add_kernel_sig_verified(&mut self, kernels: Vec) { + for k in kernels { + self.kernel_sig_verification_cache.insert(k.hash(), true); + } + } + + fn add_rangeproof_verified(&mut self, outputs: Vec) { + for o in outputs { + self.rangeproof_verification_cache + .insert(o.proof.hash(), true); + } + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 62a9f5a34..c91b9e0b5 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,6 +30,7 @@ extern crate grin_keychain as keychain; extern crate grin_util as util; #[macro_use] extern crate lazy_static; +extern crate lru_cache; extern crate num_bigint as bigint; extern crate rand; extern crate serde; diff --git a/core/tests/block.rs b/core/tests/block.rs index 6893b7aa2..a9fb9b756 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -18,6 +18,9 @@ extern crate grin_keychain as keychain; extern crate grin_util as util; extern crate grin_wallet as wallet; +use std::sync::{Arc, RwLock}; +use std::time::Instant; + pub mod common; use chrono::Duration; @@ -26,14 +29,18 @@ use grin_core::consensus::{BLOCK_OUTPUT_WEIGHT, MAX_BLOCK_WEIGHT}; use grin_core::core::block::Error; use grin_core::core::hash::Hashed; use grin_core::core::id::ShortIdentifiable; +use grin_core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use grin_core::core::Committed; use grin_core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures}; use grin_core::{global, ser}; use keychain::{BlindingFactor, ExtKeychain, Keychain}; -use std::time::Instant; use util::{secp, secp_static}; use wallet::libtx::build::{self, input, output, with_fee}; +fn verifier_cache() -> Arc> { + Arc::new(RwLock::new(LruVerifierCache::new())) +} + // Too slow for now #[test] // TODO: make this fast enough or add similar but faster test? #[allow(dead_code)] @@ -61,7 +68,10 @@ fn too_large_block() { let prev = BlockHeader::default(); let key_id = keychain.derive_key_id(1).unwrap(); let b = new_block(vec![&tx], &keychain, &prev, &key_id); - assert!(b.validate(&BlindingFactor::zero(), &zero_commit).is_err()); + assert!( + b.validate(&BlindingFactor::zero(), &zero_commit, verifier_cache()) + .is_err() + ); } #[test] @@ -107,7 +117,8 @@ fn block_with_cut_through() { // block should have been automatically compacted (including reward // output) and should still be valid println!("3"); - b.validate(&BlindingFactor::zero(), &zero_commit).unwrap(); + b.validate(&BlindingFactor::zero(), &zero_commit, verifier_cache()) + .unwrap(); assert_eq!(b.inputs().len(), 3); assert_eq!(b.outputs().len(), 3); println!("4"); @@ -143,7 +154,10 @@ fn empty_block_with_coinbase_is_valid() { // the block should be valid here (single coinbase output with corresponding // txn kernel) - assert!(b.validate(&BlindingFactor::zero(), &zero_commit).is_ok()); + assert!( + b.validate(&BlindingFactor::zero(), &zero_commit, verifier_cache()) + .is_ok() + ); } #[test] @@ -172,7 +186,7 @@ fn remove_coinbase_output_flag() { .is_ok() ); assert_eq!( - b.validate(&BlindingFactor::zero(), &zero_commit), + b.validate(&BlindingFactor::zero(), &zero_commit, verifier_cache()), Err(Error::CoinbaseSumMismatch) ); } @@ -202,7 +216,7 @@ fn remove_coinbase_kernel_flag() { ); assert_eq!( - b.validate(&BlindingFactor::zero(), &zero_commit), + b.validate(&BlindingFactor::zero(), &zero_commit, verifier_cache()), Err(Error::Secp(secp::Error::IncorrectCommitSum)) ); } diff --git a/core/tests/core.rs b/core/tests/core.rs index e36ebd4d4..13bfae9d2 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -18,12 +18,15 @@ extern crate grin_keychain as keychain; extern crate grin_util as util; extern crate grin_wallet as wallet; +use std::sync::{Arc, RwLock}; + pub mod common; use common::{new_block, tx1i1o, tx1i2o, tx2i1o}; use grin_core::core::block::BlockHeader; use grin_core::core::block::Error::KernelLockHeight; use grin_core::core::hash::{Hashed, ZERO_HASH}; +use grin_core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use grin_core::core::{aggregate, deaggregate, KernelFeatures, Output, Transaction}; use grin_core::ser; use keychain::{BlindingFactor, ExtKeychain, Keychain}; @@ -87,6 +90,10 @@ fn test_zero_commit_fails() { ).unwrap(); } +fn verifier_cache() -> Arc> { + Arc::new(RwLock::new(LruVerifierCache::new())) +} + #[test] fn build_tx_kernel() { let keychain = ExtKeychain::from_random_seed().unwrap(); @@ -106,7 +113,7 @@ fn build_tx_kernel() { ).unwrap(); // check the tx is valid - tx.validate().unwrap(); + tx.validate(verifier_cache()).unwrap(); // check the kernel is also itself valid assert_eq!(tx.kernels().len(), 1); @@ -124,13 +131,15 @@ fn transaction_cut_through() { let tx1 = tx1i2o(); let tx2 = tx2i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); + assert!(tx1.validate(verifier_cache()).is_ok()); + assert!(tx2.validate(verifier_cache()).is_ok()); + + let vc = verifier_cache(); // now build a "cut_through" tx from tx1 and tx2 - let tx3 = aggregate(vec![tx1, tx2]).unwrap(); + let tx3 = aggregate(vec![tx1, tx2], vc.clone()).unwrap(); - assert!(tx3.validate().is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); } // Attempt to deaggregate a multi-kernel transaction in a different way @@ -141,26 +150,31 @@ fn multi_kernel_transaction_deaggregation() { let tx3 = tx1i1o(); let tx4 = tx1i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); - assert!(tx3.validate().is_ok()); - assert!(tx4.validate().is_ok()); + let vc = verifier_cache(); - let tx1234 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]).unwrap(); - let tx12 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap(); - let tx34 = aggregate(vec![tx3.clone(), tx4.clone()]).unwrap(); + assert!(tx1.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); + assert!(tx4.validate(vc.clone()).is_ok()); - assert!(tx1234.validate().is_ok()); - assert!(tx12.validate().is_ok()); - assert!(tx34.validate().is_ok()); + let tx1234 = aggregate( + vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()], + vc.clone(), + ).unwrap(); + let tx12 = aggregate(vec![tx1.clone(), tx2.clone()], vc.clone()).unwrap(); + let tx34 = aggregate(vec![tx3.clone(), tx4.clone()], vc.clone()).unwrap(); - let deaggregated_tx34 = deaggregate(tx1234.clone(), vec![tx12.clone()]).unwrap(); - assert!(deaggregated_tx34.validate().is_ok()); + assert!(tx1234.validate(vc.clone()).is_ok()); + assert!(tx12.validate(vc.clone()).is_ok()); + assert!(tx34.validate(vc.clone()).is_ok()); + + let deaggregated_tx34 = deaggregate(tx1234.clone(), vec![tx12.clone()], vc.clone()).unwrap(); + assert!(deaggregated_tx34.validate(vc.clone()).is_ok()); assert_eq!(tx34, deaggregated_tx34); - let deaggregated_tx12 = deaggregate(tx1234.clone(), vec![tx34.clone()]).unwrap(); + let deaggregated_tx12 = deaggregate(tx1234.clone(), vec![tx34.clone()], vc.clone()).unwrap(); - assert!(deaggregated_tx12.validate().is_ok()); + assert!(deaggregated_tx12.validate(vc.clone()).is_ok()); assert_eq!(tx12, deaggregated_tx12); } @@ -170,18 +184,20 @@ fn multi_kernel_transaction_deaggregation_2() { let tx2 = tx1i1o(); let tx3 = tx1i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); - assert!(tx3.validate().is_ok()); + let vc = verifier_cache(); - let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()]).unwrap(); - let tx12 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap(); + assert!(tx1.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); - assert!(tx123.validate().is_ok()); - assert!(tx12.validate().is_ok()); + let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()], vc.clone()).unwrap(); + let tx12 = aggregate(vec![tx1.clone(), tx2.clone()], vc.clone()).unwrap(); - let deaggregated_tx3 = deaggregate(tx123.clone(), vec![tx12.clone()]).unwrap(); - assert!(deaggregated_tx3.validate().is_ok()); + assert!(tx123.validate(vc.clone()).is_ok()); + assert!(tx12.validate(vc.clone()).is_ok()); + + let deaggregated_tx3 = deaggregate(tx123.clone(), vec![tx12.clone()], vc.clone()).unwrap(); + assert!(deaggregated_tx3.validate(vc.clone()).is_ok()); assert_eq!(tx3, deaggregated_tx3); } @@ -191,19 +207,21 @@ fn multi_kernel_transaction_deaggregation_3() { let tx2 = tx1i1o(); let tx3 = tx1i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); - assert!(tx3.validate().is_ok()); + let vc = verifier_cache(); - let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()]).unwrap(); - let tx13 = aggregate(vec![tx1.clone(), tx3.clone()]).unwrap(); - let tx2 = aggregate(vec![tx2.clone()]).unwrap(); + assert!(tx1.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); - assert!(tx123.validate().is_ok()); - assert!(tx2.validate().is_ok()); + let tx123 = aggregate(vec![tx1.clone(), tx2.clone(), tx3.clone()], vc.clone()).unwrap(); + let tx13 = aggregate(vec![tx1.clone(), tx3.clone()], vc.clone()).unwrap(); + let tx2 = aggregate(vec![tx2.clone()], vc.clone()).unwrap(); - let deaggregated_tx13 = deaggregate(tx123.clone(), vec![tx2.clone()]).unwrap(); - assert!(deaggregated_tx13.validate().is_ok()); + assert!(tx123.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); + + let deaggregated_tx13 = deaggregate(tx123.clone(), vec![tx2.clone()], vc.clone()).unwrap(); + assert!(deaggregated_tx13.validate(vc.clone()).is_ok()); assert_eq!(tx13, deaggregated_tx13); } @@ -215,26 +233,32 @@ fn multi_kernel_transaction_deaggregation_4() { let tx4 = tx1i1o(); let tx5 = tx1i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); - assert!(tx3.validate().is_ok()); - assert!(tx4.validate().is_ok()); - assert!(tx5.validate().is_ok()); + let vc = verifier_cache(); - let tx12345 = aggregate(vec![ - tx1.clone(), - tx2.clone(), - tx3.clone(), - tx4.clone(), - tx5.clone(), - ]).unwrap(); - assert!(tx12345.validate().is_ok()); + assert!(tx1.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); + assert!(tx4.validate(vc.clone()).is_ok()); + assert!(tx5.validate(vc.clone()).is_ok()); + + let tx12345 = aggregate( + vec![ + tx1.clone(), + tx2.clone(), + tx3.clone(), + tx4.clone(), + tx5.clone(), + ], + vc.clone(), + ).unwrap(); + assert!(tx12345.validate(vc.clone()).is_ok()); let deaggregated_tx5 = deaggregate( tx12345.clone(), vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()], + vc.clone(), ).unwrap(); - assert!(deaggregated_tx5.validate().is_ok()); + assert!(deaggregated_tx5.validate(vc.clone()).is_ok()); assert_eq!(tx5, deaggregated_tx5); } @@ -246,26 +270,35 @@ fn multi_kernel_transaction_deaggregation_5() { let tx4 = tx1i1o(); let tx5 = tx1i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); - assert!(tx3.validate().is_ok()); - assert!(tx4.validate().is_ok()); - assert!(tx5.validate().is_ok()); + let vc = verifier_cache(); - let tx12345 = aggregate(vec![ - tx1.clone(), - tx2.clone(), - tx3.clone(), - tx4.clone(), - tx5.clone(), - ]).unwrap(); - let tx12 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap(); - let tx34 = aggregate(vec![tx3.clone(), tx4.clone()]).unwrap(); + assert!(tx1.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); + assert!(tx4.validate(vc.clone()).is_ok()); + assert!(tx5.validate(vc.clone()).is_ok()); - assert!(tx12345.validate().is_ok()); + let tx12345 = aggregate( + vec![ + tx1.clone(), + tx2.clone(), + tx3.clone(), + tx4.clone(), + tx5.clone(), + ], + vc.clone(), + ).unwrap(); + let tx12 = aggregate(vec![tx1.clone(), tx2.clone()], vc.clone()).unwrap(); + let tx34 = aggregate(vec![tx3.clone(), tx4.clone()], vc.clone()).unwrap(); - let deaggregated_tx5 = deaggregate(tx12345.clone(), vec![tx12.clone(), tx34.clone()]).unwrap(); - assert!(deaggregated_tx5.validate().is_ok()); + assert!(tx12345.validate(vc.clone()).is_ok()); + + let deaggregated_tx5 = deaggregate( + tx12345.clone(), + vec![tx12.clone(), tx34.clone()], + vc.clone(), + ).unwrap(); + assert!(deaggregated_tx5.validate(vc.clone()).is_ok()); assert_eq!(tx5, deaggregated_tx5); } @@ -275,22 +308,24 @@ fn basic_transaction_deaggregation() { let tx1 = tx1i2o(); let tx2 = tx2i1o(); - assert!(tx1.validate().is_ok()); - assert!(tx2.validate().is_ok()); + let vc = verifier_cache(); + + assert!(tx1.validate(vc.clone()).is_ok()); + assert!(tx2.validate(vc.clone()).is_ok()); // now build a "cut_through" tx from tx1 and tx2 - let tx3 = aggregate(vec![tx1.clone(), tx2.clone()]).unwrap(); + let tx3 = aggregate(vec![tx1.clone(), tx2.clone()], vc.clone()).unwrap(); - assert!(tx3.validate().is_ok()); + assert!(tx3.validate(vc.clone()).is_ok()); - let deaggregated_tx1 = deaggregate(tx3.clone(), vec![tx2.clone()]).unwrap(); + let deaggregated_tx1 = deaggregate(tx3.clone(), vec![tx2.clone()], vc.clone()).unwrap(); - assert!(deaggregated_tx1.validate().is_ok()); + assert!(deaggregated_tx1.validate(vc.clone()).is_ok()); assert_eq!(tx1, deaggregated_tx1); - let deaggregated_tx2 = deaggregate(tx3.clone(), vec![tx1.clone()]).unwrap(); + let deaggregated_tx2 = deaggregate(tx3.clone(), vec![tx1.clone()], vc.clone()).unwrap(); - assert!(deaggregated_tx2.validate().is_ok()); + assert!(deaggregated_tx2.validate(vc.clone()).is_ok()); assert_eq!(tx2, deaggregated_tx2); } @@ -320,7 +355,7 @@ fn hash_output() { #[test] fn blind_tx() { let btx = tx2i1o(); - assert!(btx.validate().is_ok()); + assert!(btx.validate(verifier_cache()).is_ok()); // Ignored for bullet proofs, because calling range_proof_info // with a bullet proof causes painful errors @@ -382,7 +417,7 @@ fn tx_build_exchange() { &keychain, ).unwrap(); - tx_final.validate().unwrap(); + tx_final.validate(verifier_cache()).unwrap(); } #[test] @@ -398,7 +433,7 @@ fn reward_empty_block() { b.cut_through() .unwrap() - .validate(&BlindingFactor::zero(), &zero_commit) + .validate(&BlindingFactor::zero(), &zero_commit, verifier_cache()) .unwrap(); } @@ -407,10 +442,12 @@ fn reward_with_tx_block() { let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); + let vc = verifier_cache(); + let zero_commit = secp_static::commit_to_zero_value(); let mut tx1 = tx2i1o(); - tx1.validate().unwrap(); + tx1.validate(vc.clone()).unwrap(); let previous_header = BlockHeader::default(); @@ -418,7 +455,7 @@ fn reward_with_tx_block() { block .cut_through() .unwrap() - .validate(&BlindingFactor::zero(), &zero_commit) + .validate(&BlindingFactor::zero(), &zero_commit, vc.clone()) .unwrap(); } @@ -427,6 +464,8 @@ fn simple_block() { let keychain = keychain::ExtKeychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); + let vc = verifier_cache(); + let zero_commit = secp_static::commit_to_zero_value(); let mut tx1 = tx2i1o(); @@ -440,7 +479,8 @@ fn simple_block() { &key_id, ); - b.validate(&BlindingFactor::zero(), &zero_commit).unwrap(); + b.validate(&BlindingFactor::zero(), &zero_commit, vc.clone()) + .unwrap(); } #[test] @@ -451,6 +491,8 @@ fn test_block_with_timelocked_tx() { let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); + let vc = verifier_cache(); + let zero_commit = secp_static::commit_to_zero_value(); // first check we can add a timelocked tx where lock height matches current @@ -468,7 +510,8 @@ fn test_block_with_timelocked_tx() { let previous_header = BlockHeader::default(); let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone()); - b.validate(&BlindingFactor::zero(), &zero_commit).unwrap(); + b.validate(&BlindingFactor::zero(), &zero_commit, vc.clone()) + .unwrap(); // now try adding a timelocked tx where lock height is greater than current // block height @@ -485,7 +528,7 @@ fn test_block_with_timelocked_tx() { let previous_header = BlockHeader::default(); let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone()); - match b.validate(&BlindingFactor::zero(), &zero_commit) { + match b.validate(&BlindingFactor::zero(), &zero_commit, vc.clone()) { Err(KernelLockHeight(height)) => { assert_eq!(height, 2); } @@ -496,11 +539,11 @@ fn test_block_with_timelocked_tx() { #[test] pub fn test_verify_1i1o_sig() { let tx = tx1i1o(); - tx.validate().unwrap(); + tx.validate(verifier_cache()).unwrap(); } #[test] pub fn test_verify_2i1o_sig() { let tx = tx2i1o(); - tx.validate().unwrap(); + tx.validate(verifier_cache()).unwrap(); } diff --git a/pool/src/pool.rs b/pool/src/pool.rs index d306fad86..b4bcb9b6f 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -16,12 +16,13 @@ //! Used for both the txpool and stempool layers in the pool. use std::collections::{HashMap, HashSet}; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use core::consensus; use core::core::hash::{Hash, Hashed}; use core::core::id::ShortIdentifiable; use core::core::transaction; +use core::core::verifier_cache::VerifierCache; use core::core::{Block, CompactBlock, Transaction, TxKernel}; use types::{BlockChain, PoolEntry, PoolEntryState, PoolError}; use util::LOGGER; @@ -38,14 +39,20 @@ pub struct Pool { pub entries: Vec, /// The blockchain pub blockchain: Arc, + pub verifier_cache: Arc>, pub name: String, } impl Pool { - pub fn new(chain: Arc, name: String) -> Pool { + pub fn new( + chain: Arc, + verifier_cache: Arc>, + name: String, + ) -> Pool { Pool { entries: vec![], blockchain: chain.clone(), + verifier_cache: verifier_cache.clone(), name, } } @@ -86,7 +93,7 @@ impl Pool { .into_iter() .filter_map(|mut bucket| { bucket.truncate(MAX_TX_CHAIN); - transaction::aggregate(bucket).ok() + transaction::aggregate(bucket, self.verifier_cache.clone()).ok() }) .collect(); @@ -118,7 +125,7 @@ impl Pool { return Ok(None); } - let tx = transaction::aggregate(txs)?; + let tx = transaction::aggregate(txs, self.verifier_cache.clone())?; Ok(Some(tx)) } @@ -191,7 +198,7 @@ impl Pool { // Create a single aggregated tx from the existing pool txs and the // new entry txs.push(entry.tx.clone()); - transaction::aggregate(txs)? + transaction::aggregate(txs, self.verifier_cache.clone())? }; // Validate aggregated tx against a known chain state (via txhashset diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index a870bf7e6..4ff236200 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -17,10 +17,12 @@ //! resulting tx pool can be added to the current chain state to produce a //! valid chain state. +use std::sync::{Arc, RwLock}; + use chrono::prelude::Utc; -use std::sync::Arc; use core::core::hash::Hash; +use core::core::verifier_cache::VerifierCache; use core::core::{transaction, Block, CompactBlock, Transaction}; use pool::Pool; use types::{BlockChain, PoolAdapter, PoolConfig, PoolEntry, PoolEntryState, PoolError, TxSource}; @@ -35,6 +37,7 @@ pub struct TransactionPool { pub stempool: Pool, /// The blockchain pub blockchain: Arc, + pub verifier_cache: Arc>, /// The pool adapter pub adapter: Arc, } @@ -44,14 +47,16 @@ impl TransactionPool { pub fn new( config: PoolConfig, chain: Arc, + verifier_cache: Arc>, adapter: Arc, ) -> TransactionPool { TransactionPool { - config: config, - txpool: Pool::new(chain.clone(), format!("txpool")), - stempool: Pool::new(chain.clone(), format!("stempool")), + config, + txpool: Pool::new(chain.clone(), verifier_cache.clone(), format!("txpool")), + stempool: Pool::new(chain.clone(), verifier_cache.clone(), format!("stempool")), blockchain: chain, - adapter: adapter, + verifier_cache, + adapter, } } @@ -72,7 +77,7 @@ impl TransactionPool { .txpool .find_matching_transactions(entry.tx.kernels().clone()); if !txs.is_empty() { - entry.tx = transaction::deaggregate(entry.tx, txs)?; + entry.tx = transaction::deaggregate(entry.tx, txs, self.verifier_cache.clone())?; entry.src.debug_name = "deagg".to_string(); } } @@ -100,7 +105,8 @@ impl TransactionPool { self.is_acceptable(&tx)?; // Make sure the transaction is valid before anything else. - tx.validate().map_err(|e| PoolError::InvalidTx(e))?; + tx.validate(self.verifier_cache.clone()) + .map_err(|e| PoolError::InvalidTx(e))?; // Check the tx lock_time is valid based on current chain state. self.blockchain.verify_tx_lock_height(&tx)?; diff --git a/pool/src/types.rs b/pool/src/types.rs index 715123087..b17abb46d 100644 --- a/pool/src/types.rs +++ b/pool/src/types.rs @@ -18,9 +18,9 @@ use chrono::prelude::{DateTime, Utc}; use core::consensus; -use core::core::block::BlockHeader; use core::core::hash::Hash; use core::core::transaction::{self, Transaction}; +use core::core::BlockHeader; /// Dandelion relay timer const DANDELION_RELAY_SECS: u64 = 600; diff --git a/pool/tests/block_building.rs b/pool/tests/block_building.rs index 24dac6158..0c0fc90b5 100644 --- a/pool/tests/block_building.rs +++ b/pool/tests/block_building.rs @@ -33,6 +33,7 @@ use chain::txhashset; use chain::types::Tip; use core::core::hash::Hashed; use core::core::target::Difficulty; +use core::core::verifier_cache::LruVerifierCache; use keychain::{ExtKeychain, Keychain}; use wallet::libtx; @@ -48,12 +49,15 @@ fn test_transaction_pool_block_building() { clean_output_dir(db_root.clone()); let chain = ChainAdapter::init(db_root.clone()).unwrap(); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + // Initialize the chain/txhashset with an initial block // so we have a non-empty UTXO set. let add_block = |height, txs| { let key_id = keychain.derive_key_id(height as u32).unwrap(); let reward = libtx::reward::output(&keychain, &key_id, 0, height).unwrap(); - let mut block = Block::new(&BlockHeader::default(), txs, Difficulty::one(), reward).unwrap(); + let mut block = + Block::new(&BlockHeader::default(), txs, Difficulty::one(), reward).unwrap(); let mut txhashset = chain.txhashset.write().unwrap(); let mut batch = chain.store.batch().unwrap(); @@ -82,7 +86,7 @@ fn test_transaction_pool_block_building() { let header = add_block(1, vec![]); // Initialize a new pool with our chain adapter. - let pool = RwLock::new(test_setup(&Arc::new(chain.clone()))); + let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache)); // Now create tx to spend that first coinbase (now matured). // Provides us with some useful outputs to test with. diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index b06d3c562..e02792a88 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -37,6 +37,7 @@ use common::{ test_transaction_spending_coinbase, ChainAdapter, }; use core::core::target::Difficulty; +use core::core::verifier_cache::LruVerifierCache; use keychain::{ExtKeychain, Keychain}; use wallet::libtx; @@ -48,6 +49,8 @@ fn test_transaction_pool_block_reconciliation() { clean_output_dir(db_root.clone()); let chain = ChainAdapter::init(db_root.clone()).unwrap(); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + // Initialize the chain/txhashset with an initial block // so we have a non-empty UTXO set. let header = { @@ -83,7 +86,7 @@ fn test_transaction_pool_block_reconciliation() { }; // Initialize a new pool with our chain adapter. - let pool = RwLock::new(test_setup(&Arc::new(chain.clone()))); + let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache.clone())); // Now create tx to spend that first coinbase (now matured). // Provides us with some useful outputs to test with. diff --git a/pool/tests/coinbase_maturity.rs b/pool/tests/coinbase_maturity.rs index 88aa856f6..ccc1cd2ac 100644 --- a/pool/tests/coinbase_maturity.rs +++ b/pool/tests/coinbase_maturity.rs @@ -27,23 +27,12 @@ pub mod common; use std::sync::{Arc, RwLock}; -use common::{test_source, test_transaction}; +use common::{test_setup, test_source, test_transaction}; use core::core::hash::Hash; +use core::core::verifier_cache::LruVerifierCache; use core::core::{BlockHeader, Transaction}; use keychain::{ExtKeychain, Keychain}; -use pool::types::{BlockChain, NoopAdapter, PoolConfig, PoolError}; -use pool::TransactionPool; - -pub fn test_setup(chain: CoinbaseMaturityErrorChainAdapter) -> TransactionPool { - TransactionPool::new( - PoolConfig { - accept_fee_base: 0, - max_pool_size: 50, - }, - Arc::new(chain.clone()), - Arc::new(NoopAdapter {}), - ) -} +use pool::types::{BlockChain, PoolError}; #[derive(Clone)] pub struct CoinbaseMaturityErrorChainAdapter {} @@ -86,8 +75,9 @@ fn test_coinbase_maturity() { // Mocking this up with an adapter that will raise an error for coinbase // maturity. - let chain = CoinbaseMaturityErrorChainAdapter::new(); - let pool = RwLock::new(test_setup(chain)); + let chain = Arc::new(CoinbaseMaturityErrorChainAdapter::new()); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + let pool = RwLock::new(test_setup(chain, verifier_cache)); { let mut write_pool = pool.write().unwrap(); diff --git a/pool/tests/common/mod.rs b/pool/tests/common/mod.rs index 0ed6b6466..19b29cdd3 100644 --- a/pool/tests/common/mod.rs +++ b/pool/tests/common/mod.rs @@ -30,6 +30,7 @@ use std::fs; use std::sync::{Arc, RwLock}; use core::core::hash::Hash; +use core::core::verifier_cache::VerifierCache; use core::core::{BlockHeader, Transaction}; use chain::store::ChainStore; @@ -105,13 +106,17 @@ impl BlockChain for ChainAdapter { } } -pub fn test_setup(chain: &Arc) -> TransactionPool { +pub fn test_setup( + chain: Arc, + verifier_cache: Arc>, +) -> TransactionPool { TransactionPool::new( PoolConfig { accept_fee_base: 0, max_pool_size: 50, }, chain.clone(), + verifier_cache.clone(), Arc::new(NoopAdapter {}), ) } diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index d4a93258c..1d64143c9 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -35,6 +35,7 @@ use common::{ }; use core::core::hash::Hashed; use core::core::target::Difficulty; +use core::core::verifier_cache::LruVerifierCache; use core::core::{transaction, Block, BlockHeader}; use keychain::{ExtKeychain, Keychain}; use wallet::libtx; @@ -48,6 +49,8 @@ fn test_the_transaction_pool() { clean_output_dir(db_root.clone()); let chain = ChainAdapter::init(db_root.clone()).unwrap(); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + // Initialize the chain/txhashset with a few blocks, // so we have a non-empty UTXO set. let header = { @@ -83,7 +86,7 @@ fn test_the_transaction_pool() { }; // Initialize a new pool with our chain adapter. - let pool = RwLock::new(test_setup(&Arc::new(chain.clone()))); + let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache.clone())); // Now create tx to spend a coinbase, giving us some useful outputs for testing // with. @@ -219,7 +222,9 @@ fn test_the_transaction_pool() { let tx4 = test_transaction(&keychain, vec![800], vec![799]); // tx1 and tx2 are already in the txpool (in aggregated form) // tx4 is the "new" part of this aggregated tx that we care about - let agg_tx = transaction::aggregate(vec![tx1.clone(), tx2.clone(), tx4]).unwrap(); + let agg_tx = + transaction::aggregate(vec![tx1.clone(), tx2.clone(), tx4], verifier_cache.clone()) + .unwrap(); write_pool .add_to_pool(test_source(), agg_tx, false, &header.hash()) .unwrap(); diff --git a/servers/src/common/adapters.rs b/servers/src/common/adapters.rs index ade7212e7..93f6b630c 100644 --- a/servers/src/common/adapters.rs +++ b/servers/src/common/adapters.rs @@ -28,6 +28,7 @@ use common::types::{self, ChainValidationMode, ServerConfig, SyncState, SyncStat use core::core::hash::{Hash, Hashed}; use core::core::target::Difficulty; use core::core::transaction::Transaction; +use core::core::verifier_cache::VerifierCache; use core::core::{BlockHeader, CompactBlock}; use core::{core, global}; use p2p; @@ -54,6 +55,7 @@ pub struct NetToChainAdapter { archive_mode: bool, chain: Weak, tx_pool: Arc>, + verifier_cache: Arc>, peers: OneTime>, config: ServerConfig, } @@ -171,7 +173,11 @@ impl p2p::ChainAdapter for NetToChainAdapter { if let Ok(prev) = chain.get_block_header(&cb.header.previous) { if block - .validate(&prev.total_kernel_offset, &prev.total_kernel_sum) + .validate( + &prev.total_kernel_offset, + &prev.total_kernel_sum, + self.verifier_cache.clone(), + ) .is_ok() { debug!(LOGGER, "adapter: successfully hydrated block from tx pool!"); @@ -379,6 +385,7 @@ impl NetToChainAdapter { archive_mode: bool, chain_ref: Weak, tx_pool: Arc>, + verifier_cache: Arc>, config: ServerConfig, ) -> NetToChainAdapter { NetToChainAdapter { @@ -386,6 +393,7 @@ impl NetToChainAdapter { archive_mode, chain: chain_ref, tx_pool, + verifier_cache, peers: OneTime::new(), config, } diff --git a/servers/src/grin/dandelion_monitor.rs b/servers/src/grin/dandelion_monitor.rs index 541feed6b..0d94a7d39 100644 --- a/servers/src/grin/dandelion_monitor.rs +++ b/servers/src/grin/dandelion_monitor.rs @@ -21,6 +21,7 @@ use std::time::Duration; use core::core::hash::Hashed; use core::core::transaction; +use core::core::verifier_cache::VerifierCache; use pool::{DandelionConfig, PoolEntryState, PoolError, TransactionPool, TxSource}; use util::LOGGER; @@ -35,6 +36,7 @@ use util::LOGGER; pub fn monitor_transactions( dandelion_config: DandelionConfig, tx_pool: Arc>, + verifier_cache: Arc>, stop: Arc, ) { debug!(LOGGER, "Started Dandelion transaction monitor."); @@ -56,14 +58,14 @@ pub fn monitor_transactions( // Step 1: find all "ToStem" entries in stempool from last run. // Aggregate them up to give a single (valid) aggregated tx and propagate it // to the next Dandelion relay along the stem. - if process_stem_phase(tx_pool.clone()).is_err() { + if process_stem_phase(tx_pool.clone(), verifier_cache.clone()).is_err() { error!(LOGGER, "dand_mon: Problem with stem phase."); } // Step 2: find all "ToFluff" entries in stempool from last run. // Aggregate them up to give a single (valid) aggregated tx and (re)add it // to our pool with stem=false (which will then broadcast it). - if process_fluff_phase(tx_pool.clone()).is_err() { + if process_fluff_phase(tx_pool.clone(), verifier_cache.clone()).is_err() { error!(LOGGER, "dand_mon: Problem with fluff phase."); } @@ -82,7 +84,10 @@ pub fn monitor_transactions( }); } -fn process_stem_phase(tx_pool: Arc>) -> Result<(), PoolError> { +fn process_stem_phase( + tx_pool: Arc>, + verifier_cache: Arc>, +) -> Result<(), PoolError> { let mut tx_pool = tx_pool.write().unwrap(); let header = tx_pool.blockchain.chain_head()?; @@ -102,7 +107,7 @@ fn process_stem_phase(tx_pool: Arc>) -> Result<(), PoolE stem_txs.len() ); - let agg_tx = transaction::aggregate(stem_txs)?; + let agg_tx = transaction::aggregate(stem_txs, verifier_cache.clone())?; let res = tx_pool.adapter.stem_tx_accepted(&agg_tx); if res.is_err() { @@ -122,7 +127,10 @@ fn process_stem_phase(tx_pool: Arc>) -> Result<(), PoolE Ok(()) } -fn process_fluff_phase(tx_pool: Arc>) -> Result<(), PoolError> { +fn process_fluff_phase( + tx_pool: Arc>, + verifier_cache: Arc>, +) -> Result<(), PoolError> { let mut tx_pool = tx_pool.write().unwrap(); let header = tx_pool.blockchain.chain_head()?; @@ -142,7 +150,7 @@ fn process_fluff_phase(tx_pool: Arc>) -> Result<(), Pool stem_txs.len() ); - let agg_tx = transaction::aggregate(stem_txs)?; + let agg_tx = transaction::aggregate(stem_txs, verifier_cache.clone())?; let src = TxSource { debug_name: "fluff".to_string(), diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index 705a5bd0b..eef5aab3c 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -30,6 +30,7 @@ use common::stats::{DiffBlock, DiffStats, PeerStats, ServerStateInfo, ServerStat use common::types::{Error, ServerConfig, StratumServerConfig, SyncState}; use core::core::hash::Hashed; use core::core::target::Difficulty; +use core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use core::{consensus, genesis, global, pow}; use grin::{dandelion_monitor, seed, sync}; use mining::stratumserver; @@ -49,6 +50,9 @@ pub struct Server { pub chain: Arc, /// in-memory transaction pool tx_pool: Arc>, + /// Shared cache for verification results when + /// verifying rangeproof and kernel signatures. + verifier_cache: Arc>, /// Whether we're currently syncing sync_state: Arc, /// To be passed around to collect stats and info @@ -122,11 +126,16 @@ impl Server { let stop = Arc::new(AtomicBool::new(false)); + // Shared cache for verification results. + // We cache rangeproof verification and kernel signature verification. + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + let pool_adapter = Arc::new(PoolToChainAdapter::new()); let pool_net_adapter = Arc::new(PoolToNetAdapter::new()); let tx_pool = Arc::new(RwLock::new(pool::TransactionPool::new( config.pool_config.clone(), pool_adapter.clone(), + verifier_cache.clone(), pool_net_adapter.clone(), ))); @@ -155,6 +164,7 @@ impl Server { chain_adapter.clone(), genesis.clone(), pow::verify_size, + verifier_cache.clone(), )?); pool_adapter.set_chain(Arc::downgrade(&shared_chain)); @@ -166,6 +176,7 @@ impl Server { archive_mode, Arc::downgrade(&shared_chain), tx_pool.clone(), + verifier_cache.clone(), config.clone(), )); @@ -260,21 +271,23 @@ impl Server { dandelion_monitor::monitor_transactions( config.dandelion_config.clone(), tx_pool.clone(), + verifier_cache.clone(), stop.clone(), ); warn!(LOGGER, "Grin server started."); Ok(Server { - config: config, + config, p2p: p2p_server, chain: shared_chain, - tx_pool: tx_pool, + tx_pool, + verifier_cache, sync_state, state_info: ServerStateInfo { awaiting_peers: awaiting_peers, ..Default::default() }, - stop: stop, + stop, }) } @@ -307,6 +320,7 @@ impl Server { config.clone(), self.chain.clone(), self.tx_pool.clone(), + self.verifier_cache.clone(), ); let stratum_stats = self.state_info.stratum_stats.clone(); let _ = thread::Builder::new() @@ -339,6 +353,7 @@ impl Server { config.clone(), self.chain.clone(), self.tx_pool.clone(), + self.verifier_cache.clone(), self.stop.clone(), ); miner.set_debug_output_id(format!("Port {}", self.config.p2p_config.port)); diff --git a/servers/src/grin/sync.rs b/servers/src/grin/sync.rs index 67a2075e5..c85c51ee7 100644 --- a/servers/src/grin/sync.rs +++ b/servers/src/grin/sync.rs @@ -427,9 +427,7 @@ fn get_locator( // for security, clear history_locators[] in any case of header chain rollback, // the easiest way is to check whether the sync head and the header head are identical. - if history_locators.len() > 0 - && tip.hash() != chain.get_header_head()?.hash() - { + if history_locators.len() > 0 && tip.hash() != chain.get_header_head()?.hash() { history_locators.clear(); } diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index cf1ecdab5..132271df5 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -24,6 +24,7 @@ use std::time::Duration; use chain; use common::types::Error; +use core::core::verifier_cache::VerifierCache; use core::ser::{self, AsFixedBytes}; use core::{consensus, core}; use keychain::{ExtKeychain, Identifier, Keychain}; @@ -76,12 +77,19 @@ impl ser::Writer for HeaderPrePowWriter { pub fn get_block( chain: &Arc, tx_pool: &Arc>, + verifier_cache: Arc>, key_id: Option, wallet_listener_url: Option, ) -> (core::Block, BlockFees) { let wallet_retry_interval = 5; // get the latest chain state and build a block on top of it - let mut result = build_block(chain, tx_pool, key_id.clone(), wallet_listener_url.clone()); + let mut result = build_block( + chain, + tx_pool, + verifier_cache.clone(), + key_id.clone(), + wallet_listener_url.clone(), + ); while let Err(e) = result { match e { self::Error::Chain(c) => match c.kind() { @@ -108,7 +116,13 @@ pub fn get_block( } } thread::sleep(Duration::from_millis(100)); - result = build_block(chain, tx_pool, key_id.clone(), wallet_listener_url.clone()); + result = build_block( + chain, + tx_pool, + verifier_cache.clone(), + key_id.clone(), + wallet_listener_url.clone(), + ); } return result.unwrap(); } @@ -118,6 +132,7 @@ pub fn get_block( fn build_block( chain: &Arc, tx_pool: &Arc>, + verifier_cache: Arc>, key_id: Option, wallet_listener_url: Option, ) -> Result<(core::Block, BlockFees), Error> { @@ -147,10 +162,21 @@ fn build_block( }; let (output, kernel, block_fees) = get_coinbase(wallet_listener_url, block_fees)?; - let mut b = core::Block::with_reward(&head, txs, output, kernel, difficulty.clone())?; + let mut b = core::Block::with_reward( + &head, + txs, + output, + kernel, + difficulty.clone(), + verifier_cache.clone(), + )?; // making sure we're not spending time mining a useless block - b.validate(&head.total_kernel_offset, &head.total_kernel_sum)?; + b.validate( + &head.total_kernel_offset, + &head.total_kernel_sum, + verifier_cache, + )?; let mut rng = rand::OsRng::new().unwrap(); b.header.nonce = rng.gen(); diff --git a/servers/src/mining/stratumserver.rs b/servers/src/mining/stratumserver.rs index b5ff8e9bb..e7f77daa2 100644 --- a/servers/src/mining/stratumserver.rs +++ b/servers/src/mining/stratumserver.rs @@ -28,6 +28,7 @@ use std::{cmp, thread}; use chain; use common::stats::{StratumStats, WorkerStats}; use common::types::{StratumServerConfig, SyncState}; +use core::core::verifier_cache::VerifierCache; use core::core::Block; use core::{global, pow}; use keychain; @@ -229,6 +230,7 @@ pub struct StratumServer { config: StratumServerConfig, chain: Arc, tx_pool: Arc>, + verifier_cache: Arc>, current_block_versions: Vec, current_difficulty: u64, minimum_share_difficulty: u64, @@ -241,15 +243,17 @@ impl StratumServer { /// Creates a new Stratum Server. pub fn new( config: StratumServerConfig, - chain_ref: Arc, + chain: Arc, tx_pool: Arc>, + verifier_cache: Arc>, ) -> StratumServer { StratumServer { id: String::from("StratumServer"), minimum_share_difficulty: config.minimum_share_difficulty, - config: config, - chain: chain_ref, - tx_pool: tx_pool, + config, + chain, + tx_pool, + verifier_cache, current_block_versions: Vec::new(), current_difficulty: ::max_value(), current_key_id: None, @@ -725,6 +729,7 @@ impl StratumServer { let (new_block, block_fees) = mine_block::get_block( &self.chain, &self.tx_pool, + self.verifier_cache.clone(), self.current_key_id.clone(), wallet_listener_url, ); diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index 60304ed96..702f4461d 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -24,6 +24,7 @@ use std::sync::{Arc, RwLock}; use chain; use common::types::StratumServerConfig; use core::core::hash::{Hash, Hashed}; +use core::core::verifier_cache::VerifierCache; use core::core::{Block, BlockHeader, Proof}; use core::pow::cuckoo; use core::{consensus, global}; @@ -35,6 +36,7 @@ pub struct Miner { config: StratumServerConfig, chain: Arc, tx_pool: Arc>, + verifier_cache: Arc>, stop: Arc, // Just to hold the port we're on, so this miner can be identified @@ -47,16 +49,18 @@ impl Miner { /// storage. pub fn new( config: StratumServerConfig, - chain_ref: Arc, + chain: Arc, tx_pool: Arc>, + verifier_cache: Arc>, stop: Arc, ) -> Miner { Miner { - config: config, - chain: chain_ref, - tx_pool: tx_pool, + config, + chain, + tx_pool, + verifier_cache, debug_output_id: String::from("none"), - stop: stop, + stop, } } @@ -147,6 +151,7 @@ impl Miner { let (mut b, block_fees) = mine_block::get_block( &self.chain, &self.tx_pool, + self.verifier_cache.clone(), key_id.clone(), wallet_listener_url.clone(), ); diff --git a/src/bin/tui/table.rs b/src/bin/tui/table.rs index 43977207b..b02bc9490 100644 --- a/src/bin/tui/table.rs +++ b/src/bin/tui/table.rs @@ -41,8 +41,10 @@ //! Adapted from https://github.com/behnam/rust-cursive-table-view //! A basic table view implementation for [cursive](https://crates.io/crates/cursive). -#![deny(missing_docs, missing_copy_implementations, trivial_casts, trivial_numeric_casts, - unsafe_code, unused_import_braces, unused_qualifications)] +#![deny( + missing_docs, missing_copy_implementations, trivial_casts, trivial_numeric_casts, unsafe_code, + unused_import_braces, unused_qualifications +)] // Crate Dependencies --------------------------------------------------------- extern crate cursive; diff --git a/wallet/src/libtx/build.rs b/wallet/src/libtx/build.rs index 0e2c84b6a..15824568f 100644 --- a/wallet/src/libtx/build.rs +++ b/wallet/src/libtx/build.rs @@ -279,9 +279,16 @@ where // Just a simple test, most exhaustive tests in the core mod.rs. #[cfg(test)] mod test { + use std::sync::{Arc, RwLock}; + use super::*; + use core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use keychain::ExtKeychain; + fn verifier_cache() -> Arc> { + Arc::new(RwLock::new(LruVerifierCache::new())) + } + #[test] fn blind_simple_tx() { let keychain = ExtKeychain::from_random_seed().unwrap(); @@ -289,6 +296,8 @@ mod test { let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); + let vc = verifier_cache(); + let tx = transaction( vec![ input(10, key_id1), @@ -299,7 +308,7 @@ mod test { &keychain, ).unwrap(); - tx.validate().unwrap(); + tx.validate(vc.clone()).unwrap(); } #[test] @@ -309,6 +318,8 @@ mod test { let key_id2 = keychain.derive_key_id(2).unwrap(); let key_id3 = keychain.derive_key_id(3).unwrap(); + let vc = verifier_cache(); + let tx = transaction_with_offset( vec![ input(10, key_id1), @@ -319,7 +330,7 @@ mod test { &keychain, ).unwrap(); - tx.validate().unwrap(); + tx.validate(vc.clone()).unwrap(); } #[test] @@ -328,11 +339,13 @@ mod test { let key_id1 = keychain.derive_key_id(1).unwrap(); let key_id2 = keychain.derive_key_id(2).unwrap(); + let vc = verifier_cache(); + let tx = transaction( vec![input(6, key_id1), output(2, key_id2), with_fee(4)], &keychain, ).unwrap(); - tx.validate().unwrap(); + tx.validate(vc.clone()).unwrap(); } } diff --git a/wallet/src/libtx/slate.rs b/wallet/src/libtx/slate.rs index f1e54c646..0f698d8ce 100644 --- a/wallet/src/libtx/slate.rs +++ b/wallet/src/libtx/slate.rs @@ -16,9 +16,11 @@ //! around during an interactive wallet exchange use rand::thread_rng; +use std::sync::{Arc, RwLock}; use uuid::Uuid; use core::core::committed::Committed; +use core::core::verifier_cache::LruVerifierCache; use core::core::{amount_to_hr_string, Transaction}; use keychain::{BlindSum, BlindingFactor, Keychain}; use libtx::error::{Error, ErrorKind}; @@ -370,11 +372,6 @@ impl Slate { // build the final excess based on final tx and offset let final_excess = { - // TODO - do we need to verify rangeproofs here? - for x in final_tx.outputs() { - x.verify_proof()?; - } - // sum the input/output commitments on the final tx let overage = final_tx.fee() as i64; let tx_excess = final_tx.sum_commitments(overage)?; @@ -398,7 +395,8 @@ impl Slate { final_tx.kernels()[0].verify()?; // confirm the overall transaction is valid (including the updated kernel) - let _ = final_tx.validate()?; + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + let _ = final_tx.validate(verifier_cache)?; self.tx = final_tx; Ok(()) diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index a721a7969..a87d761c8 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -14,6 +14,9 @@ //! Transaction building functions +use std::sync::{Arc, RwLock}; + +use core::core::verifier_cache::LruVerifierCache; use core::core::Transaction; use keychain::{Identifier, Keychain}; use libtx::slate::Slate; @@ -196,7 +199,8 @@ where // finalize the burn transaction and send let tx_burn = build::transaction(parts, &keychain)?; - tx_burn.validate()?; + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); + tx_burn.validate(verifier_cache)?; Ok(tx_burn) } diff --git a/wallet/tests/common/testclient.rs b/wallet/tests/common/testclient.rs index 560f9d4f4..4de6a794b 100644 --- a/wallet/tests/common/testclient.rs +++ b/wallet/tests/common/testclient.rs @@ -20,7 +20,7 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::thread; use std::time::Duration; @@ -34,6 +34,7 @@ use common::failure::ResultExt; use chain::types::NoopAdapter; use chain::Chain; +use core::core::verifier_cache::LruVerifierCache; use core::core::Transaction; use core::global::{set_mining_mode, ChainTypes}; use core::{pow, ser}; @@ -100,6 +101,7 @@ where pub fn new(chain_dir: &str) -> Self { set_mining_mode(ChainTypes::AutomatedTesting); let genesis_block = pow::mine_genesis_block().unwrap(); + let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); let dir_name = format!("{}/.grin", chain_dir); let db_env = Arc::new(store::new_env(dir_name.to_string())); let c = Chain::init( @@ -108,6 +110,7 @@ where Arc::new(NoopAdapter {}), genesis_block, pow::verify_size, + verifier_cache, ).unwrap(); let (tx, rx) = channel(); let retval = WalletProxy {