mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
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
This commit is contained in:
parent
9a127d6291
commit
939d391e0e
34 changed files with 562 additions and 219 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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<RwLock<txhashset::TxHashSet>>,
|
||||
// Recently processed blocks to avoid double-processing
|
||||
block_hashes_cache: Arc<RwLock<VecDeque<Hash>>>,
|
||||
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
// POW verification function
|
||||
pow_verifier: fn(&BlockHeader, u8) -> bool,
|
||||
}
|
||||
|
@ -154,6 +155,7 @@ impl Chain {
|
|||
adapter: Arc<ChainAdapter>,
|
||||
genesis: Block,
|
||||
pow_verifier: fn(&BlockHeader, u8) -> bool,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
) -> Result<Chain, Error> {
|
||||
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
|
||||
|
|
|
@ -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<Option<Tip>, Error> {
|
||||
pub fn process_block(
|
||||
b: &Block,
|
||||
ctx: &mut BlockContext,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
) -> Result<Option<Tip>, 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<Option<Tip>, 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<RwLock<VerifierCache>>,
|
||||
) -> 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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<Block, Error> {
|
||||
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<RwLock<VerifierCache>>,
|
||||
) -> Result<Block, Error> {
|
||||
// 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<RwLock<VerifierCache>>,
|
||||
) -> Result<(Commitment), Error> {
|
||||
self.body.validate(true)?;
|
||||
self.body.validate(true, verifier)?;
|
||||
|
||||
self.verify_kernel_lock_heights()?;
|
||||
self.verify_coinbase()?;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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<Commitment> = vec![];
|
||||
let mut proofs: Vec<RangeProof> = 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<RwLock<VerifierCache>>,
|
||||
) -> 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<RwLock<VerifierCache>>) -> 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<Input>, outputs: &mut Vec<Output>) -> Result
|
|||
}
|
||||
|
||||
/// Aggregate a vec of txs into a multi-kernel tx with cut_through.
|
||||
pub fn aggregate(mut txs: Vec<Transaction>) -> Result<Transaction, Error> {
|
||||
pub fn aggregate(
|
||||
mut txs: Vec<Transaction>,
|
||||
verifier: Arc<RwLock<VerifierCache>>,
|
||||
) -> Result<Transaction, Error> {
|
||||
// convenience short-circuiting
|
||||
if txs.is_empty() {
|
||||
return Ok(Transaction::empty());
|
||||
|
@ -854,14 +871,18 @@ pub fn aggregate(mut txs: Vec<Transaction>) -> Result<Transaction, Error> {
|
|||
// 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<Transaction>) -> Result<Transaction, Error> {
|
||||
pub fn deaggregate(
|
||||
mk_tx: Transaction,
|
||||
txs: Vec<Transaction>,
|
||||
verifier: Arc<RwLock<VerifierCache>>,
|
||||
) -> Result<Transaction, Error> {
|
||||
let mut inputs: Vec<Input> = vec![];
|
||||
let mut outputs: Vec<Output> = vec![];
|
||||
let mut kernels: Vec<TxKernel> = vec![];
|
||||
|
@ -870,7 +891,7 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec<Transaction>) -> Result<Transact
|
|||
// transaction
|
||||
let mut kernel_offsets = vec![];
|
||||
|
||||
let tx = aggregate(txs)?;
|
||||
let tx = aggregate(txs, verifier.clone())?;
|
||||
|
||||
for mk_input in mk_tx.body.inputs {
|
||||
if !tx.body.inputs.contains(&mk_input) && !inputs.contains(&mk_input) {
|
||||
|
@ -922,7 +943,7 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec<Transaction>) -> Result<Transact
|
|||
let tx = Transaction::new(inputs, outputs, kernels).with_offset(total_kernel_offset);
|
||||
|
||||
// Now validate the resulting tx to ensure we have not built something invalid.
|
||||
tx.validate()?;
|
||||
tx.validate(verifier)?;
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
|
|
116
core/src/core/verifier_cache.rs
Normal file
116
core/src/core/verifier_cache.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2018 The Grin Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! VerifierCache trait for batch verifying outputs and kernels.
|
||||
//! We pass a "caching verifier" into the block validation processing with this.
|
||||
|
||||
use lru_cache::LruCache;
|
||||
|
||||
use core::hash::{Hash, Hashed};
|
||||
use core::{Output, TxKernel};
|
||||
use util::LOGGER;
|
||||
|
||||
/// Verifier cache for caching expensive verification results.
|
||||
/// Specifically the following -
|
||||
/// * kernel signature verification
|
||||
/// * output rangeproof verification
|
||||
pub trait VerifierCache: Sync + Send {
|
||||
/// Takes a vec of tx kernels and returns those kernels
|
||||
/// that have not yet been verified.
|
||||
fn filter_kernel_sig_unverified(&mut self, kernels: &Vec<TxKernel>) -> Vec<TxKernel>;
|
||||
/// 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<Output>) -> Vec<Output>;
|
||||
/// 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<TxKernel>);
|
||||
/// Adds a vec of outputs to the cache (used in conjunction with the the filter above).
|
||||
fn add_rangeproof_verified(&mut self, outputs: Vec<Output>);
|
||||
}
|
||||
|
||||
/// 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<Hash, bool>,
|
||||
rangeproof_verification_cache: LruCache<Hash, bool>,
|
||||
}
|
||||
|
||||
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<TxKernel>) -> Vec<TxKernel> {
|
||||
let res = kernels
|
||||
.into_iter()
|
||||
.filter(|x| {
|
||||
!*self
|
||||
.kernel_sig_verification_cache
|
||||
.get_mut(&x.hash())
|
||||
.unwrap_or(&mut false)
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
debug!(
|
||||
LOGGER,
|
||||
"lru_verifier_cache: kernel sigs: {}, not cached (must verify): {}",
|
||||
kernels.len(),
|
||||
res.len()
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
fn filter_rangeproof_unverified(&mut self, outputs: &Vec<Output>) -> Vec<Output> {
|
||||
let res = outputs
|
||||
.into_iter()
|
||||
.filter(|x| {
|
||||
!*self
|
||||
.rangeproof_verification_cache
|
||||
.get_mut(&x.proof.hash())
|
||||
.unwrap_or(&mut false)
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
debug!(
|
||||
LOGGER,
|
||||
"lru_verifier_cache: rangeproofs: {}, not cached (must verify): {}",
|
||||
outputs.len(),
|
||||
res.len()
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
fn add_kernel_sig_verified(&mut self, kernels: Vec<TxKernel>) {
|
||||
for k in kernels {
|
||||
self.kernel_sig_verification_cache.insert(k.hash(), true);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_rangeproof_verified(&mut self, outputs: Vec<Output>) {
|
||||
for o in outputs {
|
||||
self.rangeproof_verification_cache
|
||||
.insert(o.proof.hash(), true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<RwLock<VerifierCache>> {
|
||||
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))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<RwLock<VerifierCache>> {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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<PoolEntry>,
|
||||
/// The blockchain
|
||||
pub blockchain: Arc<BlockChain>,
|
||||
pub verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Pool {
|
||||
pub fn new(chain: Arc<BlockChain>, name: String) -> Pool {
|
||||
pub fn new(
|
||||
chain: Arc<BlockChain>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
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
|
||||
|
|
|
@ -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<BlockChain>,
|
||||
pub verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
/// The pool adapter
|
||||
pub adapter: Arc<PoolAdapter>,
|
||||
}
|
||||
|
@ -44,14 +47,16 @@ impl TransactionPool {
|
|||
pub fn new(
|
||||
config: PoolConfig,
|
||||
chain: Arc<BlockChain>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
adapter: Arc<PoolAdapter>,
|
||||
) -> 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)?;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<ChainAdapter>) -> TransactionPool {
|
||||
pub fn test_setup(
|
||||
chain: Arc<BlockChain>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
) -> TransactionPool {
|
||||
TransactionPool::new(
|
||||
PoolConfig {
|
||||
accept_fee_base: 0,
|
||||
max_pool_size: 50,
|
||||
},
|
||||
chain.clone(),
|
||||
verifier_cache.clone(),
|
||||
Arc::new(NoopAdapter {}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
peers: OneTime<Weak<p2p::Peers>>,
|
||||
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<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
config: ServerConfig,
|
||||
) -> NetToChainAdapter {
|
||||
NetToChainAdapter {
|
||||
|
@ -386,6 +393,7 @@ impl NetToChainAdapter {
|
|||
archive_mode,
|
||||
chain: chain_ref,
|
||||
tx_pool,
|
||||
verifier_cache,
|
||||
peers: OneTime::new(),
|
||||
config,
|
||||
}
|
||||
|
|
|
@ -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<RwLock<TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
stop: Arc<AtomicBool>,
|
||||
) {
|
||||
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<RwLock<TransactionPool>>) -> Result<(), PoolError> {
|
||||
fn process_stem_phase(
|
||||
tx_pool: Arc<RwLock<TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
) -> 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<RwLock<TransactionPool>>) -> 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<RwLock<TransactionPool>>) -> Result<(), PoolE
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn process_fluff_phase(tx_pool: Arc<RwLock<TransactionPool>>) -> Result<(), PoolError> {
|
||||
fn process_fluff_phase(
|
||||
tx_pool: Arc<RwLock<TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
) -> 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<RwLock<TransactionPool>>) -> 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(),
|
||||
|
|
|
@ -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<chain::Chain>,
|
||||
/// in-memory transaction pool
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
/// Shared cache for verification results when
|
||||
/// verifying rangeproof and kernel signatures.
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
/// Whether we're currently syncing
|
||||
sync_state: Arc<SyncState>,
|
||||
/// 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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<chain::Chain>,
|
||||
tx_pool: &Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
key_id: Option<Identifier>,
|
||||
wallet_listener_url: Option<String>,
|
||||
) -> (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<chain::Chain>,
|
||||
tx_pool: &Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
key_id: Option<Identifier>,
|
||||
wallet_listener_url: Option<String>,
|
||||
) -> 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();
|
||||
|
|
|
@ -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<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
current_block_versions: Vec<Block>,
|
||||
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::Chain>,
|
||||
chain: Arc<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
) -> 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: <u64>::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,
|
||||
);
|
||||
|
|
|
@ -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<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
stop: Arc<AtomicBool>,
|
||||
|
||||
// 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::Chain>,
|
||||
chain: Arc<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool>>,
|
||||
verifier_cache: Arc<RwLock<VerifierCache>>,
|
||||
stop: Arc<AtomicBool>,
|
||||
) -> 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(),
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<RwLock<VerifierCache>> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue