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",
"elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"primitives 0.1.0",
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",

View File

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

View File

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

View File

@ -341,6 +341,8 @@ impl Storage {
/// all transaction meta is removed
/// DOES NOT update best block
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
try!(self.block_number(hash).ok_or(Error::NotMain(hash.clone())));

View File

@ -104,7 +104,7 @@ impl ChainVerifier {
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::{
TransactionInputSigner,
TransactionSignatureChecker,
@ -113,7 +113,6 @@ impl ChainVerifier {
verify_script,
};
let mut sigops: usize = 0;
for (input_index, input) in transaction.inputs().iter().enumerate() {
let store_parent_transaction = self.store.transaction(&input.previous_output.hash);
let parent_transaction = match store_parent_transaction {
@ -139,15 +138,6 @@ impl ChainVerifier {
let input: Script = input.script_sig().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()
.verify_p2sh(true)
.verify_clocktimeverify(self.verify_clocktimeverify);
@ -156,13 +146,13 @@ impl ChainVerifier {
if self.skip_sig { continue; }
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
return Err(TransactionError::Signature(input_index))
}
}
Ok(sigops)
Ok(())
}
}
@ -196,17 +186,31 @@ impl Verify for ChainVerifier {
}
// verify transactions (except coinbase)
let mut block_sigops = 0;
for (idx, transaction) in block.transactions().iter().skip(1).enumerate() {
let tx_sigops = try!(self.verify_transaction(block, transaction).map_err(|e| Error::Transaction(idx+1, e)));
block_sigops += tx_sigops;
let mut block_sigops = try!(
utils::transaction_sigops(&block.transactions()[0])
.map_err(|e| Error::Transaction(1, TransactionError::SignatureMallformed(format!("{}", e))))
);
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);
}
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!
match self.store.accepted_location(block.header()) {
@ -489,46 +493,6 @@ mod tests {
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]
fn sigops_overflow_block() {
let path = RandomTempPath::create_dir();

View File

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

View File

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

View File

@ -1,6 +1,8 @@
//! Verification utilities
use primitives::hash::H256;
use byteorder::{BigEndian, ByteOrder};
use chain;
use script::{self, Script};
pub fn check_nbits(hash: &H256, n_bits: u32) -> bool {
let hash_bytes: &[u8] = &**hash;
@ -52,6 +54,25 @@ pub fn block_reward_satoshi(block_height: u32) -> u64 {
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)]
mod tests {