BCH Nov2018 HF: minimal tx size

This commit is contained in:
Svyatoslav Nikolsky 2018-10-16 17:15:26 +03:00
parent ad4f51ac07
commit 91ea989bb7
4 changed files with 81 additions and 4 deletions

View File

@ -191,6 +191,13 @@ impl ConsensusFork {
}
}
pub fn min_transaction_size(&self, median_time_past: u32) -> usize {
match *self {
ConsensusFork::BitcoinCash(ref fork) if median_time_past >= fork.magnetic_anomaly_time => 100,
_ => 0,
}
}
pub fn max_transaction_size(&self) -> usize {
// BitcoinCash: according to REQ-5: max size of tx is still 1_000_000
// SegWit: size * 4 <= 4_000_000 ===> max size of tx is still 1_000_000
@ -328,6 +335,14 @@ mod tests {
assert_eq!(ConsensusFork::BitcoinCash(BitcoinCashConsensusParams::new(Network::Mainnet)).max_transaction_size(), 1_000_000);
}
#[test]
fn test_consensus_fork_min_transaction_size() {
assert_eq!(ConsensusFork::BitcoinCore.min_transaction_size(0), 0);
assert_eq!(ConsensusFork::BitcoinCore.min_transaction_size(2000000000), 0);
assert_eq!(ConsensusFork::BitcoinCash(BitcoinCashConsensusParams::new(Network::Mainnet)).min_transaction_size(0), 0);
assert_eq!(ConsensusFork::BitcoinCash(BitcoinCashConsensusParams::new(Network::Mainnet)).min_transaction_size(2000000000), 100);
}
#[test]
fn test_consensus_fork_max_block_sigops() {
assert_eq!(ConsensusFork::BitcoinCore.max_block_sigops(0, 1_000_000), 20_000);

View File

@ -1,5 +1,6 @@
use primitives::hash::H256;
use primitives::bytes::Bytes;
use ser::Serializable;
use storage::{TransactionMetaProvider, TransactionOutputProvider};
use network::{ConsensusParams, ConsensusFork};
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, SignatureVersion};
@ -13,6 +14,7 @@ use error::TransactionError;
use VerificationLevel;
pub struct TransactionAcceptor<'a> {
pub size: TransactionSize<'a>,
pub premature_witness: TransactionPrematureWitness<'a>,
pub bip30: TransactionBip30<'a>,
pub missing_inputs: TransactionMissingInputs<'a>,
@ -44,6 +46,7 @@ impl<'a> TransactionAcceptor<'a> {
let tx_ordering = consensus.fork.transaction_ordering(median_time_past);
let missing_input_tx_index = transaction_index_for_output_check(tx_ordering,transaction_index);
TransactionAcceptor {
size: TransactionSize::new(transaction, consensus, median_time_past),
premature_witness: TransactionPrematureWitness::new(transaction, deployments),
bip30: TransactionBip30::new_for_sync(transaction, meta_store, consensus, block_hash, height),
missing_inputs: TransactionMissingInputs::new(transaction, output_store, missing_input_tx_index),
@ -56,6 +59,7 @@ impl<'a> TransactionAcceptor<'a> {
}
pub fn check(&self) -> Result<(), TransactionError> {
try!(self.size.check());
try!(self.premature_witness.check());
try!(self.bip30.check());
try!(self.missing_inputs.check());
@ -69,6 +73,7 @@ impl<'a> TransactionAcceptor<'a> {
}
pub struct MemoryPoolTransactionAcceptor<'a> {
pub size: TransactionSize<'a>,
pub missing_inputs: TransactionMissingInputs<'a>,
pub maturity: TransactionMaturity<'a>,
pub overspent: TransactionOverspent<'a>,
@ -95,6 +100,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
let transaction_index = 0;
let max_block_sigops = consensus.fork.max_block_sigops(height, consensus.fork.max_block_size(height, median_time_past));
MemoryPoolTransactionAcceptor {
size: TransactionSize::new(transaction, consensus, median_time_past),
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
maturity: TransactionMaturity::new(transaction, meta_store, height),
overspent: TransactionOverspent::new(transaction, output_store),
@ -108,6 +114,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
pub fn check(&self) -> Result<(), TransactionError> {
// Bip30 is not checked because we don't need to allow tx pool acceptance of an unspent duplicate.
// Tx pool validation is not strinctly a matter of consensus.
try!(self.size.check());
try!(self.missing_inputs.check());
try!(self.maturity.check());
try!(self.overspent.check());
@ -498,6 +505,30 @@ impl<'a> TransactionPrematureWitness<'a> {
}
}
pub struct TransactionSize<'a> {
transaction: CanonTransaction<'a>,
min_transaction_size: usize,
}
impl<'a> TransactionSize<'a> {
fn new(transaction: CanonTransaction<'a>, consensus: &'a ConsensusParams, median_time_past: u32) -> Self {
let min_transaction_size = consensus.fork.min_transaction_size(median_time_past);
TransactionSize {
transaction: transaction,
min_transaction_size,
}
}
fn check(&self) -> Result<(), TransactionError> {
let size = self.transaction.raw.serialized_size();
if size < self.min_transaction_size {
Err(TransactionError::MinSize)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use chain::{IndexedTransaction, Transaction, TransactionOutput};
@ -505,7 +536,7 @@ mod tests {
use script::Builder;
use canon::CanonTransaction;
use error::TransactionError;
use super::TransactionReturnReplayProtection;
use super::{TransactionReturnReplayProtection, TransactionSize};
#[test]
fn return_replay_protection_works() {
@ -533,4 +564,29 @@ mod tests {
let checker = TransactionReturnReplayProtection::new(CanonTransaction::new(&transaction), &consensus, 100);
assert_eq!(checker.check(), Ok(()));
}
#[test]
fn transaction_size_works() {
let small_tx = Transaction::default();
let big_tx: Transaction = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000".into();
let small_tx = IndexedTransaction::new(small_tx.hash(), small_tx);
let big_tx = IndexedTransaction::new(big_tx.hash(), big_tx);
let small_tx = CanonTransaction::new(&small_tx);
let big_tx = CanonTransaction::new(&big_tx);
let unrestricted_consensus = ConsensusParams::new(Network::Unitest, ConsensusFork::BitcoinCore);
let restricted_consensus = ConsensusParams::new(Network::Unitest, ConsensusFork::BitcoinCash(BitcoinCashConsensusParams::new(Network::Unitest)));
// no restrictions
let checker = TransactionSize::new(small_tx, &unrestricted_consensus, 10000000);
assert_eq!(checker.check(), Ok(()));
// big + restricted
let checker = TransactionSize::new(big_tx, &restricted_consensus, 2000000000);
assert_eq!(checker.check(), Ok(()));
// small + restricted
let checker = TransactionSize::new(small_tx, &restricted_consensus, 2000000000);
assert_eq!(checker.check(), Err(TransactionError::MinSize));
}
}

View File

@ -361,16 +361,20 @@ mod tests {
let tx1: Transaction = test_data::TransactionBuilder::with_version(4)
.add_input(&input_tx, 0)
.add_output(40)
.add_output(10).add_output(10).add_output(10)
.add_output(5).add_output(5).add_output(5)
.into();
let tx2: Transaction = test_data::TransactionBuilder::with_version(1)
.add_input(&tx1, 0)
.add_output(30)
.add_output(1).add_output(1).add_output(1)
.add_output(2).add_output(2).add_output(2)
.into();
assert!(tx1.hash() > tx2.hash());
let block = test_data::block_builder()
.transaction()
.coinbase()
.output().value(2).build()
.output().value(2).script_pubkey_with_sigops(100).build()
.build()
.with_transaction(tx2)
.with_transaction(tx1)

View File

@ -80,6 +80,8 @@ pub enum TransactionError {
CoinbaseSignatureLength(usize),
/// Transaction size exceeds block size limit
MaxSize,
/// Transaction size is below min size limit
MinSize,
/// Transaction has more sigops than it's allowed
MaxSigops,
/// Transaction is a part of memory pool, but is a coinbase