Merge pull request #395 from paritytech/bip90

BIP34, BIP66, BIP90
This commit is contained in:
Svyatoslav Nikolsky 2017-04-11 08:42:58 +03:00 committed by GitHub
commit 8f3f793cbe
11 changed files with 146 additions and 17 deletions

View File

@ -7,9 +7,15 @@ pub struct ConsensusParams {
/// Time when BIP16 becomes active. /// Time when BIP16 becomes active.
/// See https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki /// See https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
pub bip16_time: u32, 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. /// 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-0065.mediawiki
pub bip65_height: u32, pub bip65_height: u32,
/// Block height at which BIP65 becomes active.
/// See https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
pub bip66_height: u32,
} }
impl ConsensusParams { impl ConsensusParams {
@ -17,15 +23,21 @@ impl ConsensusParams {
match magic { match magic {
Magic::Mainnet | Magic::Other(_) => ConsensusParams { Magic::Mainnet | Magic::Other(_) => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012 bip16_time: 1333238400, // Apr 1 2012
bip34_height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
}, },
Magic::Testnet => ConsensusParams { Magic::Testnet => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012 bip16_time: 1333238400, // Apr 1 2012
bip34_height: 21111, // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
}, },
Magic::Regtest | Magic::Unitest => ConsensusParams { Magic::Regtest | Magic::Unitest => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012 bip16_time: 1333238400, // Apr 1 2012
bip34_height: 100000000, // not activated on regtest
bip65_height: 1351, bip65_height: 1351,
bip66_height: 1251, // used only in rpc tests
}, },
} }
} }
@ -41,10 +53,24 @@ mod tests {
use super::super::Magic; use super::super::Magic;
use super::ConsensusParams; 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] #[test]
fn test_consensus_params_bip65_height() { fn test_consensus_params_bip65_height() {
assert_eq!(ConsensusParams::with_magic(Magic::Mainnet).bip65_height, 388381); assert_eq!(ConsensusParams::with_magic(Magic::Mainnet).bip65_height, 388381);
assert_eq!(ConsensusParams::with_magic(Magic::Testnet).bip65_height, 581885); assert_eq!(ConsensusParams::with_magic(Magic::Testnet).bip65_height, 581885);
assert_eq!(ConsensusParams::with_magic(Magic::Regtest).bip65_height, 1351); 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);
}
} }

View File

@ -77,5 +77,10 @@ impl VerificationFlags {
self.verify_clocktimeverify = value; self.verify_clocktimeverify = value;
self self
} }
pub fn verify_dersig(mut self, value: bool) -> Self {
self.verify_dersig = value;
self
}
} }

View File

@ -34,6 +34,14 @@ impl From<u8> for Num {
} }
} }
impl From<u32> for Num {
fn from(i: u32) -> Self {
Num {
value: i as i64
}
}
}
impl From<usize> for Num { impl From<usize> for Num {
fn from(i: usize) -> Self { fn from(i: usize) -> Self {
Num { Num {

View File

@ -209,7 +209,8 @@ impl<F> BlockHeaderBuilder<F> where F: Invoke<chain::BlockHeader> {
merkle_root: 0.into(), merkle_root: 0.into(),
parent: 0.into(), parent: 0.into(),
bits: Compact::max_value(), 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()
.build(); .build();
assert_eq!(hash, "842cee5f58ad1b1caf1896902fc62a0542188a1462372b166ca550acd7fccf1a".into()); assert_eq!(hash, "3e24319d69a77c58e2da8c7331a21729482835c96834dafb3e1793c1253847c7".into());
assert_eq!(block.header().previous_header_hash, "0000000000000000000000000000000000000000000000000000000000000000".into()); assert_eq!(block.header().previous_header_hash, "0000000000000000000000000000000000000000000000000000000000000000".into());
} }

View File

@ -1,5 +1,6 @@
use network::{Magic, ConsensusParams}; use network::{Magic, ConsensusParams};
use db::TransactionOutputProvider; use db::TransactionOutputProvider;
use script;
use sigops::transaction_sigops; use sigops::transaction_sigops;
use work::block_reward_satoshi; use work::block_reward_satoshi;
use duplex_store::DuplexTransactionOutputProvider; use duplex_store::DuplexTransactionOutputProvider;
@ -12,6 +13,7 @@ pub struct BlockAcceptor<'a> {
pub finality: BlockFinality<'a>, pub finality: BlockFinality<'a>,
pub sigops: BlockSigops<'a>, pub sigops: BlockSigops<'a>,
pub coinbase_claim: BlockCoinbaseClaim<'a>, pub coinbase_claim: BlockCoinbaseClaim<'a>,
pub coinbase_script: BlockCoinbaseScript<'a>,
} }
impl<'a> BlockAcceptor<'a> { impl<'a> BlockAcceptor<'a> {
@ -19,15 +21,17 @@ impl<'a> BlockAcceptor<'a> {
let params = network.consensus_params(); let params = network.consensus_params();
BlockAcceptor { BlockAcceptor {
finality: BlockFinality::new(block, height), finality: BlockFinality::new(block, height),
sigops: BlockSigops::new(block, store, params, MAX_BLOCK_SIGOPS), coinbase_script: BlockCoinbaseScript::new(block, &params, height),
coinbase_claim: BlockCoinbaseClaim::new(block, store, height), coinbase_claim: BlockCoinbaseClaim::new(block, store, height),
sigops: BlockSigops::new(block, store, params, MAX_BLOCK_SIGOPS),
} }
} }
pub fn check(&self) -> Result<(), Error> { pub fn check(&self) -> Result<(), Error> {
try!(self.finality.check()); self.finality.check()?;
try!(self.sigops.check()); self.sigops.check()?;
try!(self.coinbase_claim.check()); self.coinbase_claim.check()?;
self.coinbase_script.check()?;
Ok(()) Ok(())
} }
} }
@ -150,3 +154,77 @@ 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> {
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(&prefix))
.unwrap_or(false);
if matches {
Ok(())
} else {
Err(Error::CoinbaseScript)
}
}
}
#[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));
}
}

View File

@ -1,7 +1,6 @@
use network::Magic; use network::{Magic, ConsensusParams};
use db::BlockHeaderProvider; use db::BlockHeaderProvider;
use canon::CanonHeader; use canon::CanonHeader;
use constants::MIN_BLOCK_VERSION;
use error::Error; use error::Error;
use work::work_required; use work::work_required;
use timestamp::median_timestamp; use timestamp::median_timestamp;
@ -14,9 +13,10 @@ pub struct HeaderAcceptor<'a> {
impl<'a> HeaderAcceptor<'a> { impl<'a> HeaderAcceptor<'a> {
pub fn new(store: &'a BlockHeaderProvider, network: Magic, header: CanonHeader<'a>, height: u32) -> Self { pub fn new(store: &'a BlockHeaderProvider, network: Magic, header: CanonHeader<'a>, height: u32) -> Self {
let params = network.consensus_params();
HeaderAcceptor { HeaderAcceptor {
// TODO: check last 1000 blocks instead of hardcoding the value // 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), work: HeaderWork::new(header, store, height, network),
median_timestamp: HeaderMedianTimestamp::new(header, store, 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> { pub struct HeaderVersion<'a> {
header: CanonHeader<'a>, header: CanonHeader<'a>,
min_version: u32, height: u32,
consensus_params: ConsensusParams,
} }
impl<'a> HeaderVersion<'a> { 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 { HeaderVersion {
header: header, header: header,
min_version: min_version, height: height,
consensus_params: consensus_params,
} }
} }
fn check(&self) -> Result<(), Error> { 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) Err(Error::OldVersionBlock)
} else { } else {
Ok(()) Ok(())

View File

@ -270,6 +270,7 @@ pub struct TransactionEval<'a> {
store: DuplexTransactionOutputProvider<'a>, store: DuplexTransactionOutputProvider<'a>,
verify_p2sh: bool, verify_p2sh: bool,
verify_clocktime: bool, verify_clocktime: bool,
verify_dersig: bool,
} }
impl<'a> TransactionEval<'a> { impl<'a> TransactionEval<'a> {
@ -282,12 +283,14 @@ impl<'a> TransactionEval<'a> {
) -> Self { ) -> Self {
let verify_p2sh = time >= params.bip16_time; let verify_p2sh = time >= params.bip16_time;
let verify_clocktime = height >= params.bip65_height; let verify_clocktime = height >= params.bip65_height;
let verify_dersig = height >= params.bip66_height;
TransactionEval { TransactionEval {
transaction: transaction, transaction: transaction,
store: store, store: store,
verify_p2sh: verify_p2sh, verify_p2sh: verify_p2sh,
verify_clocktime: verify_clocktime, verify_clocktime: verify_clocktime,
verify_dersig: verify_dersig,
} }
} }
@ -314,7 +317,8 @@ impl<'a> TransactionEval<'a> {
let flags = VerificationFlags::default() let flags = VerificationFlags::default()
.verify_p2sh(self.verify_p2sh) .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))); try!(verify_script(&input, &output, &flags, &checker).map_err(|_| TransactionError::Signature(index)));
} }

View File

@ -6,7 +6,6 @@ pub const MAX_BLOCK_SIZE: usize = 1_000_000;
pub const MAX_BLOCK_SIGOPS: usize = 20_000; 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;
pub const MIN_BLOCK_VERSION: u32 = 0;
pub const RETARGETING_FACTOR: u32 = 4; pub const RETARGETING_FACTOR: u32 = 4;
pub const TARGET_SPACING_SECONDS: u32 = 10 * 60; pub const TARGET_SPACING_SECONDS: u32 = 10 * 60;

View File

@ -27,6 +27,8 @@ pub enum Error {
MerkleRoot, MerkleRoot,
/// Coinbase spends too much /// Coinbase spends too much
CoinbaseOverspend { expected_max: u64, actual: u64 }, 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 /// Maximum sigops operations exceeded - will not provide how much it was in total
/// since it stops counting once `MAX_BLOCK_SIGOPS` is reached /// since it stops counting once `MAX_BLOCK_SIGOPS` is reached
MaximumSigops, MaximumSigops,

View File

@ -60,7 +60,7 @@ extern crate db;
extern crate chain; extern crate chain;
extern crate network; extern crate network;
extern crate primitives; extern crate primitives;
extern crate serialization; extern crate serialization as ser;
extern crate script; extern crate script;
pub mod constants; pub mod constants;

View File

@ -1,5 +1,5 @@
use std::ops; use std::ops;
use serialization::Serializable; use ser::Serializable;
use chain::IndexedTransaction; use chain::IndexedTransaction;
use duplex_store::NoopStore; use duplex_store::NoopStore;
use sigops::transaction_sigops; use sigops::transaction_sigops;