Merge branch 'master' into gav

This commit is contained in:
Gav Wood 2016-01-18 16:20:35 +01:00
commit eec70fe69a
21 changed files with 380 additions and 145 deletions

View File

@ -19,6 +19,7 @@ time = "0.1"
#interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" } #interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" }
evmjit = { path = "rust-evmjit", optional = true } evmjit = { path = "rust-evmjit", optional = true }
ethash = { path = "ethash" } ethash = { path = "ethash" }
num_cpus = "0.2"
[features] [features]
jit = ["evmjit"] jit = ["evmjit"]

View File

@ -6,4 +6,4 @@ authors = ["arkpar <arkadiy@ethcore.io"]
[lib] [lib]
[dependencies] [dependencies]
tiny-keccak = "1.0" sha3 = { path = "../util/sha3" }

View File

@ -6,7 +6,7 @@
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use sizes::{CACHE_SIZES, DAG_SIZES}; use sizes::{CACHE_SIZES, DAG_SIZES};
use tiny_keccak::Keccak; use sha3::{self};
pub const ETHASH_EPOCH_LENGTH: u64 = 30000; pub const ETHASH_EPOCH_LENGTH: u64 = 30000;
pub const ETHASH_CACHE_ROUNDS: usize = 3; pub const ETHASH_CACHE_ROUNDS: usize = 3;
@ -85,9 +85,7 @@ fn fnv_hash(x: u32, y: u32) -> u32 {
#[inline] #[inline]
fn sha3_512(input: &[u8], output: &mut [u8]) { fn sha3_512(input: &[u8], output: &mut [u8]) {
let mut sha3 = Keccak::new_keccak512(); unsafe { sha3::sha3_512(output.as_mut_ptr(), output.len(), input.as_ptr(), input.len()) };
sha3.update(input);
sha3.finalize(output);
} }
#[inline] #[inline]
@ -107,9 +105,7 @@ fn get_seedhash(block_number: u64) -> H256 {
let epochs = block_number / ETHASH_EPOCH_LENGTH; let epochs = block_number / ETHASH_EPOCH_LENGTH;
let mut ret: H256 = [0u8; 32]; let mut ret: H256 = [0u8; 32];
for _ in 0..epochs { for _ in 0..epochs {
let mut sha3 = Keccak::new_keccak256(); unsafe { sha3::sha3_256(ret[..].as_mut_ptr(), 32, ret[..].as_ptr(), 32) };
sha3.update(&ret);
sha3.finalize(&mut ret);
} }
ret ret
} }
@ -125,15 +121,12 @@ pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256) ->
unsafe { ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32) }; unsafe { ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32) };
unsafe { ptr::copy_nonoverlapping(mem::transmute(&nonce), buf[32..].as_mut_ptr(), 8) }; unsafe { ptr::copy_nonoverlapping(mem::transmute(&nonce), buf[32..].as_mut_ptr(), 8) };
let mut sha3 = Keccak::new_keccak512(); unsafe { sha3::sha3_512(buf.as_mut_ptr(), 64, buf.as_ptr(), 40) };
sha3.update(&buf[0..40]);
sha3.finalize(&mut buf);
unsafe { ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32) }; unsafe { ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32) };
let mut hash = [0u8; 32]; let mut hash = [0u8; 32];
let mut sha3 = Keccak::new_keccak256(); unsafe { sha3::sha3_256(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len()) };
sha3.update(&buf); hash.as_mut_ptr();
sha3.finalize(&mut hash);
hash hash
} }
@ -157,10 +150,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64
// compute sha3-512 hash and replicate across mix // compute sha3-512 hash and replicate across mix
unsafe { unsafe {
let mut sha3 = Keccak::new_keccak512(); sha3::sha3_512(s_mix.get_unchecked_mut(0).bytes.as_mut_ptr(), NODE_BYTES, s_mix.get_unchecked(0).bytes.as_ptr(), 40);
sha3.update(&s_mix.get_unchecked(0).bytes[0..40]);
sha3.finalize(&mut s_mix.get_unchecked_mut(0).bytes);
let (f_mix, mut mix) = s_mix.split_at_mut(1); let (f_mix, mut mix) = s_mix.split_at_mut(1);
for w in 0..MIX_WORDS { for w in 0..MIX_WORDS {
*mix.get_unchecked_mut(0).as_words_mut().get_unchecked_mut(w) = *f_mix.get_unchecked(0).as_words().get_unchecked(w % NODE_WORDS); *mix.get_unchecked_mut(0).as_words_mut().get_unchecked_mut(w) = *f_mix.get_unchecked(0).as_words().get_unchecked(w % NODE_WORDS);
@ -189,15 +179,13 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64
*mix.get_unchecked_mut(0).as_words_mut().get_unchecked_mut(i) = reduction; *mix.get_unchecked_mut(0).as_words_mut().get_unchecked_mut(i) = reduction;
} }
let mut mix_hash: H256 = [0u8; 32]; let mut mix_hash = [0u8; 32];
let mut buf = [0u8; 32 + 64];
ptr::copy_nonoverlapping(f_mix.get_unchecked_mut(0).bytes.as_ptr(), buf.as_mut_ptr(), 64);
ptr::copy_nonoverlapping(mix.get_unchecked_mut(0).bytes.as_ptr(), buf[64..].as_mut_ptr(), 32);
ptr::copy_nonoverlapping(mix.get_unchecked_mut(0).bytes.as_ptr(), mix_hash.as_mut_ptr(), 32); ptr::copy_nonoverlapping(mix.get_unchecked_mut(0).bytes.as_ptr(), mix_hash.as_mut_ptr(), 32);
let mut value: H256 = [0u8; 32]; let mut value: H256 = [0u8; 32];
sha3::sha3_256(value.as_mut_ptr(), value.len(), buf.as_ptr(), buf.len());
let mut sha3 = Keccak::new_keccak256();
sha3.update(&f_mix.get_unchecked(0).bytes);
sha3.update(&mix_hash);
sha3.finalize(&mut value);
ProofOfWork { ProofOfWork {
mix_hash: mix_hash, mix_hash: mix_hash,
value: value, value: value,
@ -212,10 +200,7 @@ fn calculate_dag_item(node_index: u32, light: &Light) -> Node {
let init = cache_nodes.get_unchecked(node_index as usize % num_parent_nodes); let init = cache_nodes.get_unchecked(node_index as usize % num_parent_nodes);
let mut ret = init.clone(); let mut ret = init.clone();
*ret.as_words_mut().get_unchecked_mut(0) ^= node_index; *ret.as_words_mut().get_unchecked_mut(0) ^= node_index;
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
let mut sha3 = Keccak::new_keccak512();
sha3.update(&ret.bytes);
sha3.finalize(&mut ret.bytes);
for i in 0..ETHASH_DATASET_PARENTS { for i in 0..ETHASH_DATASET_PARENTS {
let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32; let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32;
@ -224,10 +209,7 @@ fn calculate_dag_item(node_index: u32, light: &Light) -> Node {
*ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w)); *ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w));
} }
} }
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
let mut sha3 = Keccak::new_keccak512();
sha3.update(&ret.bytes);
sha3.finalize(&mut ret.bytes);
ret ret
} }
} }
@ -246,9 +228,7 @@ fn light_new(block_number: u64) -> Light {
unsafe { unsafe {
sha3_512(&seedhash[0..32], &mut nodes.get_unchecked_mut(0).bytes); sha3_512(&seedhash[0..32], &mut nodes.get_unchecked_mut(0).bytes);
for i in 1..num_nodes { for i in 1..num_nodes {
let mut sha3 = Keccak::new_keccak512(); sha3::sha3_512(nodes.get_unchecked_mut(i).bytes.as_mut_ptr(), NODE_BYTES, nodes.get_unchecked(i - 1).bytes.as_ptr(), NODE_BYTES);
sha3.update(&nodes.get_unchecked_mut(i - 1).bytes);
sha3.finalize(&mut nodes.get_unchecked_mut(i).bytes);
} }
for _ in 0..ETHASH_CACHE_ROUNDS { for _ in 0..ETHASH_CACHE_ROUNDS {
@ -275,9 +255,9 @@ fn test_difficulty_test() {
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d ]; let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d ];
let nonce = 0xd7b3ac70a301a249; let nonce = 0xd7b3ac70a301a249;
let boundary_good = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84]; let boundary_good = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
assert!(quick_check_difficulty(&hash, nonce, &mix_hash, &boundary_good)); assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash)[..], boundary_good[..]);
let boundary_bad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84]; let boundary_bad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
assert!(!quick_check_difficulty(&hash, nonce, &mix_hash, &boundary_bad)); assert!(quick_get_difficulty(&hash, nonce, &mix_hash)[..] != boundary_bad[..]);
} }
#[test] #[test]

View File

@ -1,6 +1,6 @@
//! Ethash implementation //! Ethash implementation
//! See https://github.com/ethereum/wiki/wiki/Ethash //! See https://github.com/ethereum/wiki/wiki/Ethash
extern crate tiny_keccak; extern crate sha3;
mod sizes; mod sizes;
mod compute; mod compute;

View File

@ -1,6 +1,7 @@
use common::*; use common::*;
use engine::*; use engine::*;
use state::*; use state::*;
use verification::PreVerifiedBlock;
/// A transaction/receipt execution entry. /// A transaction/receipt execution entry.
pub struct Entry { pub struct Entry {
@ -263,30 +264,39 @@ impl IsBlock for SealedBlock {
fn block(&self) -> &Block { &self.block } fn block(&self) -> &Block { &self.block }
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by block header, transactions and uncles
pub fn enact<'x, 'y>(block_bytes: &[u8], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> { pub fn enact<'x, 'y>(header: &Header, transactions: &[Transaction], uncles: &[Header], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
{ {
let header = BlockView::new(block_bytes).header_view();
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce()); let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author())); trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
} }
let block = BlockView::new(block_bytes); let mut b = OpenBlock::new(engine, db, parent, last_hashes, header.author().clone(), header.extra_data().clone());
let header = block.header_view(); b.set_difficulty(*header.difficulty());
let mut b = OpenBlock::new(engine, db, parent, last_hashes, header.author(), header.extra_data()); b.set_gas_limit(*header.gas_limit());
b.set_difficulty(header.difficulty());
b.set_gas_limit(header.gas_limit());
b.set_timestamp(header.timestamp()); b.set_timestamp(header.timestamp());
// info!("enact: Enacting #{}. env_info={:?}", header.number(), b.env_info()); for t in transactions { try!(b.push_transaction(t.clone(), None)); }
for t in block.transactions().into_iter() { try!(b.push_transaction(t, None)); } for u in uncles { try!(b.push_uncle(u.clone())); }
for u in block.uncles().into_iter() { try!(b.push_uncle(u)); }
Ok(b.close()) Ok(b.close())
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_bytes<'x, 'y>(block_bytes: &[u8], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_verified<'x, 'y>(block: &PreVerifiedBlock, engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: &LastHashes) -> Result<SealedBlock, Error> { pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: &LastHashes) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view(); let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact(block_bytes, engine, db, parent, last_hashes)).seal(header.seal()))) Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(header.seal())))
} }
#[test] #[test]

View File

@ -87,7 +87,7 @@ pub trait BlockChainClient : Sync + Send {
fn block_receipts(&self, hash: &H256) -> Option<Bytes>; fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Import a block into the blockchain. /// Import a block into the blockchain.
fn import_block(&mut self, byte: &[u8]) -> ImportResult; fn import_block(&mut self, bytes: Bytes) -> ImportResult;
/// Get block queue information. /// Get block queue information.
fn queue_status(&self) -> BlockQueueStatus; fn queue_status(&self) -> BlockQueueStatus;
@ -154,59 +154,76 @@ impl Client {
} }
/// This is triggered by a message coming from a block queue when the block is ready for insertion /// This is triggered by a message coming from a block queue when the block is ready for insertion
pub fn import_verified_block(&mut self, bytes: Bytes) { pub fn import_verified_blocks(&mut self) {
let block = BlockView::new(&bytes);
let header = block.header(); let mut bad = HashSet::new();
if let Err(e) = verify_block_family(&header, &bytes, self.engine.deref().deref(), self.chain.read().unwrap().deref()) { let blocks = self.queue.drain(128);
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); if blocks.is_empty() {
self.queue.mark_as_bad(&header.hash());
return; return;
}; }
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
Some(p) => p, for block in blocks {
None => { if bad.contains(&block.header.parent_hash) {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash); self.queue.mark_as_bad(&block.header.hash());
bad.insert(block.header.hash());
continue;
}
let header = &block.header;
if let Err(e) = verify_block_family(&header, &block.bytes, self.engine.deref().deref(), self.chain.read().unwrap().deref()) {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.queue.mark_as_bad(&header.hash()); self.queue.mark_as_bad(&header.hash());
bad.insert(block.header.hash());
return; return;
}, };
}; let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
// build last hashes Some(p) => p,
let mut last_hashes = LastHashes::new(); None => {
last_hashes.resize(256, H256::new()); warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
last_hashes[0] = header.parent_hash.clone(); self.queue.mark_as_bad(&header.hash());
for i in 0..255 { bad.insert(block.header.hash());
match self.chain.read().unwrap().block_details(&last_hashes[i]) { return;
Some(details) => {
last_hashes[i + 1] = details.parent.clone();
}, },
None => break, };
// build last hashes
let mut last_hashes = LastHashes::new();
last_hashes.resize(256, H256::new());
last_hashes[0] = header.parent_hash.clone();
for i in 0..255 {
match self.chain.read().unwrap().block_details(&last_hashes[i]) {
Some(details) => {
last_hashes[i + 1] = details.parent.clone();
},
None => break,
}
} }
}
let result = match enact(&bytes, self.engine.deref().deref(), self.state_db.clone(), &parent, &last_hashes) { let result = match enact_verified(&block, self.engine.deref().deref(), self.state_db.clone(), &parent, &last_hashes) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
bad.insert(block.header.hash());
self.queue.mark_as_bad(&header.hash());
return;
}
};
if let Err(e) = verify_block_final(&header, result.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.queue.mark_as_bad(&header.hash()); self.queue.mark_as_bad(&header.hash());
return; return;
} }
};
if let Err(e) = verify_block_final(&header, result.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.queue.mark_as_bad(&header.hash());
return;
}
self.chain.write().unwrap().insert_block(&bytes); //TODO: err here? self.chain.write().unwrap().insert_block(&block.bytes); //TODO: err here?
let ancient = if header.number() >= HISTORY { Some(header.number() - HISTORY) } else { None }; let ancient = if header.number() >= HISTORY { Some(header.number() - HISTORY) } else { None };
match result.drain().commit(header.number(), &header.hash(), ancient.map(|n|(n, self.chain.read().unwrap().block_hash(n).unwrap()))) { match result.drain().commit(header.number(), &header.hash(), ancient.map(|n|(n, self.chain.read().unwrap().block_hash(n).unwrap()))) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
warn!(target: "client", "State DB commit failed: {:?}", e); warn!(target: "client", "State DB commit failed: {:?}", e);
return; return;
}
} }
info!(target: "client", "Imported #{} ({})", header.number(), header.hash());
} }
debug!(target: "client", "Imported #{} ({})", header.number(), header.hash());
} }
} }
@ -264,8 +281,8 @@ impl BlockChainClient for Client {
unimplemented!(); unimplemented!();
} }
fn import_block(&mut self, bytes: &[u8]) -> ImportResult { fn import_block(&mut self, bytes: Bytes) -> ImportResult {
let header = BlockView::new(bytes).header(); let header = BlockView::new(&bytes).header();
if self.chain.read().unwrap().is_known(&header.hash()) { if self.chain.read().unwrap().is_known(&header.hash()) {
return Err(ImportError::AlreadyInChain); return Err(ImportError::AlreadyInChain);
} }

View File

@ -146,6 +146,10 @@ impl Engine for Ethash {
} }
Ok(()) Ok(())
} }
fn verify_transaction(&self, t: &Transaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
} }
impl Ethash { impl Ethash {

View File

@ -83,6 +83,7 @@ extern crate heapsize;
extern crate crypto; extern crate crypto;
extern crate time; extern crate time;
extern crate env_logger; extern crate env_logger;
extern crate num_cpus;
#[cfg(feature = "jit" )] #[cfg(feature = "jit" )]
extern crate evmjit; extern crate evmjit;
#[macro_use] #[macro_use]

View File

@ -1,59 +1,245 @@
use std::thread::{JoinHandle, self};
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use util::*; use util::*;
use verification::*; use verification::*;
use error::*; use error::*;
use engine::Engine; use engine::Engine;
use sync::*; use sync::*;
use views::*; use views::*;
use header::*;
/// A queue of blocks. Sits between network or other I/O and the BlockChain. /// A queue of blocks. Sits between network or other I/O and the BlockChain.
/// Sorts them ready for blockchain insertion. /// Sorts them ready for blockchain insertion.
pub struct BlockQueue { pub struct BlockQueue {
engine: Arc<Box<Engine>>, engine: Arc<Box<Engine>>,
more_to_verify: Arc<Condvar>,
verification: Arc<Mutex<Verification>>,
verifiers: Vec<JoinHandle<()>>,
deleting: Arc<AtomicBool>,
ready_signal: Arc<QueueSignal>,
processing: HashSet<H256>
}
struct UnVerifiedBlock {
header: Header,
bytes: Bytes,
}
struct VerifyingBlock {
hash: H256,
block: Option<PreVerifiedBlock>,
}
struct QueueSignal {
signalled: AtomicBool,
message_channel: IoChannel<NetSyncMessage>, message_channel: IoChannel<NetSyncMessage>,
}
impl QueueSignal {
fn set(&self) {
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
}
}
fn reset(&self) {
self.signalled.store(false, AtomicOrdering::Relaxed);
}
}
#[derive(Default)]
struct Verification {
unverified: VecDeque<UnVerifiedBlock>,
verified: VecDeque<PreVerifiedBlock>,
verifying: VecDeque<VerifyingBlock>,
bad: HashSet<H256>, bad: HashSet<H256>,
} }
impl BlockQueue { impl BlockQueue {
/// Creates a new queue instance. /// Creates a new queue instance.
pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue { pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
let verification = Arc::new(Mutex::new(Verification::default()));
let more_to_verify = Arc::new(Condvar::new());
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
let deleting = Arc::new(AtomicBool::new(false));
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
let thread_count = max(::num_cpus::get(), 2) - 1;
for _ in 0..thread_count {
let verification = verification.clone();
let engine = engine.clone();
let more_to_verify = more_to_verify.clone();
let ready_signal = ready_signal.clone();
let deleting = deleting.clone();
verifiers.push(thread::spawn(move || BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting)));
}
BlockQueue { BlockQueue {
engine: engine, engine: engine,
message_channel: message_channel, ready_signal: ready_signal.clone(),
bad: HashSet::new(), more_to_verify: more_to_verify.clone(),
verification: verification.clone(),
verifiers: verifiers,
deleting: deleting.clone(),
processing: HashSet::new(),
}
}
fn verify(verification: Arc<Mutex<Verification>>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>) {
while !deleting.load(AtomicOrdering::Relaxed) {
{
let mut lock = verification.lock().unwrap();
while lock.unverified.is_empty() && !deleting.load(AtomicOrdering::Relaxed) {
lock = wait.wait(lock).unwrap();
}
if deleting.load(AtomicOrdering::Relaxed) {
return;
}
}
let block = {
let mut v = verification.lock().unwrap();
if v.unverified.is_empty() {
continue;
}
let block = v.unverified.pop_front().unwrap();
v.verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
block
};
let block_hash = block.header.hash();
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) {
Ok(verified) => {
let mut v = verification.lock().unwrap();
for e in &mut v.verifying {
if e.hash == block_hash {
e.block = Some(verified);
break;
}
}
if !v.verifying.is_empty() && v.verifying.front().unwrap().hash == block_hash {
// we're next!
let mut vref = v.deref_mut();
BlockQueue::drain_verifying(&mut vref.verifying, &mut vref.verified, &mut vref.bad);
ready.set();
}
},
Err(err) => {
let mut v = verification.lock().unwrap();
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
v.bad.insert(block_hash.clone());
v.verifying.retain(|e| e.hash != block_hash);
let mut vref = v.deref_mut();
BlockQueue::drain_verifying(&mut vref.verifying, &mut vref.verified, &mut vref.bad);
ready.set();
}
}
}
}
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreVerifiedBlock>, bad: &mut HashSet<H256>) {
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
let block = verifying.pop_front().unwrap().block.unwrap();
if bad.contains(&block.header.parent_hash) {
bad.insert(block.header.hash());
}
else {
verified.push_back(block);
}
} }
} }
/// Clear the queue and stop verification activity. /// Clear the queue and stop verification activity.
pub fn clear(&mut self) { pub fn clear(&mut self) {
let mut verification = self.verification.lock().unwrap();
verification.unverified.clear();
verification.verifying.clear();
} }
/// Add a block to the queue. /// Add a block to the queue.
pub fn import_block(&mut self, bytes: &[u8]) -> ImportResult { pub fn import_block(&mut self, bytes: Bytes) -> ImportResult {
let header = BlockView::new(bytes).header(); let header = BlockView::new(&bytes).header();
if self.bad.contains(&header.hash()) { if self.processing.contains(&header.hash()) {
return Err(ImportError::Bad(None)); return Err(ImportError::AlreadyQueued);
}
{
let mut verification = self.verification.lock().unwrap();
if verification.bad.contains(&header.hash()) {
return Err(ImportError::Bad(None));
}
if verification.bad.contains(&header.parent_hash) {
verification.bad.insert(header.hash());
return Err(ImportError::Bad(None));
}
} }
if self.bad.contains(&header.parent_hash) { match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
self.bad.insert(header.hash()); Ok(()) => {
return Err(ImportError::Bad(None)); self.processing.insert(header.hash());
self.verification.lock().unwrap().unverified.push_back(UnVerifiedBlock { header: header, bytes: bytes });
self.more_to_verify.notify_all();
},
Err(err) => {
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
self.verification.lock().unwrap().bad.insert(header.hash());
}
} }
try!(verify_block_basic(&header, bytes, self.engine.deref().deref()).map_err(|e| {
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e);
e
}));
try!(verify_block_unordered(&header, bytes, self.engine.deref().deref()).map_err(|e| {
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e);
e
}));
try!(self.message_channel.send(UserMessage(SyncMessage::BlockVerified(bytes.to_vec()))).map_err(|e| Error::from(e)));
Ok(()) Ok(())
} }
/// Mark given block and all its children as bad. Stops verification.
pub fn mark_as_bad(&mut self, hash: &H256) { pub fn mark_as_bad(&mut self, hash: &H256) {
self.bad.insert(hash.clone()); let mut verification_lock = self.verification.lock().unwrap();
//TODO: walk the queue let mut verification = verification_lock.deref_mut();
verification.bad.insert(hash.clone());
let mut new_verified = VecDeque::new();
for block in verification.verified.drain(..) {
if verification.bad.contains(&block.header.parent_hash) {
verification.bad.insert(block.header.hash());
}
else {
new_verified.push_back(block);
}
}
verification.verified = new_verified;
}
pub fn drain(&mut self, max: usize) -> Vec<PreVerifiedBlock> {
let mut verification = self.verification.lock().unwrap();
let count = min(max, verification.verified.len());
let mut result = Vec::with_capacity(count);
for _ in 0..count {
let block = verification.verified.pop_front().unwrap();
self.processing.remove(&block.header.hash());
result.push(block);
}
self.ready_signal.reset();
result
} }
} }
impl Drop for BlockQueue {
fn drop(&mut self) {
self.clear();
self.deleting.store(true, AtomicOrdering::Relaxed);
self.more_to_verify.notify_all();
for t in self.verifiers.drain(..) {
t.join().unwrap();
}
}
}
#[cfg(test)]
mod tests {
use util::*;
use spec::*;
use queue::*;
#[test]
fn test_block_queue() {
// TODO better test
let spec = Spec::new_test();
let engine = spec.to_engine().unwrap();
let _ = BlockQueue::new(Arc::new(engine), IoChannel::disconnected());
}
}

View File

@ -55,8 +55,8 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
match net_message { match net_message {
&mut UserMessage(ref mut message) => { &mut UserMessage(ref mut message) => {
match message { match message {
&mut SyncMessage::BlockVerified(ref mut bytes) => { &mut SyncMessage::BlockVerified => {
self.client.write().unwrap().import_verified_block(mem::replace(bytes, Bytes::new())); self.client.write().unwrap().import_verified_blocks();
}, },
_ => {}, // ignore other messages _ => {}, // ignore other messages
} }

View File

@ -401,7 +401,7 @@ impl ChainSync {
let header_view = HeaderView::new(header_rlp.as_raw()); let header_view = HeaderView::new(header_rlp.as_raw());
// TODO: Decompose block and add to self.headers and self.bodies instead // TODO: Decompose block and add to self.headers and self.bodies instead
if header_view.number() == From::from(self.last_imported_block + 1) { if header_view.number() == From::from(self.last_imported_block + 1) {
match io.chain().import_block(block_rlp.as_raw()) { match io.chain().import_block(block_rlp.as_raw().to_vec()) {
Err(ImportError::AlreadyInChain) => { Err(ImportError::AlreadyInChain) => {
trace!(target: "sync", "New block already in chain {:?}", h); trace!(target: "sync", "New block already in chain {:?}", h);
}, },
@ -655,7 +655,7 @@ impl ChainSync {
block_rlp.append_raw(body.at(0).as_raw(), 1); block_rlp.append_raw(body.at(0).as_raw(), 1);
block_rlp.append_raw(body.at(1).as_raw(), 1); block_rlp.append_raw(body.at(1).as_raw(), 1);
let h = &headers.1[i].hash; let h = &headers.1[i].hash;
match io.chain().import_block(&block_rlp.out()) { match io.chain().import_block(block_rlp.out()) {
Err(ImportError::AlreadyInChain) => { Err(ImportError::AlreadyInChain) => {
trace!(target: "sync", "Block already in chain {:?}", h); trace!(target: "sync", "Block already in chain {:?}", h);
self.last_imported_block = headers.0 + i as BlockNumber; self.last_imported_block = headers.0 + i as BlockNumber;

View File

@ -43,7 +43,7 @@ pub enum SyncMessage {
/// New block has been imported into the blockchain /// New block has been imported into the blockchain
NewChainBlock(Bytes), NewChainBlock(Bytes),
/// A block is ready /// A block is ready
BlockVerified(Bytes), BlockVerified,
} }
pub type NetSyncMessage = NetworkIoMessage<SyncMessage>; pub type NetSyncMessage = NetworkIoMessage<SyncMessage>;

View File

@ -43,7 +43,7 @@ impl TestBlockChainClient {
rlp.append(&header); rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1); rlp.append_raw(&rlp::NULL_RLP, 1);
rlp.append_raw(uncles.as_raw(), 1); rlp.append_raw(uncles.as_raw(), 1);
self.import_block(rlp.as_raw()).unwrap(); self.import_block(rlp.as_raw().to_vec()).unwrap();
} }
} }
} }
@ -110,7 +110,7 @@ impl BlockChainClient for TestBlockChainClient {
None None
} }
fn import_block(&mut self, b: &[u8]) -> ImportResult { fn import_block(&mut self, b: Bytes) -> ImportResult {
let header = Rlp::new(&b).val_at::<BlockHeader>(0); let header = Rlp::new(&b).val_at::<BlockHeader>(0);
let number: usize = header.number as usize; let number: usize = header.number as usize;
if number > self.blocks.len() { if number > self.blocks.len() {
@ -132,7 +132,7 @@ impl BlockChainClient for TestBlockChainClient {
if number == self.numbers.len() { if number == self.numbers.len() {
self.difficulty = self.difficulty + header.difficulty; self.difficulty = self.difficulty + header.difficulty;
self.last_hash = header.hash(); self.last_hash = header.hash();
self.blocks.insert(header.hash(), b.to_vec()); self.blocks.insert(header.hash(), b);
self.numbers.insert(number, header.hash()); self.numbers.insert(number, header.hash());
let mut parent_hash = header.parent_hash; let mut parent_hash = header.parent_hash;
if number > 0 { if number > 0 {

View File

@ -9,6 +9,16 @@ use common::*;
use engine::Engine; use engine::Engine;
use blockchain::*; use blockchain::*;
/// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreVerifiedBlock {
/// Populated block header
pub header: Header,
/// Populated block transactions
pub transactions: Vec<Transaction>,
/// Block bytes
pub bytes: Bytes,
}
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> { pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
try!(verify_header(&header, engine)); try!(verify_header(&header, engine));
@ -29,19 +39,26 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block /// Still operates on a individual block
/// TODO: return cached transactions, header hash. /// Returns a PreVerifiedBlock structure populated with transactions
pub fn verify_block_unordered(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> { pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreVerifiedBlock, Error> {
try!(engine.verify_block_unordered(&header, Some(bytes))); try!(engine.verify_block_unordered(&header, Some(&bytes)));
for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) { for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
try!(engine.verify_block_unordered(&u, None)); try!(engine.verify_block_unordered(&u, None));
} }
// Verify transactions. // Verify transactions.
// TODO: pass in pre-recovered transactions - maybe verify_transaction wants to call `sender()`. let mut transactions = Vec::new();
let v = BlockView::new(bytes); {
for t in v.transactions() { let v = BlockView::new(&bytes);
try!(engine.verify_transaction(&t, &header)); for t in v.transactions() {
try!(engine.verify_transaction(&t, &header));
transactions.push(t);
}
} }
Ok(()) Ok(PreVerifiedBlock {
header: header,
transactions: transactions,
bytes: bytes,
})
} }
/// Phase 3 verification. Check block information against parent and uncles. /// Phase 3 verification. Check block information against parent and uncles.

View File

@ -5,10 +5,6 @@ license = "GPL-3.0"
name = "ethcore-util" name = "ethcore-util"
version = "0.1.0" version = "0.1.0"
authors = ["Ethcore <admin@ethcore.io>"] authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
gcc = "0.3"
[dependencies] [dependencies]
log = "0.3" log = "0.3"
@ -27,6 +23,7 @@ elastic-array = "0.4"
heapsize = "0.2" heapsize = "0.2"
itertools = "0.4" itertools = "0.4"
slab = { git = "https://github.com/arkpar/slab.git" } slab = { git = "https://github.com/arkpar/slab.git" }
sha3 = { path = "sha3" }
[dev-dependencies] [dev-dependencies]
json-tests = { path = "json-tests" } json-tests = { path = "json-tests" }

11
util/sha3/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
description = "Rust bindings for tinykeccak C library"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "sha3"
version = "0.1.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
gcc = "0.3"

4
util/sha3/src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
extern {
pub fn sha3_256(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) -> i32;
pub fn sha3_512(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) -> i32;
}

View File

@ -151,14 +151,22 @@ impl<Message> Handler for IoManager<Message> where Message: Send + 'static {
/// Allows sending messages into the event loop. All the IO handlers will get the message /// Allows sending messages into the event loop. All the IO handlers will get the message
/// in the `message` callback. /// in the `message` callback.
pub struct IoChannel<Message> where Message: Send { pub struct IoChannel<Message> where Message: Send {
channel: Sender<IoMessage<Message>> channel: Option<Sender<IoMessage<Message>>>
} }
impl<Message> IoChannel<Message> where Message: Send { impl<Message> IoChannel<Message> where Message: Send {
pub fn send(&mut self, message: Message) -> Result<(), IoError> { /// Send a msessage through the channel
try!(self.channel.send(IoMessage::UserMessage(message))); pub fn send(&self, message: Message) -> Result<(), IoError> {
if let Some(ref channel) = self.channel {
try!(channel.send(IoMessage::UserMessage(message)));
}
Ok(()) Ok(())
} }
/// Create a new channel to connected to event loop.
pub fn disconnected() -> IoChannel<Message> {
IoChannel { channel: None }
}
} }
/// General IO Service. Starts an event loop and dispatches IO requests. /// General IO Service. Starts an event loop and dispatches IO requests.
@ -198,7 +206,7 @@ impl<Message> IoService<Message> where Message: Send + 'static {
/// Create a new message channel /// Create a new message channel
pub fn channel(&mut self) -> IoChannel<Message> { pub fn channel(&mut self) -> IoChannel<Message> {
IoChannel { channel: self.host_channel.clone() } IoChannel { channel: Some(self.host_channel.clone()) }
} }
} }

View File

@ -1,14 +1,13 @@
//! Wrapper around tiny-keccak crate. //! Wrapper around tiny-keccak crate.
extern crate sha3 as sha3_ext;
use std::mem::uninitialized; use std::mem::uninitialized;
use bytes::{BytesConvertable, Populatable}; use bytes::{BytesConvertable, Populatable};
use hash::{H256, FixedHash}; use hash::{H256, FixedHash};
use self::sha3_ext::*;
pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] ); pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] );
extern {
fn sha3_256(out: *mut u8, outlen: usize, input: *const u8, inputlen: usize) -> i32;
}
/// Types implementing this trait are sha3able. /// Types implementing this trait are sha3able.
/// ///