uahf: OP_RETURN replay protection

This commit is contained in:
Svyatoslav Nikolsky 2017-08-08 14:41:00 +03:00
parent 6110d94544
commit d6b9445344
7 changed files with 75 additions and 20 deletions

1
Cargo.lock generated
View File

@ -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)",

View File

@ -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<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),
}
impl ConsensusParams {
pub fn new(magic: Magic, fork: ConsensusFork) -> Self {
match magic {

View File

@ -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
}
}

View File

@ -5,6 +5,7 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
[dependencies]
time = "0.1"
lazy_static = "0.2"
log = "0.3"
rayon = "0.7"
parking_lot = "0.4"

View File

@ -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(())
}
}

View File

@ -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,
}

View File

@ -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;