Merge branch 'master' into support_bip65

This commit is contained in:
NikVolf 2016-11-15 14:44:01 +03:00
commit 14e043ce23
8 changed files with 50 additions and 64 deletions

1
Cargo.lock generated
View File

@ -147,6 +147,7 @@ dependencies = [
"chain 0.1.0", "chain 0.1.0",
"elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)", "rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",

View File

@ -14,6 +14,7 @@ serialization = { path = "../serialization" }
parking_lot = "0.3" parking_lot = "0.3"
test-data = { path = "../test-data" } test-data = { path = "../test-data" }
bit-vec = "0.4" bit-vec = "0.4"
log = "0.3"
[features] [features]
dev = [] dev = []

View File

@ -8,6 +8,7 @@ extern crate byteorder;
extern crate chain; extern crate chain;
extern crate serialization; extern crate serialization;
extern crate bit_vec; extern crate bit_vec;
#[macro_use] extern crate log;
#[cfg(test)] #[cfg(test)]
extern crate ethcore_devtools as devtools; extern crate ethcore_devtools as devtools;

View File

@ -341,6 +341,8 @@ impl Storage {
/// all transaction meta is removed /// all transaction meta is removed
/// DOES NOT update best block /// DOES NOT update best block
fn decanonize_block(&self, context: &mut UpdateContext, hash: &H256) -> Result<(), Error> { fn decanonize_block(&self, context: &mut UpdateContext, hash: &H256) -> Result<(), Error> {
trace!(target: "reorg", "Decanonizing block {}", hash);
// ensure that block is of the main chain // ensure that block is of the main chain
try!(self.block_number(hash).ok_or(Error::NotMain(hash.clone()))); try!(self.block_number(hash).ok_or(Error::NotMain(hash.clone())));

View File

@ -104,7 +104,7 @@ impl ChainVerifier {
Ok(()) Ok(())
} }
fn verify_transaction(&self, block: &chain::Block, transaction: &chain::Transaction) -> Result<usize, TransactionError> { fn verify_transaction(&self, block: &chain::Block, transaction: &chain::Transaction) -> Result<(), TransactionError> {
use script::{ use script::{
TransactionInputSigner, TransactionInputSigner,
TransactionSignatureChecker, TransactionSignatureChecker,
@ -113,7 +113,6 @@ impl ChainVerifier {
verify_script, verify_script,
}; };
let mut sigops: usize = 0;
for (input_index, input) in transaction.inputs().iter().enumerate() { for (input_index, input) in transaction.inputs().iter().enumerate() {
let store_parent_transaction = self.store.transaction(&input.previous_output.hash); let store_parent_transaction = self.store.transaction(&input.previous_output.hash);
let parent_transaction = match store_parent_transaction { let parent_transaction = match store_parent_transaction {
@ -139,15 +138,6 @@ impl ChainVerifier {
let input: Script = input.script_sig().to_vec().into(); let input: Script = input.script_sig().to_vec().into();
let output: Script = paired_output.script_pubkey.to_vec().into(); let output: Script = paired_output.script_pubkey.to_vec().into();
sigops +=
try!(output.sigop_count().map_err(|e| TransactionError::ReferenceSignatureMallformed(format!("{}", e)))) +
try!(input.sigop_count().map_err(|e| TransactionError::SignatureMallformed(format!("{}", e))));
if sigops > MAX_BLOCK_SIGOPS {
// already overflown
return Err(TransactionError::SigopsAmount);
}
let flags = VerificationFlags::default() let flags = VerificationFlags::default()
.verify_p2sh(true) .verify_p2sh(true)
.verify_clocktimeverify(self.verify_clocktimeverify); .verify_clocktimeverify(self.verify_clocktimeverify);
@ -156,13 +146,13 @@ impl ChainVerifier {
if self.skip_sig { continue; } if self.skip_sig { continue; }
if let Err(e) = verify_script(&input, &output, &flags, &checker) { if let Err(e) = verify_script(&input, &output, &flags, &checker) {
println!("transaction signature verification failure: {:?}", e); trace!(target: "verification", "transaction signature verification failure: {}", e);
// todo: log error here // todo: log error here
return Err(TransactionError::Signature(input_index)) return Err(TransactionError::Signature(input_index))
} }
} }
Ok(sigops) Ok(())
} }
} }
@ -196,17 +186,31 @@ impl Verify for ChainVerifier {
} }
// verify transactions (except coinbase) // verify transactions (except coinbase)
let mut block_sigops = 0; let mut block_sigops = try!(
for (idx, transaction) in block.transactions().iter().skip(1).enumerate() { utils::transaction_sigops(&block.transactions()[0])
let tx_sigops = try!(self.verify_transaction(block, transaction).map_err(|e| Error::Transaction(idx+1, e))); .map_err(|e| Error::Transaction(1, TransactionError::SignatureMallformed(format!("{}", e))))
block_sigops += tx_sigops; );
if block_sigops >= MAX_BLOCK_SIGOPS { for (idx, transaction) in block.transactions().iter().skip(1).enumerate() {
block_sigops += try!(
utils::transaction_sigops(transaction)
.map_err(|e| Error::Transaction(idx+1, TransactionError::SignatureMallformed(format!("{}", e))))
);
if block_sigops > MAX_BLOCK_SIGOPS {
return Err(Error::MaximumSigops); return Err(Error::MaximumSigops);
} }
try!(self.verify_transaction(block, transaction).map_err(|e| Error::Transaction(idx+1, e)));
} }
trace!(target: "verification", "Block {} total sigops: {}", &hash, &block_sigops); trace!(
target: "verification", "Block {} (transactons: {}, sigops: {}) verification finished",
&hash,
block.transactions().len(),
&block_sigops
);
// todo: pre-process projected block number once verification is parallel! // todo: pre-process projected block number once verification is parallel!
match self.store.accepted_location(block.header()) { match self.store.accepted_location(block.header()) {
@ -489,46 +493,6 @@ mod tests {
assert_eq!(expected, verifier.verify(&block)) assert_eq!(expected, verifier.verify(&block))
} }
#[test]
fn sigops_overflow_tx() {
let path = RandomTempPath::create_dir();
let storage = Storage::new(path.as_path()).unwrap();
let genesis = test_data::block_builder()
.transaction()
.coinbase()
.build()
.transaction()
.output().value(50).build()
.build()
.merkled_header().build()
.build();
storage.insert_block(&genesis).unwrap();
let reference_tx = genesis.transactions()[1].hash();
let mut builder = script::Builder::default();
for _ in 0..21000 {
builder = builder.push_opcode(script::Opcode::OP_CHECKSIG)
}
let block = test_data::block_builder()
.transaction().coinbase().build()
.transaction()
.input()
.hash(reference_tx)
.signature_bytes(builder.into_script().to_bytes())
.build()
.build()
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip();
let expected = Err(Error::Transaction(1, TransactionError::SigopsAmount));
assert_eq!(expected, verifier.verify(&block));
}
#[test] #[test]
fn sigops_overflow_block() { fn sigops_overflow_block() {
let path = RandomTempPath::create_dir(); let path = RandomTempPath::create_dir();

View File

@ -69,10 +69,6 @@ pub enum TransactionError {
Overspend, Overspend,
/// Signature script can't be properly parsed /// Signature script can't be properly parsed
SignatureMallformed(String), SignatureMallformed(String),
/// Signature script in referenced transaction can't be properly parsed
ReferenceSignatureMallformed(String),
/// Too many sigops
SigopsAmount,
} }
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]

View File

@ -157,7 +157,7 @@ impl Queue {
items.push_front(hash, ScheduleItem::Continued(item.block(), num)); items.push_front(hash, ScheduleItem::Continued(item.block(), num));
}, },
Err(e) => { Err(e) => {
println!("Verification failed: {:?}", e); trace!(target: "verification", "Verification of block {} failed: {:?}", &hash, e);
let mut invalid = self.invalid.write(); let mut invalid = self.invalid.write();
let mut processing = self.processing.write(); let mut processing = self.processing.write();

View File

@ -1,6 +1,8 @@
//! Verification utilities //! Verification utilities
use primitives::hash::H256; use primitives::hash::H256;
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use chain;
use script::{self, Script};
pub fn check_nbits(hash: &H256, n_bits: u32) -> bool { pub fn check_nbits(hash: &H256, n_bits: u32) -> bool {
let hash_bytes: &[u8] = &**hash; let hash_bytes: &[u8] = &**hash;
@ -52,6 +54,25 @@ pub fn block_reward_satoshi(block_height: u32) -> u64 {
res res
} }
pub fn transaction_sigops(transaction: &chain::Transaction) -> Result<usize, script::Error> {
let mut result = 0usize;
for output in transaction.outputs.iter() {
let output_script: Script = output.script_pubkey.to_vec().into();
// todo: not always allow malformed output?
result += output_script.sigop_count().unwrap_or(0);
}
if transaction.is_coinbase() { return Ok(result); }
for input in transaction.inputs.iter() {
let input_script: Script = input.script_sig().to_vec().into();
result += try!(input_script.sigop_count());
}
Ok(result)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {