diff --git a/Cargo.lock b/Cargo.lock index 0bc55cb5..28e1b48d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,7 @@ version = "0.1.0" dependencies = [ "chain 0.1.0", "db 0.1.0", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "network 0.1.0", "parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/network/src/consensus.rs b/network/src/consensus.rs index 57831204..ef8a83dc 100644 --- a/network/src/consensus.rs +++ b/network/src/consensus.rs @@ -2,25 +2,6 @@ use std::cmp::max; use hash::H256; use {Magic, Deployment}; -#[derive(Debug, Clone, Copy)] -/// Concurrent consensus rule forks. -pub enum ConsensusFork { - /// No fork. - NoFork, - /// SegWit2x (aka The New York Agreement). - /// Briefly: SegWit + blocks up to 2MB. - /// Technical specification: - /// Segregated Witness (Consensus layer) - https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki - /// Block size increase to 2MB - https://github.com/bitcoin/bips/blob/master/bip-0102.mediawiki - SegWit2x(u32), - /// Bitcoin Cash (aka UAHF). - /// Briefly: no SegWit + blocks up to 8MB + replay protection. - /// Technical specification: - /// UAHF Technical Specification - https://github.com/Bitcoin-UAHF/spec/blob/master/uahf-technical-spec.md - /// BUIP-HF Digest for replay protected signature verification across hard forks - https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md - BitcoinCash(u32), -} - #[derive(Debug, Clone)] /// Parameters that influence chain consensus. pub struct ConsensusParams { @@ -50,6 +31,25 @@ pub struct ConsensusParams { pub segwit_deployment: Option, } +#[derive(Debug, Clone, Copy)] +/// Concurrent consensus rule forks. +pub enum ConsensusFork { + /// No fork. + NoFork, + /// SegWit2x (aka The New York Agreement). + /// Briefly: SegWit + blocks up to 2MB. + /// Technical specification: + /// Segregated Witness (Consensus layer) - https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki + /// Block size increase to 2MB - https://github.com/bitcoin/bips/blob/master/bip-0102.mediawiki + SegWit2x(u32), + /// Bitcoin Cash (aka UAHF). + /// Briefly: no SegWit + blocks up to 8MB + replay protection. + /// Technical specification: + /// UAHF Technical Specification - https://github.com/Bitcoin-UAHF/spec/blob/master/uahf-technical-spec.md + /// BUIP-HF Digest for replay protected signature verification across hard forks - https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md + BitcoinCash(u32), +} + impl ConsensusParams { pub fn new(magic: Magic, fork: ConsensusFork) -> Self { match magic { diff --git a/script/src/builder.rs b/script/src/builder.rs index 33f6c052..6f4f0bf7 100644 --- a/script/src/builder.rs +++ b/script/src/builder.rs @@ -126,4 +126,9 @@ impl Builder { pub fn into_script(self) -> Script { Script::new(self.data) } + + /// Builds final script bytes + pub fn into_bytes(self) -> Bytes { + self.data + } } diff --git a/verification/Cargo.toml b/verification/Cargo.toml index 69e3125c..7cd07b2c 100644 --- a/verification/Cargo.toml +++ b/verification/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Nikolay Volf "] [dependencies] time = "0.1" +lazy_static = "0.2" log = "0.3" rayon = "0.7" parking_lot = "0.4" diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index f75119b6..80451057 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -1,9 +1,11 @@ use primitives::hash::H256; +use primitives::bytes::Bytes; use db::{TransactionMetaProvider, TransactionOutputProvider, BlockHeaderProvider}; -use network::ConsensusParams; +use network::{ConsensusParams, ConsensusFork}; use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner}; use duplex_store::DuplexTransactionOutputProvider; use deployments::Deployments; +use script::Builder; use sigops::transaction_sigops; use canon::CanonTransaction; use constants::{COINBASE_MATURITY}; @@ -15,6 +17,7 @@ pub struct TransactionAcceptor<'a> { pub maturity: TransactionMaturity<'a>, pub overspent: TransactionOverspent<'a>, pub double_spent: TransactionDoubleSpend<'a>, + pub return_replay_protection: TransactionReturnReplayProtection<'a>, pub eval: TransactionEval<'a>, } @@ -41,6 +44,7 @@ impl<'a> TransactionAcceptor<'a> { maturity: TransactionMaturity::new(transaction, meta_store, height), overspent: TransactionOverspent::new(transaction, output_store), double_spent: TransactionDoubleSpend::new(transaction, output_store), + return_replay_protection: TransactionReturnReplayProtection::new(transaction, consensus, height), eval: TransactionEval::new(transaction, output_store, consensus, height, time, deployments, headers), } } @@ -51,6 +55,7 @@ impl<'a> TransactionAcceptor<'a> { try!(self.maturity.check()); try!(self.overspent.check()); try!(self.double_spent.check()); + try!(self.return_replay_protection.check()); try!(self.eval.check()); Ok(()) } @@ -62,6 +67,7 @@ pub struct MemoryPoolTransactionAcceptor<'a> { pub overspent: TransactionOverspent<'a>, pub sigops: TransactionSigops<'a>, pub double_spent: TransactionDoubleSpend<'a>, + pub return_replay_protection: TransactionReturnReplayProtection<'a>, pub eval: TransactionEval<'a>, } @@ -87,6 +93,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> { overspent: TransactionOverspent::new(transaction, output_store), sigops: TransactionSigops::new(transaction, output_store, consensus, max_block_sigops, time), double_spent: TransactionDoubleSpend::new(transaction, output_store), + return_replay_protection: TransactionReturnReplayProtection::new(transaction, consensus, height), eval: TransactionEval::new(transaction, output_store, consensus, height, time, deployments, headers), } } @@ -99,6 +106,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> { try!(self.overspent.check()); try!(self.sigops.check()); try!(self.double_spent.check()); + try!(self.return_replay_protection.check()); try!(self.eval.check()); Ok(()) } @@ -363,3 +371,39 @@ impl<'a> TransactionDoubleSpend<'a> { Ok(()) } } + +pub struct TransactionReturnReplayProtection<'a> { + transaction: CanonTransaction<'a>, + consensus: &'a ConsensusParams, + height: u32, +} + +lazy_static! { + pub static ref BITCOIN_CASH_RETURN_REPLAY_PROTECTION_SCRIPT: Bytes = Builder::default() + .return_bytes(b"Bitcoin: A Peer-to-Peer Electronic Cash System") // TODO: check that data.size() == 46 + .into_bytes(); +} + +impl<'a> TransactionReturnReplayProtection<'a> { + fn new(transaction: CanonTransaction<'a>, consensus: &'a ConsensusParams, height: u32) -> Self { + TransactionReturnReplayProtection { + transaction: transaction, + consensus: consensus, + height: height, + } + } + + fn check(&self) -> Result<(), TransactionError> { + if let ConsensusFork::BitcoinCash(fork_block) = self.consensus.fork { + // Transactions with such OP_RETURNs shall be considered valid again for block 530,001 and onwards + if self.height >= fork_block && self.height <= 530_000 { + if (*self.transaction).raw.outputs.iter() + .any(|out| out.script_pubkey == *BITCOIN_CASH_RETURN_REPLAY_PROTECTION_SCRIPT) { + return Err(TransactionError::ReturnReplayProtection) + } + } + } + + Ok(()) + } +} diff --git a/verification/src/error.rs b/verification/src/error.rs index 5c9ac4d5..1647e722 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -93,5 +93,7 @@ pub enum TransactionError { UnspentTransactionWithTheSameHash, /// Using output that is surely spent UsingSpentOutput(H256, u32), + /// Transaction, protected using BitcoinCash OP_RETURN replay protection (REQ-6-1). + ReturnReplayProtection, } diff --git a/verification/src/lib.rs b/verification/src/lib.rs index d26ddd37..56fcfd8f 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -53,6 +53,8 @@ extern crate time; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate log; extern crate parking_lot; extern crate rayon;