From eef94cc65fee7893d5894a589b582d026285213d Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 8 Apr 2017 20:59:10 +0700 Subject: [PATCH 1/5] fixed bip66 --- network/src/consensus.rs | 13 +++++++++++++ script/src/flags.rs | 5 +++++ verification/src/accept_transaction.rs | 6 +++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/network/src/consensus.rs b/network/src/consensus.rs index 3f45a809..d24c1d13 100644 --- a/network/src/consensus.rs +++ b/network/src/consensus.rs @@ -10,6 +10,9 @@ pub struct ConsensusParams { /// Block height at which BIP65 becomes active. /// See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki pub bip65_height: u32, + /// Block height at which BIP65 becomes active. + /// See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki + pub bip66_height: u32, } impl ConsensusParams { @@ -18,14 +21,17 @@ impl ConsensusParams { Magic::Mainnet | Magic::Other(_) => ConsensusParams { bip16_time: 1333238400, // Apr 1 2012 bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 + bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 }, Magic::Testnet => ConsensusParams { bip16_time: 1333238400, // Apr 1 2012 bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 + bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 }, Magic::Regtest | Magic::Unitest => ConsensusParams { bip16_time: 1333238400, // Apr 1 2012 bip65_height: 1351, + bip66_height: 1251, }, } } @@ -47,4 +53,11 @@ mod tests { assert_eq!(ConsensusParams::with_magic(Magic::Testnet).bip65_height, 581885); assert_eq!(ConsensusParams::with_magic(Magic::Regtest).bip65_height, 1351); } + + #[test] + fn test_consensus_params_bip66_height() { + assert_eq!(ConsensusParams::with_magic(Magic::Mainnet).bip66_height, 363725); + assert_eq!(ConsensusParams::with_magic(Magic::Testnet).bip66_height, 330776); + assert_eq!(ConsensusParams::with_magic(Magic::Regtest).bip66_height, 1251); + } } diff --git a/script/src/flags.rs b/script/src/flags.rs index 9cb90522..97edab54 100644 --- a/script/src/flags.rs +++ b/script/src/flags.rs @@ -77,5 +77,10 @@ impl VerificationFlags { self.verify_clocktimeverify = value; self } + + pub fn verify_dersig(mut self, value: bool) -> Self { + self.verify_dersig = value; + self + } } diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index 151f70d3..70e1e6e9 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -300,6 +300,7 @@ pub struct TransactionEval<'a> { store: DuplexTransactionOutputProvider<'a>, verify_p2sh: bool, verify_clocktime: bool, + verify_dersig: bool, } impl<'a> TransactionEval<'a> { @@ -312,12 +313,14 @@ impl<'a> TransactionEval<'a> { ) -> Self { let verify_p2sh = time >= params.bip16_time; let verify_clocktime = height >= params.bip65_height; + let verify_dersig = height >= params.bip66_height; TransactionEval { transaction: transaction, store: store, verify_p2sh: verify_p2sh, verify_clocktime: verify_clocktime, + verify_dersig: verify_dersig, } } } @@ -346,7 +349,8 @@ impl<'a> TransactionRule for TransactionEval<'a> { let flags = VerificationFlags::default() .verify_p2sh(self.verify_p2sh) - .verify_clocktimeverify(self.verify_clocktime); + .verify_clocktimeverify(self.verify_clocktime) + .verify_dersig(self.verify_dersig); try!(verify_script(&input, &output, &flags, &checker).map_err(|_| TransactionError::Signature(index))); } From 195b8d8b6dce5dbb1aa4773df87b94068e6e56e1 Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 8 Apr 2017 21:16:29 +0700 Subject: [PATCH 2/5] fixed bip90 --- network/src/consensus.rs | 17 +++++++++++++++-- verification/src/accept_header.rs | 20 +++++++++++++------- verification/src/constants.rs | 1 - 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/network/src/consensus.rs b/network/src/consensus.rs index d24c1d13..637e7f76 100644 --- a/network/src/consensus.rs +++ b/network/src/consensus.rs @@ -7,11 +7,14 @@ pub struct ConsensusParams { /// Time when BIP16 becomes active. /// See https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki pub bip16_time: u32, + /// Block height at which BIP34 becomes active. + /// See https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki + pub bip34_height: u32, /// Block height at which BIP65 becomes active. /// See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki pub bip65_height: u32, /// Block height at which BIP65 becomes active. - /// See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki + /// See https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki pub bip66_height: u32, } @@ -20,18 +23,21 @@ impl ConsensusParams { match magic { Magic::Mainnet | Magic::Other(_) => ConsensusParams { bip16_time: 1333238400, // Apr 1 2012 + bip34_height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8 bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 }, Magic::Testnet => ConsensusParams { bip16_time: 1333238400, // Apr 1 2012 + bip34_height: 21111, // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8 bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 }, Magic::Regtest | Magic::Unitest => ConsensusParams { bip16_time: 1333238400, // Apr 1 2012 + bip34_height: 100000000, // not activated on regtest bip65_height: 1351, - bip66_height: 1251, + bip66_height: 1251, // used only in rpc tests }, } } @@ -47,6 +53,13 @@ mod tests { use super::super::Magic; use super::ConsensusParams; + #[test] + fn test_consensus_params_bip34_height() { + assert_eq!(ConsensusParams::with_magic(Magic::Mainnet).bip34_height, 227931); + assert_eq!(ConsensusParams::with_magic(Magic::Testnet).bip34_height, 21111); + assert_eq!(ConsensusParams::with_magic(Magic::Regtest).bip34_height, 100000000); + } + #[test] fn test_consensus_params_bip65_height() { assert_eq!(ConsensusParams::with_magic(Magic::Mainnet).bip65_height, 388381); diff --git a/verification/src/accept_header.rs b/verification/src/accept_header.rs index 7bbbf7e5..d6682890 100644 --- a/verification/src/accept_header.rs +++ b/verification/src/accept_header.rs @@ -1,7 +1,6 @@ -use network::Magic; +use network::{Magic, ConsensusParams}; use db::BlockHeaderProvider; use canon::CanonHeader; -use constants::MIN_BLOCK_VERSION; use error::Error; use work::work_required; use timestamp::median_timestamp; @@ -14,9 +13,10 @@ pub struct HeaderAcceptor<'a> { impl<'a> HeaderAcceptor<'a> { pub fn new(store: &'a BlockHeaderProvider, network: Magic, header: CanonHeader<'a>, height: u32) -> Self { + let params = network.consensus_params(); HeaderAcceptor { // TODO: check last 1000 blocks instead of hardcoding the value - version: HeaderVersion::new(header, MIN_BLOCK_VERSION), + version: HeaderVersion::new(header, height, params), work: HeaderWork::new(header, store, height, network), median_timestamp: HeaderMedianTimestamp::new(header, store, network), } @@ -30,21 +30,27 @@ impl<'a> HeaderAcceptor<'a> { } } +/// Conforms to BIP90 +/// https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki pub struct HeaderVersion<'a> { header: CanonHeader<'a>, - min_version: u32, + height: u32, + consensus_params: ConsensusParams, } impl<'a> HeaderVersion<'a> { - fn new(header: CanonHeader<'a>, min_version: u32) -> Self { + fn new(header: CanonHeader<'a>, height: u32, consensus_params: ConsensusParams) -> Self { HeaderVersion { header: header, - min_version: min_version, + height: height, + consensus_params: consensus_params, } } fn check(&self) -> Result<(), Error> { - if self.header.raw.version < self.min_version { + if (self.header.raw.version < 2 && self.height >= self.consensus_params.bip34_height) || + (self.header.raw.version < 3 && self.height >= self.consensus_params.bip65_height) || + (self.header.raw.version < 4 && self.height >= self.consensus_params.bip66_height) { Err(Error::OldVersionBlock) } else { Ok(()) diff --git a/verification/src/constants.rs b/verification/src/constants.rs index 9c8faab9..6b96d732 100644 --- a/verification/src/constants.rs +++ b/verification/src/constants.rs @@ -6,7 +6,6 @@ pub const MAX_BLOCK_SIZE: usize = 1_000_000; pub const MAX_BLOCK_SIGOPS: usize = 20_000; pub const MIN_COINBASE_SIZE: usize = 2; pub const MAX_COINBASE_SIZE: usize = 100; -pub const MIN_BLOCK_VERSION: u32 = 0; pub const RETARGETING_FACTOR: u32 = 4; pub const TARGET_SPACING_SECONDS: u32 = 10 * 60; From 178c3e565a037c2a378cf4d076f15b49a0e7282a Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 8 Apr 2017 21:36:49 +0700 Subject: [PATCH 3/5] fixed bip34 --- verification/src/accept_block.rs | 41 +++++++++++++++++++++++--- verification/src/error.rs | 2 ++ verification/src/lib.rs | 2 +- verification/src/verify_transaction.rs | 2 +- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index 46ba6480..30373e98 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -1,4 +1,5 @@ use network::{Magic, ConsensusParams}; +use ser::serialize; use db::TransactionOutputProvider; use sigops::transaction_sigops; use work::block_reward_satoshi; @@ -12,6 +13,7 @@ pub struct BlockAcceptor<'a> { pub finality: BlockFinality<'a>, pub sigops: BlockSigops<'a>, pub coinbase_claim: BlockCoinbaseClaim<'a>, + pub coinbase_script: BlockCoinbaseScript<'a>, } impl<'a> BlockAcceptor<'a> { @@ -19,15 +21,17 @@ impl<'a> BlockAcceptor<'a> { let params = network.consensus_params(); BlockAcceptor { finality: BlockFinality::new(block, height), - sigops: BlockSigops::new(block, store, params, MAX_BLOCK_SIGOPS), + coinbase_script: BlockCoinbaseScript::new(block, ¶ms, height), coinbase_claim: BlockCoinbaseClaim::new(block, store, height), + sigops: BlockSigops::new(block, store, params, MAX_BLOCK_SIGOPS), } } pub fn check(&self) -> Result<(), Error> { - try!(self.finality.check()); - try!(self.sigops.check()); - try!(self.coinbase_claim.check()); + self.finality.check()?; + self.sigops.check()?; + self.coinbase_claim.check()?; + self.coinbase_script.check()?; Ok(()) } } @@ -150,3 +154,32 @@ impl<'a> BlockCoinbaseClaim<'a> { } } } + +pub struct BlockCoinbaseScript<'a> { + block: CanonBlock<'a>, + bip34_active: bool, + height: u32, +} + +impl<'a> BlockCoinbaseScript<'a> { + fn new(block: CanonBlock<'a>, consensus_params: &ConsensusParams, height: u32) -> Self { + BlockCoinbaseScript { + block: block, + bip34_active: height >= consensus_params.bip34_height, + height: height, + } + } + + fn check(&self) -> Result<(), Error> { + let matches = !self.bip34_active || self.block.transactions.first() + .and_then(|tx| tx.raw.inputs.first()) + .map(|input| input.script_sig.starts_with(&serialize(&self.height))) + .unwrap_or(false); + + if matches { + Ok(()) + } else { + Err(Error::CoinbaseScript) + } + } +} diff --git a/verification/src/error.rs b/verification/src/error.rs index cacfc4a9..5c9ac4d5 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -27,6 +27,8 @@ pub enum Error { MerkleRoot, /// Coinbase spends too much CoinbaseOverspend { expected_max: u64, actual: u64 }, + /// Coinbase has invalid script sig prefix (BIP90 -> BIP34) + CoinbaseScript, /// Maximum sigops operations exceeded - will not provide how much it was in total /// since it stops counting once `MAX_BLOCK_SIGOPS` is reached MaximumSigops, diff --git a/verification/src/lib.rs b/verification/src/lib.rs index 11edb394..481baf6f 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -60,7 +60,7 @@ extern crate db; extern crate chain; extern crate network; extern crate primitives; -extern crate serialization; +extern crate serialization as ser; extern crate script; pub mod constants; diff --git a/verification/src/verify_transaction.rs b/verification/src/verify_transaction.rs index da677a51..734a74df 100644 --- a/verification/src/verify_transaction.rs +++ b/verification/src/verify_transaction.rs @@ -1,5 +1,5 @@ use std::ops; -use serialization::Serializable; +use ser::Serializable; use chain::IndexedTransaction; use duplex_store::NoopStore; use sigops::transaction_sigops; From f6b7876de21461708e31ad049dc6afa0e128d1cc Mon Sep 17 00:00:00 2001 From: debris Date: Sun, 9 Apr 2017 14:32:03 +0800 Subject: [PATCH 4/5] block builder builds blocks with version 4 --- test-data/src/block.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test-data/src/block.rs b/test-data/src/block.rs index c354819d..f9f3cb72 100644 --- a/test-data/src/block.rs +++ b/test-data/src/block.rs @@ -209,7 +209,8 @@ impl BlockHeaderBuilder where F: Invoke { merkle_root: 0.into(), parent: 0.into(), bits: Compact::max_value(), - version: 1, + // set to 4 to allow creating long test chains + version: 4, } } @@ -575,7 +576,7 @@ fn example5() { .build() .build(); - assert_eq!(hash, "842cee5f58ad1b1caf1896902fc62a0542188a1462372b166ca550acd7fccf1a".into()); + assert_eq!(hash, "3e24319d69a77c58e2da8c7331a21729482835c96834dafb3e1793c1253847c7".into()); assert_eq!(block.header().previous_header_hash, "0000000000000000000000000000000000000000000000000000000000000000".into()); } From 9a96168869ba0923c54d2e056d875af1471a146a Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 11 Apr 2017 13:23:53 +0800 Subject: [PATCH 5/5] tests and fix for block coinbase script validation --- script/src/num.rs | 8 +++++ verification/src/accept_block.rs | 51 ++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/script/src/num.rs b/script/src/num.rs index f6318512..db80b853 100644 --- a/script/src/num.rs +++ b/script/src/num.rs @@ -34,6 +34,14 @@ impl From for Num { } } +impl From for Num { + fn from(i: u32) -> Self { + Num { + value: i as i64 + } + } +} + impl From for Num { fn from(i: usize) -> Self { Num { diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index 30373e98..e65e22b7 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -1,6 +1,6 @@ use network::{Magic, ConsensusParams}; -use ser::serialize; use db::TransactionOutputProvider; +use script; use sigops::transaction_sigops; use work::block_reward_satoshi; use duplex_store::DuplexTransactionOutputProvider; @@ -171,9 +171,17 @@ impl<'a> BlockCoinbaseScript<'a> { } fn check(&self) -> Result<(), Error> { - let matches = !self.bip34_active || self.block.transactions.first() + if !self.bip34_active { + return Ok(()) + } + + let prefix = script::Builder::default() + .push_num(self.height.into()) + .into_script(); + + let matches = self.block.transactions.first() .and_then(|tx| tx.raw.inputs.first()) - .map(|input| input.script_sig.starts_with(&serialize(&self.height))) + .map(|input| input.script_sig.starts_with(&prefix)) .unwrap_or(false); if matches { @@ -183,3 +191,40 @@ impl<'a> BlockCoinbaseScript<'a> { } } } + +#[cfg(test)] +mod tests { + extern crate test_data; + + use {Error, CanonBlock}; + use super::BlockCoinbaseScript; + + #[test] + fn test_block_coinbase_script() { + // transaction from block 461373 + // https://blockchain.info/rawtx/7cf05175ce9c8dbfff9aafa8263edc613fc08f876e476553009afcf7e3868a0c?format=hex + let tx = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3f033d0a070004b663ec58049cba630608733867a0787a02000a425720537570706f727420384d200a666973686572206a696e78696e092f425720506f6f6c2fffffffff01903d9d4e000000001976a914721afdf638d570285d02d3076d8be6a03ee0794d88ac00000000".into(); + let block_number = 461373; + let block = test_data::block_builder() + .with_transaction(tx) + .header().build() + .build() + .into(); + + let coinbase_script_validator = BlockCoinbaseScript { + block: CanonBlock::new(&block), + bip34_active: true, + height: block_number, + }; + + assert_eq!(coinbase_script_validator.check(), Ok(())); + + let coinbase_script_validator2 = BlockCoinbaseScript { + block: CanonBlock::new(&block), + bip34_active: true, + height: block_number - 1, + }; + + assert_eq!(coinbase_script_validator2.check(), Err(Error::CoinbaseScript)); + } +}