sigops counting separated to its own module
This commit is contained in:
parent
2c707b94c7
commit
3080ade4bd
|
@ -6,7 +6,6 @@ dependencies = [
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"db 0.1.0",
|
"db 0.1.0",
|
||||||
"ethcore-devtools 1.3.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)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"network 0.1.0",
|
"network 0.1.0",
|
||||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -28,5 +28,6 @@ pub trait AsTransactionProvider {
|
||||||
pub trait PreviousTransactionOutputProvider {
|
pub trait PreviousTransactionOutputProvider {
|
||||||
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>;
|
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;
|
fn is_spent(&self, prevout: &chain::OutPoint) -> bool;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
use chain::Transaction;
|
use chain::Transaction;
|
||||||
|
use ser::Serializable;
|
||||||
use db::TransactionProvider;
|
use db::TransactionProvider;
|
||||||
|
|
||||||
pub fn transaction_fee(store: &TransactionProvider, transaction: &Transaction) -> u64 {
|
pub fn transaction_fee(store: &TransactionProvider, transaction: &Transaction) -> u64 {
|
||||||
let inputs_sum = transaction.inputs.iter()
|
let inputs_sum = transaction.inputs.iter().map(|input| {
|
||||||
.fold(0, |accumulator, input| {
|
let input_transaction = store.transaction(&input.previous_output.hash)
|
||||||
let input_transaction = store.transaction(&input.previous_output.hash)
|
.expect("transaction must be verified by caller");
|
||||||
.expect("transaction must be verified by caller");
|
input_transaction.outputs[input.previous_output.index as usize].value
|
||||||
accumulator + input_transaction.outputs[input.previous_output.index as usize].value
|
}).sum::<u64>();
|
||||||
});
|
|
||||||
let outputs_sum = transaction.outputs.iter().map(|output| output.value).sum();
|
let outputs_sum = transaction.outputs.iter().map(|output| output.value).sum();
|
||||||
inputs_sum.saturating_sub(outputs_sum)
|
inputs_sum.saturating_sub(outputs_sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transaction_fee_rate(store: &TransactionProvider, transaction: &Transaction) -> u64 {
|
pub fn transaction_fee_rate(store: &TransactionProvider, transaction: &Transaction) -> u64 {
|
||||||
use ser::Serializable;
|
|
||||||
|
|
||||||
transaction_fee(store, transaction) / transaction.serialized_size() as u64
|
transaction_fee(store, transaction) / transaction.serialized_size() as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "0.5"
|
byteorder = "0.5"
|
||||||
parking_lot = "0.3"
|
parking_lot = "0.3"
|
||||||
linked-hash-map = "0.3"
|
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
scoped-pool = "1.0"
|
scoped-pool = "1.0"
|
||||||
|
|
|
@ -5,8 +5,8 @@ use scoped_pool::Pool;
|
||||||
use hash::H256;
|
use hash::H256;
|
||||||
use db::{self, BlockLocation, PreviousTransactionOutputProvider, BlockHeaderProvider};
|
use db::{self, BlockLocation, PreviousTransactionOutputProvider, BlockHeaderProvider};
|
||||||
use network::{Magic, ConsensusParams};
|
use network::{Magic, ConsensusParams};
|
||||||
use script::Script;
|
|
||||||
use error::{Error, TransactionError};
|
use error::{Error, TransactionError};
|
||||||
|
use sigops::{StoreWithUnretainedOutputs, transaction_sigops};
|
||||||
use {Verify, chain, utils};
|
use {Verify, chain, utils};
|
||||||
|
|
||||||
const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours
|
const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours
|
||||||
|
@ -72,49 +72,14 @@ impl ChainVerifier {
|
||||||
height >= self.consensus_params.bip65_height
|
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.
|
/// Returns number of block signature operations.
|
||||||
/// NOTE: This function expects all previous blocks to be already in database.
|
/// NOTE: This function expects all previous blocks to be already in database.
|
||||||
fn block_sigops(&self, block: &db::IndexedBlock) -> usize {
|
fn block_sigops(&self, block: &db::IndexedBlock) -> usize {
|
||||||
// strict pay-to-script-hash signature operations count toward block
|
// strict pay-to-script-hash signature operations count toward block
|
||||||
// signature operations limit is enforced with BIP16
|
// signature operations limit is enforced with BIP16
|
||||||
|
let store = StoreWithUnretainedOutputs::new(self.store.clone(), block);
|
||||||
let bip16_active = self.verify_p2sh(block.header().time);
|
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> {
|
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;
|
let mut total_unspent = 0u64;
|
||||||
for (tx_index, (_, tx)) in block.transactions().enumerate().skip(1) {
|
for (tx_index, (_, tx)) in block.transactions().enumerate().skip(1) {
|
||||||
let mut total_claimed: u64 = 0;
|
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");
|
.expect("missing tx, out of order verification or malformed db");
|
||||||
|
|
||||||
total_claimed += previous_output.value;
|
total_claimed += previous_output.value;
|
||||||
|
@ -223,10 +189,11 @@ impl ChainVerifier {
|
||||||
// must not be coinbase (sequence = 0 is returned above)
|
// must not be coinbase (sequence = 0 is returned above)
|
||||||
if transaction.is_coinbase() { return Err(TransactionError::MisplacedCoinbase(sequence)); }
|
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() {
|
for (input_index, input) in transaction.inputs().iter().enumerate() {
|
||||||
// signature verification
|
// signature verification
|
||||||
let signer: TransactionInputSigner = transaction.clone().into();
|
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,
|
Some(output) => output,
|
||||||
_ => return Err(TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
_ => return Err(TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate linked_hash_map;
|
|
||||||
extern crate time;
|
extern crate time;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
extern crate scoped_pool;
|
||||||
|
|
||||||
extern crate db;
|
extern crate db;
|
||||||
extern crate chain;
|
extern crate chain;
|
||||||
|
@ -13,7 +13,6 @@ extern crate network;
|
||||||
extern crate primitives;
|
extern crate primitives;
|
||||||
extern crate serialization;
|
extern crate serialization;
|
||||||
extern crate script;
|
extern crate script;
|
||||||
extern crate scoped_pool;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
|
@ -22,6 +21,7 @@ extern crate test_data;
|
||||||
|
|
||||||
mod chain_verifier;
|
mod chain_verifier;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod sigops;
|
||||||
mod task;
|
mod task;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ pub use primitives::{uint, hash, compact};
|
||||||
|
|
||||||
pub use chain_verifier::{Chain, ChainVerifier, VerificationResult};
|
pub use chain_verifier::{Chain, ChainVerifier, VerificationResult};
|
||||||
pub use error::{Error, TransactionError};
|
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};
|
pub use utils::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash, block_reward_satoshi};
|
||||||
|
|
||||||
/// Interface for block verification
|
/// Interface for block verification
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue