uahf: OP_RETURN replay protection
This commit is contained in:
parent
6110d94544
commit
d6b9445344
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"db 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)",
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"network 0.1.0",
|
"network 0.1.0",
|
||||||
"parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -2,25 +2,6 @@ use std::cmp::max;
|
||||||
use hash::H256;
|
use hash::H256;
|
||||||
use {Magic, Deployment};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
/// Parameters that influence chain consensus.
|
/// Parameters that influence chain consensus.
|
||||||
pub struct ConsensusParams {
|
pub struct ConsensusParams {
|
||||||
|
@ -50,6 +31,25 @@ pub struct ConsensusParams {
|
||||||
pub segwit_deployment: Option<Deployment>,
|
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 {
|
impl ConsensusParams {
|
||||||
pub fn new(magic: Magic, fork: ConsensusFork) -> Self {
|
pub fn new(magic: Magic, fork: ConsensusFork) -> Self {
|
||||||
match magic {
|
match magic {
|
||||||
|
|
|
@ -126,4 +126,9 @@ impl Builder {
|
||||||
pub fn into_script(self) -> Script {
|
pub fn into_script(self) -> Script {
|
||||||
Script::new(self.data)
|
Script::new(self.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds final script bytes
|
||||||
|
pub fn into_bytes(self) -> Bytes {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
lazy_static = "0.2"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
rayon = "0.7"
|
rayon = "0.7"
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
|
use primitives::bytes::Bytes;
|
||||||
use db::{TransactionMetaProvider, TransactionOutputProvider, BlockHeaderProvider};
|
use db::{TransactionMetaProvider, TransactionOutputProvider, BlockHeaderProvider};
|
||||||
use network::ConsensusParams;
|
use network::{ConsensusParams, ConsensusFork};
|
||||||
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
|
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
|
||||||
use duplex_store::DuplexTransactionOutputProvider;
|
use duplex_store::DuplexTransactionOutputProvider;
|
||||||
use deployments::Deployments;
|
use deployments::Deployments;
|
||||||
|
use script::Builder;
|
||||||
use sigops::transaction_sigops;
|
use sigops::transaction_sigops;
|
||||||
use canon::CanonTransaction;
|
use canon::CanonTransaction;
|
||||||
use constants::{COINBASE_MATURITY};
|
use constants::{COINBASE_MATURITY};
|
||||||
|
@ -15,6 +17,7 @@ pub struct TransactionAcceptor<'a> {
|
||||||
pub maturity: TransactionMaturity<'a>,
|
pub maturity: TransactionMaturity<'a>,
|
||||||
pub overspent: TransactionOverspent<'a>,
|
pub overspent: TransactionOverspent<'a>,
|
||||||
pub double_spent: TransactionDoubleSpend<'a>,
|
pub double_spent: TransactionDoubleSpend<'a>,
|
||||||
|
pub return_replay_protection: TransactionReturnReplayProtection<'a>,
|
||||||
pub eval: TransactionEval<'a>,
|
pub eval: TransactionEval<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +44,7 @@ impl<'a> TransactionAcceptor<'a> {
|
||||||
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||||
overspent: TransactionOverspent::new(transaction, output_store),
|
overspent: TransactionOverspent::new(transaction, output_store),
|
||||||
double_spent: TransactionDoubleSpend::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),
|
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.maturity.check());
|
||||||
try!(self.overspent.check());
|
try!(self.overspent.check());
|
||||||
try!(self.double_spent.check());
|
try!(self.double_spent.check());
|
||||||
|
try!(self.return_replay_protection.check());
|
||||||
try!(self.eval.check());
|
try!(self.eval.check());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -62,6 +67,7 @@ pub struct MemoryPoolTransactionAcceptor<'a> {
|
||||||
pub overspent: TransactionOverspent<'a>,
|
pub overspent: TransactionOverspent<'a>,
|
||||||
pub sigops: TransactionSigops<'a>,
|
pub sigops: TransactionSigops<'a>,
|
||||||
pub double_spent: TransactionDoubleSpend<'a>,
|
pub double_spent: TransactionDoubleSpend<'a>,
|
||||||
|
pub return_replay_protection: TransactionReturnReplayProtection<'a>,
|
||||||
pub eval: TransactionEval<'a>,
|
pub eval: TransactionEval<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +93,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
||||||
overspent: TransactionOverspent::new(transaction, output_store),
|
overspent: TransactionOverspent::new(transaction, output_store),
|
||||||
sigops: TransactionSigops::new(transaction, output_store, consensus, max_block_sigops, time),
|
sigops: TransactionSigops::new(transaction, output_store, consensus, max_block_sigops, time),
|
||||||
double_spent: TransactionDoubleSpend::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),
|
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.overspent.check());
|
||||||
try!(self.sigops.check());
|
try!(self.sigops.check());
|
||||||
try!(self.double_spent.check());
|
try!(self.double_spent.check());
|
||||||
|
try!(self.return_replay_protection.check());
|
||||||
try!(self.eval.check());
|
try!(self.eval.check());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -363,3 +371,39 @@ impl<'a> TransactionDoubleSpend<'a> {
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,5 +93,7 @@ pub enum TransactionError {
|
||||||
UnspentTransactionWithTheSameHash,
|
UnspentTransactionWithTheSameHash,
|
||||||
/// Using output that is surely spent
|
/// Using output that is surely spent
|
||||||
UsingSpentOutput(H256, u32),
|
UsingSpentOutput(H256, u32),
|
||||||
|
/// Transaction, protected using BitcoinCash OP_RETURN replay protection (REQ-6-1).
|
||||||
|
ReturnReplayProtection,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
|
|
||||||
extern crate time;
|
extern crate time;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate rayon;
|
extern crate rayon;
|
||||||
|
|
Loading…
Reference in New Issue