Merge branch 'master' into support_bip65
This commit is contained in:
commit
14e043ce23
|
@ -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)",
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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())));
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue