uahf: block sigops check
This commit is contained in:
parent
127d662448
commit
6110d94544
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue