sigops counting separated to its own module

This commit is contained in:
debris 2016-12-09 11:16:50 +01:00
parent 2c707b94c7
commit 3080ade4bd
7 changed files with 75 additions and 52 deletions

1
Cargo.lock generated
View File

@ -6,7 +6,6 @@ dependencies = [
"chain 0.1.0",
"db 0.1.0",
"ethcore-devtools 1.3.0",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"network 0.1.0",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -28,5 +28,6 @@ pub trait AsTransactionProvider {
pub trait PreviousTransactionOutputProvider {
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>;
// TODO: this should not be here, cause it requires meta data
fn is_spent(&self, prevout: &chain::OutPoint) -> bool;
}

View File

@ -1,20 +1,18 @@
use chain::Transaction;
use ser::Serializable;
use db::TransactionProvider;
pub fn transaction_fee(store: &TransactionProvider, transaction: &Transaction) -> u64 {
let inputs_sum = transaction.inputs.iter()
.fold(0, |accumulator, input| {
let input_transaction = store.transaction(&input.previous_output.hash)
.expect("transaction must be verified by caller");
accumulator + input_transaction.outputs[input.previous_output.index as usize].value
});
let inputs_sum = transaction.inputs.iter().map(|input| {
let input_transaction = store.transaction(&input.previous_output.hash)
.expect("transaction must be verified by caller");
input_transaction.outputs[input.previous_output.index as usize].value
}).sum::<u64>();
let outputs_sum = transaction.outputs.iter().map(|output| output.value).sum();
inputs_sum.saturating_sub(outputs_sum)
}
pub fn transaction_fee_rate(store: &TransactionProvider, transaction: &Transaction) -> u64 {
use ser::Serializable;
transaction_fee(store, transaction) / transaction.serialized_size() as u64
}

View File

@ -6,7 +6,6 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
[dependencies]
byteorder = "0.5"
parking_lot = "0.3"
linked-hash-map = "0.3"
time = "0.1"
log = "0.3"
scoped-pool = "1.0"

View File

@ -5,8 +5,8 @@ use scoped_pool::Pool;
use hash::H256;
use db::{self, BlockLocation, PreviousTransactionOutputProvider, BlockHeaderProvider};
use network::{Magic, ConsensusParams};
use script::Script;
use error::{Error, TransactionError};
use sigops::{StoreWithUnretainedOutputs, transaction_sigops};
use {Verify, chain, utils};
const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours
@ -72,49 +72,14 @@ impl ChainVerifier {
height >= self.consensus_params.bip65_height
}
/// Returns previous transaction output.
/// NOTE: This function expects all previous blocks to be already in database.
fn previous_transaction_output<T>(&self, prevout_provider: &T, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>
where T: PreviousTransactionOutputProvider {
self.store.transaction(&prevout.hash)
.and_then(|tx| tx.outputs.into_iter().nth(prevout.index as usize))
.or_else(|| prevout_provider.previous_transaction_output(prevout))
}
/// Returns number of transaction signature operations.
/// NOTE: This function expects all previous blocks to be already in database.
fn transaction_sigops(&self, block: &db::IndexedBlock, transaction: &chain::Transaction, bip16_active: bool) -> usize {
let output_sigops: usize = transaction.outputs.iter().map(|output| {
let output_script: Script = output.script_pubkey.clone().into();
output_script.sigops_count(false)
}).sum();
if transaction.is_coinbase() {
return output_sigops;
}
let input_sigops: usize = transaction.inputs.iter().map(|input| {
let input_script: Script = input.script_sig.clone().into();
let mut sigops = input_script.sigops_count(false);
if bip16_active {
let previous_output = self.previous_transaction_output(block, &input.previous_output)
.expect("missing tx, out of order verification or malformed db");
let prevout_script: Script = previous_output.script_pubkey.into();
sigops += input_script.pay_to_script_hash_sigops(&prevout_script);
}
sigops
}).sum();
input_sigops + output_sigops
}
/// Returns number of block signature operations.
/// NOTE: This function expects all previous blocks to be already in database.
fn block_sigops(&self, block: &db::IndexedBlock) -> usize {
// strict pay-to-script-hash signature operations count toward block
// signature operations limit is enforced with BIP16
let store = StoreWithUnretainedOutputs::new(self.store.clone(), block);
let bip16_active = self.verify_p2sh(block.header().time);
block.transactions().map(|(_, tx)| self.transaction_sigops(block, tx, bip16_active)).sum()
block.transactions().map(|(_, tx)| transaction_sigops(tx, &store, bip16_active)).sum()
}
fn ordered_verify(&self, block: &db::IndexedBlock, at_height: u32) -> Result<(), Error> {
@ -161,6 +126,7 @@ impl ChainVerifier {
}
}
let unretained_store = StoreWithUnretainedOutputs::new(self.store.clone(), block);
let mut total_unspent = 0u64;
for (tx_index, (_, tx)) in block.transactions().enumerate().skip(1) {
let mut total_claimed: u64 = 0;
@ -175,7 +141,7 @@ impl ChainVerifier {
}
}
let previous_output = self.previous_transaction_output(block, &input.previous_output)
let previous_output = unretained_store.previous_transaction_output(&input.previous_output)
.expect("missing tx, out of order verification or malformed db");
total_claimed += previous_output.value;
@ -223,10 +189,11 @@ impl ChainVerifier {
// must not be coinbase (sequence = 0 is returned above)
if transaction.is_coinbase() { return Err(TransactionError::MisplacedCoinbase(sequence)); }
let unretained_store = StoreWithUnretainedOutputs::new(self.store.clone(), prevout_provider);
for (input_index, input) in transaction.inputs().iter().enumerate() {
// signature verification
let signer: TransactionInputSigner = transaction.clone().into();
let paired_output = match self.previous_transaction_output(prevout_provider, &input.previous_output) {
let paired_output = match unretained_store.previous_transaction_output(&input.previous_output) {
Some(output) => output,
_ => return Err(TransactionError::UnknownReference(input.previous_output.hash.clone()))
};

View File

@ -2,10 +2,10 @@
extern crate byteorder;
extern crate parking_lot;
extern crate linked_hash_map;
extern crate time;
#[macro_use]
extern crate log;
extern crate scoped_pool;
extern crate db;
extern crate chain;
@ -13,7 +13,6 @@ extern crate network;
extern crate primitives;
extern crate serialization;
extern crate script;
extern crate scoped_pool;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
@ -22,6 +21,7 @@ extern crate test_data;
mod chain_verifier;
mod error;
mod sigops;
mod task;
mod utils;
@ -29,6 +29,7 @@ pub use primitives::{uint, hash, compact};
pub use chain_verifier::{Chain, ChainVerifier, VerificationResult};
pub use error::{Error, TransactionError};
pub use sigops::{transaction_sigops, StoreWithUnretainedOutputs};
pub use utils::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash, block_reward_satoshi};
/// Interface for block verification

View File

@ -0,0 +1,58 @@
use chain::{Transaction, TransactionOutput, OutPoint};
use db::{PreviousTransactionOutputProvider, SharedStore};
use script::Script;
pub struct StoreWithUnretainedOutputs<'a, T> where T: 'a {
store: SharedStore,
outputs: &'a T,
}
impl<'a, T> StoreWithUnretainedOutputs<'a, T> where T: PreviousTransactionOutputProvider {
pub fn new(store: SharedStore, outputs: &'a T) -> Self {
StoreWithUnretainedOutputs {
store: store,
outputs: outputs,
}
}
}
impl<'a, T> PreviousTransactionOutputProvider for StoreWithUnretainedOutputs<'a, T> where T: PreviousTransactionOutputProvider {
fn previous_transaction_output(&self, prevout: &OutPoint) -> Option<TransactionOutput> {
self.store.transaction(&prevout.hash)
.and_then(|tx| tx.outputs.into_iter().nth(prevout.index as usize))
.or_else(|| self.outputs.previous_transaction_output(prevout))
}
fn is_spent(&self, _prevout: &OutPoint) -> bool {
unimplemented!();
}
}
pub fn transaction_sigops(
transaction: &Transaction,
store: &PreviousTransactionOutputProvider,
bip16_active: bool
) -> usize {
let output_sigops: usize = transaction.outputs.iter().map(|output| {
let output_script: Script = output.script_pubkey.clone().into();
output_script.sigops_count(false)
}).sum();
if transaction.is_coinbase() {
return output_sigops;
}
let input_sigops: usize = transaction.inputs.iter().map(|input| {
let input_script: Script = input.script_sig.clone().into();
let mut sigops = input_script.sigops_count(false);
if bip16_active {
let previous_output = store.previous_transaction_output(&input.previous_output)
.expect("missing tx, out of order verification or malformed db");
let prevout_script: Script = previous_output.script_pubkey.into();
sigops += input_script.pay_to_script_hash_sigops(&prevout_script);
}
sigops
}).sum();
input_sigops + output_sigops
}