uahf: block sigops check

This commit is contained in:
Svyatoslav Nikolsky 2017-08-08 13:45:39 +03:00
parent 127d662448
commit 6110d94544
8 changed files with 88 additions and 33 deletions

View File

@ -1,3 +1,4 @@
use std::cmp::max;
use hash::H256; use hash::H256;
use {Magic, Deployment}; use {Magic, Deployment};
@ -116,6 +117,16 @@ impl ConsensusParams {
} }
impl ConsensusFork { impl ConsensusFork {
/// Absolute (across all forks) maximum block size. Currently is 8MB for post-HF BitcoinCash
pub fn absolute_maximum_block_size() -> usize {
8_000_000
}
// Absolute (across all forks) maximum number of sigops in single block. Currently is max(sigops) for 8MB post-HF BitcoinCash block
pub fn absolute_maximum_block_sigops() -> usize {
160_000
}
pub fn min_block_size(&self, height: u32) -> usize { pub fn min_block_size(&self, height: u32) -> usize {
match *self { match *self {
ConsensusFork::SegWit2x(fork_height) if height == fork_height => 0, ConsensusFork::SegWit2x(fork_height) if height == fork_height => 0,
@ -133,8 +144,18 @@ impl ConsensusFork {
} }
} }
pub fn max_transaction_size(&self, height: u32) -> usize { pub fn max_transaction_size(&self, _height: u32) -> usize {
self.max_block_size(height) // BitcoinCash: according to REQ-5: max size of tx is still 1_000_000
1_000_000
}
pub fn max_block_sigops(&self, height: u32, block_size: usize) -> usize {
match *self {
// according to REQ-5: max_block_sigops = 20000 * ceil((max(blocksize_bytes, 1000000) / 1000000))
ConsensusFork::BitcoinCash(fork_height) if height >= fork_height && block_size > 1_000_000 =>
20_000 * (max(block_size, 1_000_000) / 1_000_000),
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) | ConsensusFork::BitcoinCash(_) => 20_000,
}
} }
} }
@ -188,13 +209,20 @@ mod tests {
} }
#[test] #[test]
fn test_consensus_fork_max_block_size() { fn test_consensus_fork_max_transaction_size() {
assert_eq!(ConsensusFork::NoFork.max_block_size(0), 1_000_000); assert_eq!(ConsensusFork::NoFork.max_transaction_size(0), 1_000_000);
assert_eq!(ConsensusFork::SegWit2x(100).max_block_size(0), 1_000_000); assert_eq!(ConsensusFork::SegWit2x(100).max_transaction_size(0), 1_000_000);
assert_eq!(ConsensusFork::SegWit2x(100).max_block_size(100), 2_000_000); assert_eq!(ConsensusFork::BitcoinCash(100).max_transaction_size(0), 1_000_000);
assert_eq!(ConsensusFork::SegWit2x(100).max_block_size(200), 2_000_000); }
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_size(0), 1_000_000);
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_size(100), 8_000_000); #[test]
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_size(200), 8_000_000); fn test_consensus_fork_max_block_sigops() {
assert_eq!(ConsensusFork::NoFork.max_block_sigops(0, 1_000_000), 20_000);
assert_eq!(ConsensusFork::SegWit2x(100).max_block_sigops(0, 1_000_000), 20_000);
assert_eq!(ConsensusFork::SegWit2x(100).max_block_sigops(100, 2_000_000), 20_000);
assert_eq!(ConsensusFork::SegWit2x(100).max_block_sigops(200, 3_000_000), 20_000);
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_sigops(0, 1_000_000), 20_000);
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_sigops(100, 2_000_000), 40_000);
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_sigops(200, 3_000_000), 60_000);
} }
} }

View File

@ -12,7 +12,6 @@ use synchronization_server::{Server, ServerTask};
use synchronization_verifier::{TransactionVerificationSink}; use synchronization_verifier::{TransactionVerificationSink};
use primitives::hash::H256; use primitives::hash::H256;
use miner::BlockTemplate; use miner::BlockTemplate;
use verification::constants::MAX_BLOCK_SIGOPS;
use synchronization_peers::{TransactionAnnouncementType, BlockAnnouncementType}; use synchronization_peers::{TransactionAnnouncementType, BlockAnnouncementType};
use types::{PeerIndex, RequestId, StorageRef, MemoryPoolRef, PeersRef, ExecutorRef, use types::{PeerIndex, RequestId, StorageRef, MemoryPoolRef, PeersRef, ExecutorRef,
ClientRef, ServerRef, SynchronizationStateRef, SyncListenerRef}; ClientRef, ServerRef, SynchronizationStateRef, SyncListenerRef};
@ -276,9 +275,11 @@ impl<T, U, V> LocalNode<T, U, V> where T: TaskExecutor, U: Server, V: Client {
/// Get block template for mining /// Get block template for mining
pub fn get_block_template(&self) -> BlockTemplate { pub fn get_block_template(&self) -> BlockTemplate {
let height = self.storage.best_block().number;
let max_block_size = self.consensus.fork.max_block_size(height);
let block_assembler = BlockAssembler { let block_assembler = BlockAssembler {
max_block_size: self.consensus.fork.max_block_size(self.storage.best_block().number) as u32, max_block_size: max_block_size as u32,
max_block_sigops: MAX_BLOCK_SIGOPS as u32, max_block_sigops: self.consensus.fork.max_block_sigops(height, max_block_size) as u32,
}; };
let memory_pool = &*self.memory_pool.read(); let memory_pool = &*self.memory_pool.read();
block_assembler.create_new_block(&self.storage, memory_pool, time::get_time().sec as u32, self.consensus.magic) block_assembler.create_new_block(&self.storage, memory_pool, time::get_time().sec as u32, self.consensus.magic)

View File

@ -6,7 +6,6 @@ use work::block_reward_satoshi;
use duplex_store::DuplexTransactionOutputProvider; use duplex_store::DuplexTransactionOutputProvider;
use deployments::Deployments; use deployments::Deployments;
use canon::CanonBlock; use canon::CanonBlock;
use constants::MAX_BLOCK_SIGOPS;
use error::{Error, TransactionError}; use error::{Error, TransactionError};
use timestamp::median_timestamp; use timestamp::median_timestamp;
@ -33,7 +32,7 @@ impl<'a> BlockAcceptor<'a> {
serialized_size: BlockSerializedSize::new(block, consensus, height), serialized_size: BlockSerializedSize::new(block, consensus, height),
coinbase_script: BlockCoinbaseScript::new(block, consensus, height), coinbase_script: BlockCoinbaseScript::new(block, consensus, height),
coinbase_claim: BlockCoinbaseClaim::new(block, store, height), coinbase_claim: BlockCoinbaseClaim::new(block, store, height),
sigops: BlockSigops::new(block, store, consensus, MAX_BLOCK_SIGOPS), sigops: BlockSigops::new(block, store, consensus, height),
} }
} }
@ -110,28 +109,29 @@ impl<'a> BlockSerializedSize<'a> {
pub struct BlockSigops<'a> { pub struct BlockSigops<'a> {
block: CanonBlock<'a>, block: CanonBlock<'a>,
store: &'a TransactionOutputProvider, store: &'a TransactionOutputProvider,
consensus_params: &'a ConsensusParams, consensus: &'a ConsensusParams,
max_sigops: usize, height: u32,
} }
impl<'a> BlockSigops<'a> { impl<'a> BlockSigops<'a> {
fn new(block: CanonBlock<'a>, store: &'a TransactionOutputProvider, consensus_params: &'a ConsensusParams, max_sigops: usize) -> Self { fn new(block: CanonBlock<'a>, store: &'a TransactionOutputProvider, consensus: &'a ConsensusParams, height: u32) -> Self {
BlockSigops { BlockSigops {
block: block, block: block,
store: store, store: store,
consensus_params: consensus_params, consensus: consensus,
max_sigops: max_sigops, height: height,
} }
} }
fn check(&self) -> Result<(), Error> { fn check(&self) -> Result<(), Error> {
let store = DuplexTransactionOutputProvider::new(self.store, &*self.block); let store = DuplexTransactionOutputProvider::new(self.store, &*self.block);
let bip16_active = self.block.header.raw.time >= self.consensus_params.bip16_time; let bip16_active = self.block.header.raw.time >= self.consensus.bip16_time;
let sigops = self.block.transactions.iter() let sigops = self.block.transactions.iter()
.map(|tx| transaction_sigops(&tx.raw, &store, bip16_active)) .map(|tx| transaction_sigops(&tx.raw, &store, bip16_active))
.sum::<usize>(); .sum::<usize>();
if sigops > self.max_sigops { let size = self.block.size();
if sigops > self.consensus.fork.max_block_sigops(self.height, size) {
Err(Error::MaximumSigops) Err(Error::MaximumSigops)
} else { } else {
Ok(()) Ok(())

View File

@ -6,7 +6,7 @@ use duplex_store::DuplexTransactionOutputProvider;
use deployments::Deployments; use deployments::Deployments;
use sigops::transaction_sigops; use sigops::transaction_sigops;
use canon::CanonTransaction; use canon::CanonTransaction;
use constants::{COINBASE_MATURITY, MAX_BLOCK_SIGOPS}; use constants::{COINBASE_MATURITY};
use error::TransactionError; use error::TransactionError;
pub struct TransactionAcceptor<'a> { pub struct TransactionAcceptor<'a> {
@ -80,11 +80,12 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
) -> Self { ) -> Self {
trace!(target: "verification", "Mempool-Tx verification {}", transaction.hash.to_reversed_str()); trace!(target: "verification", "Mempool-Tx verification {}", transaction.hash.to_reversed_str());
let transaction_index = 0; let transaction_index = 0;
let max_block_sigops = consensus.fork.max_block_sigops(height, consensus.fork.max_block_size(height));
MemoryPoolTransactionAcceptor { MemoryPoolTransactionAcceptor {
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index), missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
maturity: TransactionMaturity::new(transaction, meta_store, height), maturity: TransactionMaturity::new(transaction, meta_store, height),
overspent: TransactionOverspent::new(transaction, output_store), overspent: TransactionOverspent::new(transaction, output_store),
sigops: TransactionSigops::new(transaction, output_store, consensus, MAX_BLOCK_SIGOPS, time), sigops: TransactionSigops::new(transaction, output_store, consensus, max_block_sigops, time),
double_spent: TransactionDoubleSpend::new(transaction, output_store), double_spent: TransactionDoubleSpend::new(transaction, output_store),
eval: TransactionEval::new(transaction, output_store, consensus, height, time, deployments, headers), eval: TransactionEval::new(transaction, output_store, consensus, height, time, deployments, headers),
} }

View File

@ -352,7 +352,7 @@ mod tests {
} }
#[test] #[test]
fn sigops_overflow_block() { fn absoulte_sigops_overflow_block() {
let genesis = test_data::block_builder() let genesis = test_data::block_builder()
.transaction() .transaction()
.coinbase() .coinbase()
@ -367,12 +367,12 @@ mod tests {
let reference_tx = genesis.transactions()[1].hash(); let reference_tx = genesis.transactions()[1].hash();
let mut builder_tx1 = script::Builder::default(); let mut builder_tx1 = script::Builder::default();
for _ in 0..11000 { for _ in 0..81000 {
builder_tx1 = builder_tx1.push_opcode(script::Opcode::OP_CHECKSIG) builder_tx1 = builder_tx1.push_opcode(script::Opcode::OP_CHECKSIG)
} }
let mut builder_tx2 = script::Builder::default(); let mut builder_tx2 = script::Builder::default();
for _ in 0..11001 { for _ in 0..81001 {
builder_tx2 = builder_tx2.push_opcode(script::Opcode::OP_CHECKSIG) builder_tx2 = builder_tx2.push_opcode(script::Opcode::OP_CHECKSIG)
} }

View File

@ -2,7 +2,6 @@
pub const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours pub const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours
pub const COINBASE_MATURITY: u32 = 100; // 2 hours pub const COINBASE_MATURITY: u32 = 100; // 2 hours
pub const MAX_BLOCK_SIGOPS: usize = 20_000;
pub const MIN_COINBASE_SIZE: usize = 2; pub const MIN_COINBASE_SIZE: usize = 2;
pub const MAX_COINBASE_SIZE: usize = 100; pub const MAX_COINBASE_SIZE: usize = 100;

View File

@ -1,13 +1,14 @@
use std::collections::HashSet; use std::collections::HashSet;
use chain::IndexedBlock; use chain::IndexedBlock;
use network::ConsensusFork;
use sigops::transaction_sigops; use sigops::transaction_sigops;
use duplex_store::NoopStore; use duplex_store::NoopStore;
use error::{Error, TransactionError}; use error::{Error, TransactionError};
use constants::MAX_BLOCK_SIGOPS;
pub struct BlockVerifier<'a> { pub struct BlockVerifier<'a> {
pub empty: BlockEmpty<'a>, pub empty: BlockEmpty<'a>,
pub coinbase: BlockCoinbase<'a>, pub coinbase: BlockCoinbase<'a>,
pub serialized_size: BlockSerializedSize<'a>,
pub extra_coinbases: BlockExtraCoinbases<'a>, pub extra_coinbases: BlockExtraCoinbases<'a>,
pub transactions_uniqueness: BlockTransactionsUniqueness<'a>, pub transactions_uniqueness: BlockTransactionsUniqueness<'a>,
pub sigops: BlockSigops<'a>, pub sigops: BlockSigops<'a>,
@ -19,9 +20,10 @@ impl<'a> BlockVerifier<'a> {
BlockVerifier { BlockVerifier {
empty: BlockEmpty::new(block), empty: BlockEmpty::new(block),
coinbase: BlockCoinbase::new(block), coinbase: BlockCoinbase::new(block),
serialized_size: BlockSerializedSize::new(block, ConsensusFork::absolute_maximum_block_size()),
extra_coinbases: BlockExtraCoinbases::new(block), extra_coinbases: BlockExtraCoinbases::new(block),
transactions_uniqueness: BlockTransactionsUniqueness::new(block), transactions_uniqueness: BlockTransactionsUniqueness::new(block),
sigops: BlockSigops::new(block, MAX_BLOCK_SIGOPS), sigops: BlockSigops::new(block, ConsensusFork::absolute_maximum_block_sigops()),
merkle_root: BlockMerkleRoot::new(block), merkle_root: BlockMerkleRoot::new(block),
} }
} }
@ -29,6 +31,7 @@ impl<'a> BlockVerifier<'a> {
pub fn check(&self) -> Result<(), Error> { pub fn check(&self) -> Result<(), Error> {
try!(self.empty.check()); try!(self.empty.check());
try!(self.coinbase.check()); try!(self.coinbase.check());
try!(self.serialized_size.check());
try!(self.extra_coinbases.check()); try!(self.extra_coinbases.check());
try!(self.transactions_uniqueness.check()); try!(self.transactions_uniqueness.check());
try!(self.sigops.check()); try!(self.sigops.check());
@ -57,6 +60,29 @@ impl<'a> BlockEmpty<'a> {
} }
} }
pub struct BlockSerializedSize<'a> {
block: &'a IndexedBlock,
max_size: usize,
}
impl<'a> BlockSerializedSize<'a> {
fn new(block: &'a IndexedBlock, max_size: usize) -> Self {
BlockSerializedSize {
block: block,
max_size: max_size,
}
}
fn check(&self) -> Result<(), Error> {
let size = self.block.size();
if size > self.max_size {
Err(Error::Size(size))
} else {
Ok(())
}
}
}
pub struct BlockCoinbase<'a> { pub struct BlockCoinbase<'a> {
block: &'a IndexedBlock, block: &'a IndexedBlock,
} }

View File

@ -1,11 +1,11 @@
use std::ops; use std::ops;
use ser::Serializable; use ser::Serializable;
use chain::IndexedTransaction; use chain::IndexedTransaction;
use network::ConsensusParams; use network::{ConsensusParams, ConsensusFork};
use duplex_store::NoopStore; use duplex_store::NoopStore;
use sigops::transaction_sigops; use sigops::transaction_sigops;
use error::TransactionError; use error::TransactionError;
use constants::{MAX_BLOCK_SIGOPS, MIN_COINBASE_SIZE, MAX_COINBASE_SIZE}; use constants::{MIN_COINBASE_SIZE, MAX_COINBASE_SIZE};
pub struct TransactionVerifier<'a> { pub struct TransactionVerifier<'a> {
pub empty: TransactionEmpty<'a>, pub empty: TransactionEmpty<'a>,
@ -47,7 +47,7 @@ impl<'a> MemoryPoolTransactionVerifier<'a> {
null_non_coinbase: TransactionNullNonCoinbase::new(transaction), null_non_coinbase: TransactionNullNonCoinbase::new(transaction),
is_coinbase: TransactionMemoryPoolCoinbase::new(transaction), is_coinbase: TransactionMemoryPoolCoinbase::new(transaction),
size: TransactionSize::new(transaction, consensus, height), size: TransactionSize::new(transaction, consensus, height),
sigops: TransactionSigops::new(transaction, MAX_BLOCK_SIGOPS), sigops: TransactionSigops::new(transaction, ConsensusFork::absolute_maximum_block_sigops()),
} }
} }