segwit: sigops cost stub

This commit is contained in:
Svyatoslav Nikolsky 2017-08-17 16:19:29 +03:00
parent bda2277b71
commit 1e28ec4ed5
5 changed files with 80 additions and 6 deletions

View File

@ -230,6 +230,15 @@ impl ConsensusFork {
sigops <= 20_000,
}
}
pub fn check_block_sigops_cost(&self, sigops_cost: usize, deployments: &Deployments) -> bool {
match *self {
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) if deployments.is_active("segwit") =>
sigops_cost <= segwit::MAX_BLOCK_SIGOPS_COST,
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) | ConsensusFork::BitcoinCash(_) =>
true,
}
}
}
#[cfg(test)]

View File

@ -305,7 +305,7 @@ pub fn verify_script(
if flags.verify_witness {
if let Some((witness_version, witness_program)) = pubkey2.parse_witness_program() {
if script_sig != Builder::default().push_data(&pubkey2) {
if script_sig != &Builder::default().push_data(&pubkey2).into_script() {
return Err(Error::WitnessMalleatedP2SH);
}

View File

@ -146,7 +146,7 @@ impl Script {
/// Parse witness program. Returns Some(witness program version, code) or None if not a witness program.
pub fn parse_witness_program(&self) -> Option<(u8, &[u8])> {
if self.data.len() > 4 || self.data.len() > 42 || self.data.len() != self.data[1] as usize + 2 {
if self.data.len() < 4 || self.data.len() > 42 || self.data.len() != self.data[1] as usize + 2 {
return None;
}
let witness_version = match Opcode::from_u8(self.data[0]) {

View File

@ -3,7 +3,7 @@ use crypto::dhash256;
use db::{TransactionOutputProvider, BlockHeaderProvider};
use script;
use ser::Stream;
use sigops::transaction_sigops;
use sigops::{transaction_sigops, transaction_sigops_cost} ;
use work::block_reward_satoshi;
use duplex_store::DuplexTransactionOutputProvider;
use deployments::{Deployments, ActiveDeployments};
@ -35,7 +35,7 @@ impl<'a> BlockAcceptor<'a> {
serialized_size: BlockSerializedSize::new(block, consensus, deployments, height),
coinbase_script: BlockCoinbaseScript::new(block, consensus, height),
coinbase_claim: BlockCoinbaseClaim::new(block, store, height),
sigops: BlockSigops::new(block, store, consensus, height),
sigops: BlockSigops::new(block, store, consensus, deployments, height),
witness: BlockWitness::new(block, deployments),
}
}
@ -128,15 +128,17 @@ pub struct BlockSigops<'a> {
block: CanonBlock<'a>,
store: &'a TransactionOutputProvider,
consensus: &'a ConsensusParams,
deployments: ActiveDeployments<'a>,
height: u32,
}
impl<'a> BlockSigops<'a> {
fn new(block: CanonBlock<'a>, store: &'a TransactionOutputProvider, consensus: &'a ConsensusParams, height: u32) -> Self {
fn new(block: CanonBlock<'a>, store: &'a TransactionOutputProvider, consensus: &'a ConsensusParams, deployments: ActiveDeployments<'a>, height: u32) -> Self {
BlockSigops {
block: block,
store: store,
consensus: consensus,
deployments: deployments,
height: height,
}
}
@ -150,6 +152,14 @@ impl<'a> BlockSigops<'a> {
let size = self.block.size();
if sigops > self.consensus.fork.max_block_sigops(self.height, size) {
return Err(Error::MaximumSigops)
}
// TODO: when segwit is enabled, only sigop_cost must be checked!!!
let sigops_cost = self.block.transactions.iter()
.map(|tx| transaction_sigops_cost(&tx.raw, &store, bip16_active))
.sum::<usize>();
if !self.consensus.fork.check_block_sigops_cost(sigops_cost, &self.deployments) {
Err(Error::MaximumSigops)
} else {
Ok(())

View File

@ -1,6 +1,8 @@
// TODO: excess clones
use network::segwit;
use chain::Transaction;
use db::TransactionOutputProvider;
use script::Script;
use script::{Script, ScriptWitness};
/// Counts signature operations in given transaction
/// bip16_active flag indicates if we should also count signature operations
@ -16,6 +18,7 @@ pub fn transaction_sigops(
output_script.sigops_count(false)
}).sum();
// TODO: bitcoin/bitcoin also includes input_sigops here
if transaction.is_coinbase() {
return output_sigops;
}
@ -38,3 +41,55 @@ pub fn transaction_sigops(
input_sigops + output_sigops + bip16_sigops
}
pub fn transaction_sigops_cost(
transaction: &Transaction,
store: &TransactionOutputProvider,
bip16_active: bool,
) -> usize {
let sigops_cost = transaction_sigops(transaction, store, bip16_active) * segwit::WITNESS_SCALE_FACTOR;
let witness_sigops_cost: usize = transaction.inputs.iter()
.map(|input| store.transaction_output(&input.previous_output, usize::max_value())
.map(|output| witness_sigops(&Script::new(input.script_sig.clone()), &Script::new(output.script_pubkey.clone()), &ScriptWitness { stack: input.script_witness.clone().into() }))
.unwrap_or(0))
.sum();
sigops_cost + witness_sigops_cost
}
fn witness_sigops(
script_sig: &Script,
script_pubkey: &Script,
script_witness: &ScriptWitness,
) -> usize {
if let Some((witness_version, witness_program)) = script_pubkey.parse_witness_program() {
return witness_program_sigops(witness_version, witness_program, script_witness);
}
if script_pubkey.is_pay_to_script_hash() && script_sig.is_push_only() {
if let Some(Ok(instruction)) = script_sig.iter().last() {
if let Some(data) = instruction.data {
let subscript = Script::new(data.into());
if let Some((witness_version, witness_program)) = subscript.parse_witness_program() {
return witness_program_sigops(witness_version, witness_program, script_witness);
}
}
}
}
0
}
fn witness_program_sigops(
witness_version: u8,
witness_program: &[u8],
script_witness: &ScriptWitness,
) -> usize {
match witness_version {
0 if witness_program.len() == 20 => 1,
0 if witness_program.len() == 32 => match script_witness.stack.last().ok() {
Some(subscript) => Script::new(subscript.clone()).sigops_count(true),
_ => 0
},
_ => 0,
}
}