Merge branch 'master' into clippy
Conflicts: src/service.rs src/views.rs
This commit is contained in:
commit
db6e6a3e58
@ -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"]
|
||||||
|
@ -6,4 +6,4 @@ authors = ["arkpar <arkadiy@ethcore.io"]
|
|||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tiny-keccak = "1.0"
|
sha3 = { path = "../util/sha3" }
|
||||||
|
@ -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]
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
34
src/block.rs
34
src/block.rs
@ -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: OverlayDB, 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: OverlayDB, 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: OverlayDB, 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: OverlayDB, 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: OverlayDB, parent: &Header, last_hashes: &LastHashes) -> Result<SealedBlock, Error> {
|
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: OverlayDB, 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]
|
||||||
|
@ -88,7 +88,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;
|
||||||
@ -152,12 +152,26 @@ 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);
|
||||||
|
if blocks.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for block in blocks {
|
||||||
|
if bad.contains(&block.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);
|
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) {
|
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
|
||||||
@ -165,6 +179,7 @@ impl Client {
|
|||||||
None => {
|
None => {
|
||||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), 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(&header.hash());
|
self.queue.mark_as_bad(&header.hash());
|
||||||
|
bad.insert(block.header.hash());
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -181,10 +196,11 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
self.queue.mark_as_bad(&header.hash());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -195,7 +211,7 @@ impl Client {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chain.write().unwrap().insert_block(&bytes); //TODO: err here?
|
self.chain.write().unwrap().insert_block(&block.bytes); //TODO: err here?
|
||||||
match result.drain().commit() {
|
match result.drain().commit() {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -206,6 +222,7 @@ impl Client {
|
|||||||
info!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
info!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BlockChainClient for Client {
|
impl BlockChainClient for Client {
|
||||||
fn block_header(&self, hash: &H256) -> Option<Bytes> {
|
fn block_header(&self, hash: &H256) -> Option<Bytes> {
|
||||||
@ -261,8 +278,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);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -27,6 +27,14 @@ fn color(instruction: Instruction, name: &'static str) -> String {
|
|||||||
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
|
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! overflowing {
|
||||||
|
($x: expr) => {{
|
||||||
|
let (v, overflow) = $x;
|
||||||
|
if overflow { return Err(evm::Error::OutOfGas); }
|
||||||
|
v
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
type CodePosition = usize;
|
type CodePosition = usize;
|
||||||
type Gas = U256;
|
type Gas = U256;
|
||||||
type ProgramCounter = usize;
|
type ProgramCounter = usize;
|
||||||
@ -136,7 +144,7 @@ trait Memory {
|
|||||||
/// Checks whether offset and size is valid memory range
|
/// Checks whether offset and size is valid memory range
|
||||||
fn is_valid_range(off: usize, size: usize) -> bool {
|
fn is_valid_range(off: usize, size: usize) -> bool {
|
||||||
// When size is zero we haven't actually expanded the memory
|
// When size is zero we haven't actually expanded the memory
|
||||||
let (_a, overflow) = off.overflowing_add(size);
|
let overflow = off.overflowing_add(size).1;
|
||||||
size > 0 && !overflow
|
size > 0 && !overflow
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,15 +243,10 @@ impl<'a> CodeReader<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RequiredMem {
|
|
||||||
Mem(U256),
|
|
||||||
OutOfMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
enum InstructionCost {
|
enum InstructionCost {
|
||||||
Gas(U256),
|
Gas(U256),
|
||||||
GasMem(U256, RequiredMem),
|
GasMem(U256, U256),
|
||||||
GasMemCopy(U256, RequiredMem, U256)
|
GasMemCopy(U256, U256, U256)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum InstructionResult {
|
enum InstructionResult {
|
||||||
@ -373,35 +376,31 @@ impl Interpreter {
|
|||||||
InstructionCost::Gas(U256::from(schedule.sload_gas))
|
InstructionCost::Gas(U256::from(schedule.sload_gas))
|
||||||
},
|
},
|
||||||
instructions::MSTORE => {
|
instructions::MSTORE => {
|
||||||
InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 32))
|
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32)))
|
||||||
},
|
},
|
||||||
instructions::MLOAD => {
|
instructions::MLOAD => {
|
||||||
InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 32))
|
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32)))
|
||||||
},
|
},
|
||||||
instructions::MSTORE8 => {
|
instructions::MSTORE8 => {
|
||||||
InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 1))
|
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1)))
|
||||||
},
|
},
|
||||||
instructions::RETURN => {
|
instructions::RETURN => {
|
||||||
InstructionCost::GasMem(default_gas, self.mem_needed(stack.peek(0), stack.peek(1)))
|
InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
|
||||||
},
|
},
|
||||||
instructions::SHA3 => {
|
instructions::SHA3 => {
|
||||||
match add_u256_usize(stack.peek(1), 31) {
|
let w = overflowing!(add_u256_usize(stack.peek(1), 31));
|
||||||
(_w, true) => InstructionCost::GasMem(U256::zero(), RequiredMem::OutOfMemory),
|
|
||||||
(w, false) => {
|
|
||||||
let words = w >> 5;
|
let words = w >> 5;
|
||||||
let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words);
|
let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words);
|
||||||
InstructionCost::GasMem(gas, self.mem_needed(stack.peek(0), stack.peek(1)))
|
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
instructions::CALLDATACOPY => {
|
instructions::CALLDATACOPY => {
|
||||||
InstructionCost::GasMemCopy(default_gas, self.mem_needed(stack.peek(0), stack.peek(2)), stack.peek(2).clone())
|
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), stack.peek(2).clone())
|
||||||
},
|
},
|
||||||
instructions::CODECOPY => {
|
instructions::CODECOPY => {
|
||||||
InstructionCost::GasMemCopy(default_gas, self.mem_needed(stack.peek(0), stack.peek(2)), stack.peek(2).clone())
|
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), stack.peek(2).clone())
|
||||||
},
|
},
|
||||||
instructions::EXTCODECOPY => {
|
instructions::EXTCODECOPY => {
|
||||||
InstructionCost::GasMemCopy(default_gas, self.mem_needed(stack.peek(1), stack.peek(3)), stack.peek(3).clone())
|
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), stack.peek(3).clone())
|
||||||
},
|
},
|
||||||
instructions::JUMPDEST => {
|
instructions::JUMPDEST => {
|
||||||
InstructionCost::Gas(U256::one())
|
InstructionCost::Gas(U256::one())
|
||||||
@ -409,50 +408,41 @@ impl Interpreter {
|
|||||||
instructions::LOG0...instructions::LOG4 => {
|
instructions::LOG0...instructions::LOG4 => {
|
||||||
let no_of_topics = instructions::get_log_topics(instruction);
|
let no_of_topics = instructions::get_log_topics(instruction);
|
||||||
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
||||||
// TODO [todr] potential overflow of datagass
|
|
||||||
let data_gas = stack.peek(1).clone() * U256::from(schedule.log_data_gas);
|
let data_gas = overflowing!(stack.peek(1).overflowing_mul(U256::from(schedule.log_data_gas)));
|
||||||
let gas = try!(self.gas_add(data_gas, U256::from(log_gas)));
|
let gas = overflowing!(data_gas.overflowing_add(U256::from(log_gas)));
|
||||||
InstructionCost::GasMem(gas, self.mem_needed(stack.peek(0), stack.peek(1)))
|
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
|
||||||
},
|
},
|
||||||
instructions::CALL | instructions::CALLCODE => {
|
instructions::CALL | instructions::CALLCODE => {
|
||||||
match add_u256_usize(stack.peek(0), schedule.call_gas) {
|
let mut gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
|
||||||
(_gas, true) => InstructionCost::GasMem(U256::zero(), RequiredMem::OutOfMemory),
|
let mem = cmp::max(
|
||||||
(mut gas, false) => {
|
try!(self.mem_needed(stack.peek(5), stack.peek(6))),
|
||||||
let mem = self.mem_max(
|
try!(self.mem_needed(stack.peek(3), stack.peek(4)))
|
||||||
self.mem_needed(stack.peek(5), stack.peek(6)),
|
|
||||||
self.mem_needed(stack.peek(3), stack.peek(4))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let address = u256_to_address(stack.peek(1));
|
let address = u256_to_address(stack.peek(1));
|
||||||
|
|
||||||
// TODO [todr] Potential overflows
|
|
||||||
if instruction == instructions::CALL && !ext.exists(&address) {
|
if instruction == instructions::CALL && !ext.exists(&address) {
|
||||||
gas = gas + U256::from(schedule.call_new_account_gas);
|
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_new_account_gas)));
|
||||||
};
|
};
|
||||||
|
|
||||||
if stack.peek(2).clone() > U256::zero() {
|
if stack.peek(2).clone() > U256::zero() {
|
||||||
gas = gas + U256::from(schedule.call_value_transfer_gas)
|
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_value_transfer_gas)));
|
||||||
};
|
};
|
||||||
|
|
||||||
InstructionCost::GasMem(gas,mem)
|
InstructionCost::GasMem(gas,mem)
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
instructions::DELEGATECALL => {
|
instructions::DELEGATECALL => {
|
||||||
match add_u256_usize(stack.peek(0), schedule.call_gas) {
|
let gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
|
||||||
(_gas, true) => InstructionCost::GasMem(U256::zero(), RequiredMem::OutOfMemory),
|
let mem = cmp::max(
|
||||||
(gas, false) => {
|
try!(self.mem_needed(stack.peek(4), stack.peek(5))),
|
||||||
let mem = self.mem_max(
|
try!(self.mem_needed(stack.peek(2), stack.peek(3)))
|
||||||
self.mem_needed(stack.peek(4), stack.peek(5)),
|
|
||||||
self.mem_needed(stack.peek(2), stack.peek(3))
|
|
||||||
);
|
);
|
||||||
InstructionCost::GasMem(gas, mem)
|
InstructionCost::GasMem(gas, mem)
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
instructions::CREATE => {
|
instructions::CREATE => {
|
||||||
let gas = U256::from(schedule.create_gas);
|
let gas = U256::from(schedule.create_gas);
|
||||||
let mem = self.mem_needed(stack.peek(1), stack.peek(2));
|
let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2)));
|
||||||
InstructionCost::GasMem(gas, mem)
|
InstructionCost::GasMem(gas, mem)
|
||||||
},
|
},
|
||||||
instructions::EXP => {
|
instructions::EXP => {
|
||||||
@ -468,84 +458,57 @@ impl Interpreter {
|
|||||||
InstructionCost::Gas(gas) => {
|
InstructionCost::Gas(gas) => {
|
||||||
Ok((gas, 0))
|
Ok((gas, 0))
|
||||||
},
|
},
|
||||||
InstructionCost::GasMem(gas, mem_size) => match mem_size {
|
InstructionCost::GasMem(gas, mem_size) => {
|
||||||
RequiredMem::Mem(mem_size) => {
|
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, mem.size(), &mem_size));
|
||||||
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
|
let gas = overflowing!(gas.overflowing_add(mem_gas));
|
||||||
let gas = try!(self.gas_add(gas, mem_gas));
|
|
||||||
Ok((gas, new_mem_size))
|
Ok((gas, new_mem_size))
|
||||||
},
|
},
|
||||||
RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas)
|
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
|
||||||
},
|
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, mem.size(), &mem_size));
|
||||||
InstructionCost::GasMemCopy(gas, mem_size, copy) => match mem_size {
|
let copy = overflowing!(add_u256_usize(©, 31));
|
||||||
RequiredMem::Mem(mem_size) => {
|
|
||||||
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
|
|
||||||
match add_u256_usize(©, 31) {
|
|
||||||
(_c, true) => Err(evm::Error::OutOfGas),
|
|
||||||
(copy, false) => {
|
|
||||||
let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32));
|
let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32));
|
||||||
let gas = try!(self.gas_add(try!(self.gas_add(gas, copy_gas)), mem_gas));
|
let gas = overflowing!(gas.overflowing_add(copy_gas));
|
||||||
|
let gas = overflowing!(gas.overflowing_add(mem_gas));
|
||||||
Ok((gas, new_mem_size))
|
Ok((gas, new_mem_size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gas_add(&self, a: U256, b: U256) -> Result<U256, evm::Error> {
|
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> {
|
||||||
match a.overflowing_add(b) {
|
|
||||||
(_val, true) => Err(evm::Error::OutOfGas),
|
|
||||||
(val, false) => Ok(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> (U256, usize) {
|
|
||||||
let gas_for_mem = |mem_size: U256| {
|
let gas_for_mem = |mem_size: U256| {
|
||||||
let s = mem_size >> 5;
|
let s = mem_size >> 5;
|
||||||
s * U256::from(schedule.memory_gas) + s * s / U256::from(schedule.quad_coeff_div)
|
// s * memory_gas + s * s / quad_coeff_div
|
||||||
|
let a = overflowing!(s.overflowing_mul(U256::from(schedule.memory_gas)));
|
||||||
|
// We need to go to U512 to calculate s*s/quad_coeff_div
|
||||||
|
let b = U512::from(s) * U512::from(s) / U512::from(schedule.quad_coeff_div);
|
||||||
|
if b > U512::from(!U256::zero()) {
|
||||||
|
Err(evm::Error::OutOfGas)
|
||||||
|
} else {
|
||||||
|
Ok(overflowing!(a.overflowing_add(U256::from(b))))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let current_mem_size = U256::from(current_mem_size);
|
let current_mem_size = U256::from(current_mem_size);
|
||||||
let req_mem_size_rounded = ((mem_size.clone() + U256::from(31)) >> 5) << 5;
|
let req_mem_size_rounded = (overflowing!(mem_size.overflowing_add(U256::from(31))) >> 5) << 5;
|
||||||
let new_mem_gas = gas_for_mem(U256::from(req_mem_size_rounded));
|
let new_mem_gas = try!(gas_for_mem(U256::from(req_mem_size_rounded)));
|
||||||
let current_mem_gas = gas_for_mem(current_mem_size);
|
let current_mem_gas = try!(gas_for_mem(current_mem_size));
|
||||||
|
|
||||||
(if req_mem_size_rounded > current_mem_size {
|
Ok((if req_mem_size_rounded > current_mem_size {
|
||||||
new_mem_gas - current_mem_gas
|
new_mem_gas - current_mem_gas
|
||||||
} else {
|
} else {
|
||||||
U256::zero()
|
U256::zero()
|
||||||
}, req_mem_size_rounded.low_u64() as usize)
|
}, req_mem_size_rounded.low_u64() as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_max(&self, m_a: RequiredMem, m_b: RequiredMem) -> RequiredMem {
|
fn mem_needed_const(&self, mem: &U256, add: usize) -> Result<U256, evm::Error> {
|
||||||
match (m_a, m_b) {
|
Ok(overflowing!(mem.overflowing_add(U256::from(add))))
|
||||||
(RequiredMem::Mem(a), RequiredMem::Mem(b)) => {
|
|
||||||
RequiredMem::Mem(cmp::max(a, b))
|
|
||||||
},
|
|
||||||
(RequiredMem::OutOfMemory, _) | (_, RequiredMem::OutOfMemory) => {
|
|
||||||
RequiredMem::OutOfMemory
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_needed_const(&self, mem: &U256, add: usize) -> RequiredMem {
|
fn mem_needed(&self, offset: &U256, size: &U256) -> Result<U256, ::evm::Error> {
|
||||||
match mem.overflowing_add(U256::from(add)) {
|
|
||||||
(_, true) => RequiredMem::OutOfMemory,
|
|
||||||
(mem, false) => RequiredMem::Mem(mem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mem_needed(&self, offset: &U256, size: &U256) -> RequiredMem {
|
|
||||||
if self.is_zero(size) {
|
if self.is_zero(size) {
|
||||||
return RequiredMem::Mem(U256::zero());
|
return Ok(U256::zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
match offset.clone().overflowing_add(size.clone()) {
|
Ok(overflowing!(offset.overflowing_add(size.clone())))
|
||||||
(_result, true) => RequiredMem::OutOfMemory,
|
|
||||||
(result, false) => {
|
|
||||||
RequiredMem::Mem(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_instruction(&self,
|
fn exec_instruction(&self,
|
||||||
@ -646,7 +609,6 @@ impl Interpreter {
|
|||||||
|
|
||||||
return match call_result {
|
return match call_result {
|
||||||
MessageCallResult::Success(gas_left) => {
|
MessageCallResult::Success(gas_left) => {
|
||||||
println!("Unused: {}", gas_left);
|
|
||||||
stack.push(U256::one());
|
stack.push(U256::one());
|
||||||
Ok(InstructionResult::UnusedGas(gas_left))
|
Ok(InstructionResult::UnusedGas(gas_left))
|
||||||
},
|
},
|
||||||
@ -906,41 +868,34 @@ impl Interpreter {
|
|||||||
instructions::ADD => {
|
instructions::ADD => {
|
||||||
let a = stack.pop_back();
|
let a = stack.pop_back();
|
||||||
let b = stack.pop_back();
|
let b = stack.pop_back();
|
||||||
let (c, _overflow) = a.overflowing_add(b);
|
stack.push(a.overflowing_add(b).0);
|
||||||
stack.push(c);
|
|
||||||
},
|
},
|
||||||
instructions::MUL => {
|
instructions::MUL => {
|
||||||
let a = stack.pop_back();
|
let a = stack.pop_back();
|
||||||
let b = stack.pop_back();
|
let b = stack.pop_back();
|
||||||
let (c, _overflow) = a.overflowing_mul(b);
|
stack.push(a.overflowing_mul(b).0);
|
||||||
stack.push(c);
|
|
||||||
},
|
},
|
||||||
instructions::SUB => {
|
instructions::SUB => {
|
||||||
let a = stack.pop_back();
|
let a = stack.pop_back();
|
||||||
let b = stack.pop_back();
|
let b = stack.pop_back();
|
||||||
let (c, _overflow) = a.overflowing_sub(b);
|
stack.push(a.overflowing_sub(b).0);
|
||||||
stack.push(c);
|
|
||||||
},
|
},
|
||||||
instructions::DIV => {
|
instructions::DIV => {
|
||||||
let a = stack.pop_back();
|
let a = stack.pop_back();
|
||||||
let b = stack.pop_back();
|
let b = stack.pop_back();
|
||||||
stack.push(match !self.is_zero(&b) {
|
stack.push(if !self.is_zero(&b) {
|
||||||
true => {
|
a.overflowing_div(b).0
|
||||||
let (c, _overflow) = a.overflowing_div(b);
|
} else {
|
||||||
c
|
U256::zero()
|
||||||
},
|
|
||||||
false => U256::zero()
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
instructions::MOD => {
|
instructions::MOD => {
|
||||||
let a = stack.pop_back();
|
let a = stack.pop_back();
|
||||||
let b = stack.pop_back();
|
let b = stack.pop_back();
|
||||||
stack.push(match !self.is_zero(&b) {
|
stack.push(if !self.is_zero(&b) {
|
||||||
true => {
|
a.overflowing_rem(b).0
|
||||||
let (c, _overflow) = a.overflowing_rem(b);
|
} else {
|
||||||
c
|
U256::zero()
|
||||||
},
|
|
||||||
false => U256::zero()
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
instructions::SDIV => {
|
instructions::SDIV => {
|
||||||
@ -954,7 +909,7 @@ impl Interpreter {
|
|||||||
} else if a == min && b == !U256::zero() {
|
} else if a == min && b == !U256::zero() {
|
||||||
min
|
min
|
||||||
} else {
|
} else {
|
||||||
let (c, _overflow) = a.overflowing_div(b);
|
let c = a.overflowing_div(b).0;
|
||||||
set_sign(c, sign_a ^ sign_b)
|
set_sign(c, sign_a ^ sign_b)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -962,10 +917,10 @@ impl Interpreter {
|
|||||||
let ua = stack.pop_back();
|
let ua = stack.pop_back();
|
||||||
let ub = stack.pop_back();
|
let ub = stack.pop_back();
|
||||||
let (a, sign_a) = get_and_reset_sign(ua);
|
let (a, sign_a) = get_and_reset_sign(ua);
|
||||||
let (b, _sign_b) = get_and_reset_sign(ub);
|
let b = get_and_reset_sign(ub).0;
|
||||||
|
|
||||||
stack.push(if !self.is_zero(&b) {
|
stack.push(if !self.is_zero(&b) {
|
||||||
let (c, _overflow) = a.overflowing_rem(b);
|
let c = a.overflowing_rem(b).0;
|
||||||
set_sign(c, sign_a)
|
set_sign(c, sign_a)
|
||||||
} else {
|
} else {
|
||||||
U256::zero()
|
U256::zero()
|
||||||
@ -974,7 +929,7 @@ impl Interpreter {
|
|||||||
instructions::EXP => {
|
instructions::EXP => {
|
||||||
let base = stack.pop_back();
|
let base = stack.pop_back();
|
||||||
let expon = stack.pop_back();
|
let expon = stack.pop_back();
|
||||||
let (res, _overflow) = base.overflowing_pow(expon);
|
let res = base.overflowing_pow(expon).0;
|
||||||
stack.push(res);
|
stack.push(res);
|
||||||
},
|
},
|
||||||
instructions::NOT => {
|
instructions::NOT => {
|
||||||
@ -1052,8 +1007,8 @@ impl Interpreter {
|
|||||||
stack.push(if !self.is_zero(&c) {
|
stack.push(if !self.is_zero(&c) {
|
||||||
// upcast to 512
|
// upcast to 512
|
||||||
let a5 = U512::from(a);
|
let a5 = U512::from(a);
|
||||||
let (res, _overflow) = a5.overflowing_add(U512::from(b));
|
let res = a5.overflowing_add(U512::from(b)).0;
|
||||||
let (x, _overflow) = res.overflowing_rem(U512::from(c));
|
let x = res.overflowing_rem(U512::from(c)).0;
|
||||||
U256::from(x)
|
U256::from(x)
|
||||||
} else {
|
} else {
|
||||||
U256::zero()
|
U256::zero()
|
||||||
@ -1066,8 +1021,8 @@ impl Interpreter {
|
|||||||
|
|
||||||
stack.push(if !self.is_zero(&c) {
|
stack.push(if !self.is_zero(&c) {
|
||||||
let a5 = U512::from(a);
|
let a5 = U512::from(a);
|
||||||
let (res, _overflow) = a5.overflowing_mul(U512::from(b));
|
let res = a5.overflowing_mul(U512::from(b)).0;
|
||||||
let (x, _overflow) = res.overflowing_rem(U512::from(c));
|
let x = res.overflowing_rem(U512::from(c)).0;
|
||||||
U256::from(x)
|
U256::from(x)
|
||||||
} else {
|
} else {
|
||||||
U256::zero()
|
U256::zero()
|
||||||
@ -1123,8 +1078,7 @@ fn get_and_reset_sign(value: U256) -> (U256, bool) {
|
|||||||
|
|
||||||
fn set_sign(value: U256, sign: bool) -> U256 {
|
fn set_sign(value: U256, sign: bool) -> U256 {
|
||||||
if sign {
|
if sign {
|
||||||
let (val, _overflow) = (!U256::zero() ^ value).overflowing_add(U256::one());
|
(!U256::zero() ^ value).overflowing_add(U256::one()).0
|
||||||
val
|
|
||||||
} else {
|
} else {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
@ -1145,6 +1099,23 @@ fn address_to_u256(value: Address) -> U256 {
|
|||||||
U256::from(H256::from(value).as_slice())
|
U256::from(H256::from(value).as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mem_gas_cost() {
|
||||||
|
// given
|
||||||
|
let interpreter = Interpreter;
|
||||||
|
let schedule = evm::Schedule::default();
|
||||||
|
let current_mem_size = 5;
|
||||||
|
let mem_size = !U256::zero();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let result = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
|
||||||
|
|
||||||
|
// then
|
||||||
|
if let Ok(_) = result {
|
||||||
|
assert!(false, "Should fail with OutOfGas");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common::*;
|
use common::*;
|
||||||
@ -1173,7 +1144,7 @@ mod tests {
|
|||||||
let mem_size = U256::from(5);
|
let mem_size = U256::from(5);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
|
let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(mem_cost, U256::from(3));
|
assert_eq!(mem_cost, U256::from(3));
|
||||||
|
@ -171,7 +171,7 @@ impl<'a> Executive<'a> {
|
|||||||
|
|
||||||
// at first, transfer value to destination
|
// at first, transfer value to destination
|
||||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
||||||
debug!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||||
|
|
||||||
if self.engine.is_builtin(¶ms.code_address) {
|
if self.engine.is_builtin(¶ms.code_address) {
|
||||||
// if destination is builtin, try to execute it
|
// if destination is builtin, try to execute it
|
||||||
|
@ -82,6 +82,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]
|
||||||
|
222
src/queue.rs
222
src/queue.rs
@ -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::AlreadyQueued);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut verification = self.verification.lock().unwrap();
|
||||||
|
if verification.bad.contains(&header.hash()) {
|
||||||
return Err(ImportError::Bad(None));
|
return Err(ImportError::Bad(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.bad.contains(&header.parent_hash) {
|
if verification.bad.contains(&header.parent_hash) {
|
||||||
self.bad.insert(header.hash());
|
verification.bad.insert(header.hash());
|
||||||
return Err(ImportError::Bad(None));
|
return Err(ImportError::Bad(None));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try!(verify_block_basic(&header, bytes, self.engine.deref().deref()).map_err(|e| {
|
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
||||||
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e);
|
Ok(()) => {
|
||||||
e
|
self.processing.insert(header.hash());
|
||||||
}));
|
self.verification.lock().unwrap().unverified.push_back(UnVerifiedBlock { header: header, bytes: bytes });
|
||||||
try!(verify_block_unordered(&header, bytes, self.engine.deref().deref()).map_err(|e| {
|
self.more_to_verify.notify_all();
|
||||||
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e);
|
},
|
||||||
e
|
Err(err) => {
|
||||||
}));
|
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
||||||
try!(self.message_channel.send(UserMessage(SyncMessage::BlockVerified(bytes.to_vec()))).map_err(|e| Error::from(e)));
|
self.verification.lock().unwrap().bad.insert(header.hash());
|
||||||
|
}
|
||||||
|
}
|
||||||
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -54,8 +54,8 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
|||||||
#[allow(match_ref_pats)]
|
#[allow(match_ref_pats)]
|
||||||
fn message<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>, net_message: &'s mut NetSyncMessage) {
|
fn message<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>, net_message: &'s mut NetSyncMessage) {
|
||||||
if let &mut UserMessage(ref mut message) = net_message {
|
if let &mut UserMessage(ref mut message) = net_message {
|
||||||
if let &mut SyncMessage::BlockVerified(ref mut bytes) = message {
|
if let &mut SyncMessage::BlockVerified= message {
|
||||||
self.client.write().unwrap().import_verified_block(mem::replace(bytes, Bytes::new()));
|
self.client.write().unwrap().import_verified_blocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,10 +150,10 @@ impl State {
|
|||||||
let e = try!(Executive::new(self, env_info, engine).transact(t));
|
let e = try!(Executive::new(self, env_info, engine).transact(t));
|
||||||
//println!("Executed: {:?}", e);
|
//println!("Executed: {:?}", e);
|
||||||
|
|
||||||
debug!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod()));
|
trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod()));
|
||||||
self.commit();
|
self.commit();
|
||||||
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
||||||
debug!("Transaction receipt: {:?}", receipt);
|
trace!("Transaction receipt: {:?}", receipt);
|
||||||
Ok(receipt)
|
Ok(receipt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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>;
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
{
|
||||||
|
let v = BlockView::new(&bytes);
|
||||||
for t in v.transactions() {
|
for t in v.transactions() {
|
||||||
try!(engine.verify_transaction(&t, &header));
|
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.
|
||||||
|
51
src/views.rs
51
src/views.rs
@ -61,6 +61,44 @@ impl<'a> Hashable for TransactionView<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// View onto transaction rlp.
|
||||||
|
pub struct AccountView<'a> {
|
||||||
|
rlp: Rlp<'a>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AccountView<'a> {
|
||||||
|
/// Creates new view onto block from raw bytes.
|
||||||
|
pub fn new(bytes: &'a [u8]) -> AccountView<'a> {
|
||||||
|
AccountView {
|
||||||
|
rlp: Rlp::new(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new view onto block from rlp.
|
||||||
|
pub fn new_from_rlp(rlp: Rlp<'a>) -> AccountView<'a> {
|
||||||
|
AccountView {
|
||||||
|
rlp: rlp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return reference to underlaying rlp.
|
||||||
|
pub fn rlp(&self) -> &Rlp<'a> {
|
||||||
|
&self.rlp
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the nonce field of the transaction.
|
||||||
|
pub fn nonce(&self) -> U256 { self.rlp.val_at(0) }
|
||||||
|
|
||||||
|
/// Get the gas_price field of the transaction.
|
||||||
|
pub fn balance(&self) -> U256 { self.rlp.val_at(1) }
|
||||||
|
|
||||||
|
/// Get the gas field of the transaction.
|
||||||
|
pub fn storage_root(&self) -> H256 { self.rlp.val_at(2) }
|
||||||
|
|
||||||
|
/// Get the value field of the transaction.
|
||||||
|
pub fn code_hash(&self) -> H256 { self.rlp.val_at(3) }
|
||||||
|
}
|
||||||
|
|
||||||
/// View onto block rlp.
|
/// View onto block rlp.
|
||||||
pub struct BlockView<'a> {
|
pub struct BlockView<'a> {
|
||||||
rlp: Rlp<'a>
|
rlp: Rlp<'a>
|
||||||
@ -97,13 +135,13 @@ impl<'a> BlockView<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return List of transactions in given block.
|
/// Return List of transactions in given block.
|
||||||
pub fn transaction_views(&self) -> Vec<TransactionView> {
|
pub fn transactions(&self) -> Vec<Transaction> {
|
||||||
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
|
self.rlp.val_at(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return List of transactions in given block.
|
/// Return List of transactions in given block.
|
||||||
pub fn transactions(&self) -> Vec<Transaction> {
|
pub fn transaction_views(&self) -> Vec<TransactionView> {
|
||||||
self.rlp.val_at(1)
|
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return transaction hashes.
|
/// Return transaction hashes.
|
||||||
@ -116,6 +154,11 @@ impl<'a> BlockView<'a> {
|
|||||||
self.rlp.val_at(2)
|
self.rlp.val_at(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return List of transactions in given block.
|
||||||
|
pub fn uncle_views(&self) -> Vec<HeaderView> {
|
||||||
|
self.rlp.at(2).iter().map(|rlp| HeaderView::new_from_rlp(rlp)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return list of uncle hashes of given block.
|
/// Return list of uncle hashes of given block.
|
||||||
pub fn uncle_hashes(&self) -> Vec<H256> {
|
pub fn uncle_hashes(&self) -> Vec<H256> {
|
||||||
self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect()
|
self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect()
|
||||||
|
@ -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
11
util/sha3/Cargo.toml
Normal 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
4
util/sha3/src/lib.rs
Normal 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;
|
||||||
|
}
|
@ -215,10 +215,14 @@ macro_rules! impl_hash {
|
|||||||
}
|
}
|
||||||
impl fmt::Display for $from {
|
impl fmt::Display for $from {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
for i in self.0[0..3].iter() {
|
for i in self.0[0..2].iter() {
|
||||||
try!(write!(f, "{:02x}", i));
|
try!(write!(f, "{:02x}", i));
|
||||||
}
|
}
|
||||||
write!(f, "…{:02x}", self.0.last().unwrap())
|
try!(write!(f, "…"));
|
||||||
|
for i in self.0[$size - 4..$size].iter() {
|
||||||
|
try!(write!(f, "{:02x}", i));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +548,7 @@ mod tests {
|
|||||||
fn hash() {
|
fn hash() {
|
||||||
let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
|
let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
|
||||||
assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h);
|
assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h);
|
||||||
assert_eq!(format!("{}", h), "012345…ef");
|
assert_eq!(format!("{}", h), "0123…89abcdef");
|
||||||
assert_eq!(format!("{:?}", h), "0123456789abcdef");
|
assert_eq!(format!("{:?}", h), "0123456789abcdef");
|
||||||
assert_eq!(h.hex(), "0123456789abcdef");
|
assert_eq!(h.hex(), "0123456789abcdef");
|
||||||
assert!(h == h);
|
assert!(h == h);
|
||||||
|
@ -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()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,13 +561,13 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
|
|||||||
let port = self.info.config.listen_address.port();
|
let port = self.info.config.listen_address.port();
|
||||||
self.info.listen_port = port;
|
self.info.listen_port = port;
|
||||||
|
|
||||||
// self.add_node("enode://a9a921de2ff09a9a4d38b623c67b2d6b477a8e654ae95d874750cbbcb31b33296496a7b4421934e2629269e180823e52c15c2b19fc59592ec51ffe4f2de76ed7@127.0.0.1:30303");
|
self.add_node("enode://a9a921de2ff09a9a4d38b623c67b2d6b477a8e654ae95d874750cbbcb31b33296496a7b4421934e2629269e180823e52c15c2b19fc59592ec51ffe4f2de76ed7@127.0.0.1:30303");
|
||||||
// GO bootnodes
|
/* // GO bootnodes
|
||||||
self.add_node("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"); // IE
|
self.add_node("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"); // IE
|
||||||
self.add_node("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"); // BR
|
self.add_node("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"); // BR
|
||||||
self.add_node("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"); // SG
|
self.add_node("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"); // SG
|
||||||
// ETH/DEV cpp-ethereum (poc-9.ethdev.com)
|
// ETH/DEV cpp-ethereum (poc-9.ethdev.com)
|
||||||
self.add_node("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303");
|
self.add_node("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303");*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_hup<'s>(&'s mut self, io: &mut IoContext<'s, NetworkIoMessage<Message>>, stream: StreamToken) {
|
fn stream_hup<'s>(&'s mut self, io: &mut IoContext<'s, NetworkIoMessage<Message>>, stream: StreamToken) {
|
||||||
|
@ -34,6 +34,22 @@ pub struct NibbleSlice<'a> {
|
|||||||
offset_encode_suffix: usize,
|
offset_encode_suffix: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct NibbleSliceIterator<'a> {
|
||||||
|
p: &'a NibbleSlice<'a>,
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for NibbleSliceIterator<'a> {
|
||||||
|
type Item = u8;
|
||||||
|
fn next(&mut self) -> Option<u8> {
|
||||||
|
self.i += 1;
|
||||||
|
match self.i <= self.p.len() {
|
||||||
|
true => Some(self.p.at(self.i - 1)),
|
||||||
|
false => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'view> NibbleSlice<'a> where 'a: 'view {
|
impl<'a, 'view> NibbleSlice<'a> where 'a: 'view {
|
||||||
/// Create a new nibble slice with the given byte-slice.
|
/// Create a new nibble slice with the given byte-slice.
|
||||||
pub fn new(data: &[u8]) -> NibbleSlice { NibbleSlice::new_offset(data, 0) }
|
pub fn new(data: &[u8]) -> NibbleSlice { NibbleSlice::new_offset(data, 0) }
|
||||||
@ -41,7 +57,7 @@ impl<'a, 'view> NibbleSlice<'a> where 'a: 'view {
|
|||||||
/// Create a new nibble slice with the given byte-slice with a nibble offset.
|
/// Create a new nibble slice with the given byte-slice with a nibble offset.
|
||||||
pub fn new_offset(data: &'a [u8], offset: usize) -> NibbleSlice { NibbleSlice{data: data, offset: offset, data_encode_suffix: &b""[..], offset_encode_suffix: 0} }
|
pub fn new_offset(data: &'a [u8], offset: usize) -> NibbleSlice { NibbleSlice{data: data, offset: offset, data_encode_suffix: &b""[..], offset_encode_suffix: 0} }
|
||||||
|
|
||||||
///
|
/// Create a composed nibble slice; one followed by the other.
|
||||||
pub fn new_composed(a: &'a NibbleSlice, b: &'a NibbleSlice) -> NibbleSlice<'a> { NibbleSlice{data: a.data, offset: a.offset, data_encode_suffix: b.data, offset_encode_suffix: b.offset} }
|
pub fn new_composed(a: &'a NibbleSlice, b: &'a NibbleSlice) -> NibbleSlice<'a> { NibbleSlice{data: a.data, offset: a.offset, data_encode_suffix: b.data, offset_encode_suffix: b.offset} }
|
||||||
|
|
||||||
/*pub fn new_composed_bytes_offset(a: &NibbleSlice, b: &NibbleSlice) -> (Bytes, usize) {
|
/*pub fn new_composed_bytes_offset(a: &NibbleSlice, b: &NibbleSlice) -> (Bytes, usize) {
|
||||||
@ -60,6 +76,10 @@ impl<'a, 'view> NibbleSlice<'a> where 'a: 'view {
|
|||||||
(r, a.len() + b.len())
|
(r, a.len() + b.len())
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
pub fn iter(&'a self) -> NibbleSliceIterator<'a> {
|
||||||
|
NibbleSliceIterator { p: self, i: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`).
|
/// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`).
|
||||||
pub fn from_encoded(data: &'a [u8]) -> (NibbleSlice, bool) {
|
pub fn from_encoded(data: &'a [u8]) -> (NibbleSlice, bool) {
|
||||||
(Self::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32)
|
(Self::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32)
|
||||||
@ -189,6 +209,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterator() {
|
||||||
|
let n = NibbleSlice::new(D);
|
||||||
|
let mut nibbles: Vec<u8> = vec![];
|
||||||
|
nibbles.extend(n.iter());
|
||||||
|
assert_eq!(nibbles, (0u8..6).collect::<Vec<_>>())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mid() {
|
fn mid() {
|
||||||
let n = NibbleSlice::new(D);
|
let n = NibbleSlice::new(D);
|
||||||
|
@ -70,7 +70,9 @@ impl OverlayDB {
|
|||||||
let mut ret = 0u32;
|
let mut ret = 0u32;
|
||||||
for i in self.overlay.drain().into_iter() {
|
for i in self.overlay.drain().into_iter() {
|
||||||
let (key, (value, rc)) = i;
|
let (key, (value, rc)) = i;
|
||||||
if rc != 0 {
|
// until we figure out state trie pruning, only commit stuff when it has a strictly positive delkta of RCs -
|
||||||
|
// this prevents RCs being reduced to 0 where the DB would pretent that the node had been removed.
|
||||||
|
if rc > 0 {
|
||||||
match self.payload(&key) {
|
match self.payload(&key) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let (back_value, back_rc) = x;
|
let (back_value, back_rc) = x;
|
||||||
|
@ -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.
|
||||||
///
|
///
|
||||||
|
@ -5,7 +5,7 @@ use rlp::*;
|
|||||||
use super::journal::*;
|
use super::journal::*;
|
||||||
|
|
||||||
/// Type of node in the trie and essential information thereof.
|
/// Type of node in the trie and essential information thereof.
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub enum Node<'a> {
|
pub enum Node<'a> {
|
||||||
Empty,
|
Empty,
|
||||||
Leaf(NibbleSlice<'a>, &'a[u8]),
|
Leaf(NibbleSlice<'a>, &'a[u8]),
|
||||||
|
@ -42,7 +42,7 @@ impl<'db> TrieDB<'db> {
|
|||||||
/// Panics, if `root` does not exist
|
/// Panics, if `root` does not exist
|
||||||
pub fn new(db: &'db HashDB, root: &'db H256) -> Self {
|
pub fn new(db: &'db HashDB, root: &'db H256) -> Self {
|
||||||
if !db.exists(root) {
|
if !db.exists(root) {
|
||||||
flush(format!("Trie root not found {}", root));
|
flushln!("TrieDB::new({}): Trie root not found!", root);
|
||||||
panic!("Trie root not found!");
|
panic!("Trie root not found!");
|
||||||
}
|
}
|
||||||
TrieDB {
|
TrieDB {
|
||||||
@ -109,7 +109,12 @@ impl<'db> TrieDB<'db> {
|
|||||||
|
|
||||||
/// Get the root node's RLP.
|
/// Get the root node's RLP.
|
||||||
fn root_node(&self) -> Node {
|
fn root_node(&self) -> Node {
|
||||||
Node::decoded(self.db.lookup(&self.root).expect("Trie root not found!"))
|
Node::decoded(self.root_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the data of the root node.
|
||||||
|
fn root_data(&self) -> &[u8] {
|
||||||
|
self.db.lookup(&self.root).expect("Trie root not found!")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the root node as a `Node`.
|
/// Get the root node as a `Node`.
|
||||||
@ -198,6 +203,122 @@ impl<'db> TrieDB<'db> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
enum Status {
|
||||||
|
Entering,
|
||||||
|
At,
|
||||||
|
AtChild(usize),
|
||||||
|
Exiting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
struct Crumb<'a> {
|
||||||
|
node: Node<'a>,
|
||||||
|
// key: &'a[u8],
|
||||||
|
status: Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Crumb<'a> {
|
||||||
|
/// Move on to next status in the node's sequence.
|
||||||
|
fn increment(&mut self) {
|
||||||
|
self.status = match (&self.status, &self.node) {
|
||||||
|
(_, &Node::Empty) => Status::Exiting,
|
||||||
|
(&Status::Entering, _) => Status::At,
|
||||||
|
(&Status::At, &Node::Branch(_, _)) => Status::AtChild(0),
|
||||||
|
(&Status::AtChild(x), &Node::Branch(_, _)) if x < 15 => Status::AtChild(x + 1),
|
||||||
|
_ => Status::Exiting,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator for going through all values in the trie.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TrieDBIterator<'a> {
|
||||||
|
db: &'a TrieDB<'a>,
|
||||||
|
trail: Vec<Crumb<'a>>,
|
||||||
|
key_nibbles: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TrieDBIterator<'a> {
|
||||||
|
/// Create a new iterator.
|
||||||
|
fn new(db: &'a TrieDB) -> TrieDBIterator<'a> {
|
||||||
|
let mut r = TrieDBIterator {
|
||||||
|
db: db,
|
||||||
|
trail: vec![],
|
||||||
|
key_nibbles: Vec::new(),
|
||||||
|
};
|
||||||
|
r.descend(db.root_data());
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Descend into a payload.
|
||||||
|
fn descend(&mut self, d: &'a [u8]) {
|
||||||
|
self.trail.push(Crumb {
|
||||||
|
status: Status::Entering,
|
||||||
|
node: self.db.get_node(d)
|
||||||
|
});
|
||||||
|
match self.trail.last().unwrap().node {
|
||||||
|
Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); },
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Descend into a payload and get the next item.
|
||||||
|
fn descend_next(&mut self, d: &'a [u8]) -> Option<(Bytes, &'a [u8])> { self.descend(d); self.next() }
|
||||||
|
|
||||||
|
/// The present key.
|
||||||
|
fn key(&self) -> Bytes {
|
||||||
|
// collapse the key_nibbles down to bytes.
|
||||||
|
self.key_nibbles.iter().step(2).zip(self.key_nibbles.iter().skip(1).step(2)).map(|(h, l)| h * 16 + l).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for TrieDBIterator<'a> {
|
||||||
|
type Item = (Bytes, &'a [u8]);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let b = match self.trail.last_mut() {
|
||||||
|
Some(ref mut b) => { b.increment(); b.clone() },
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
match (b.status, b.node) {
|
||||||
|
(Status::Exiting, n) => {
|
||||||
|
match n {
|
||||||
|
Node::Leaf(n, _) | Node::Extension(n, _) => {
|
||||||
|
let l = self.key_nibbles.len();
|
||||||
|
self.key_nibbles.truncate(l - n.len());
|
||||||
|
},
|
||||||
|
Node::Branch(_, _) => { self.key_nibbles.pop(); },
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
self.trail.pop();
|
||||||
|
self.next()
|
||||||
|
},
|
||||||
|
(Status::At, Node::Leaf(_, v)) => Some((self.key(), v)),
|
||||||
|
(Status::At, Node::Extension(_, d)) => self.descend_next(d),
|
||||||
|
(Status::At, Node::Branch(_, Some(v))) => Some((self.key(), v)),
|
||||||
|
(Status::At, Node::Branch(_, _)) => self.next(),
|
||||||
|
(Status::AtChild(i), Node::Branch(children, _)) if children[i].len() > 0 => {
|
||||||
|
match i {
|
||||||
|
0 => self.key_nibbles.push(0),
|
||||||
|
i => *self.key_nibbles.last_mut().unwrap() = i as u8,
|
||||||
|
}
|
||||||
|
self.descend_next(children[i])
|
||||||
|
},
|
||||||
|
(Status::AtChild(i), Node::Branch(_, _)) => {
|
||||||
|
if i == 0 { self.key_nibbles.push(0); }
|
||||||
|
self.next()
|
||||||
|
},
|
||||||
|
_ => panic!() // Should never see Entering or AtChild without a Branch here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> TrieDB<'db> {
|
||||||
|
/// Get all keys/values stored in the trie.
|
||||||
|
pub fn iter(&self) -> TrieDBIterator { TrieDBIterator::new(self) }
|
||||||
|
}
|
||||||
|
|
||||||
impl<'db> Trie for TrieDB<'db> {
|
impl<'db> Trie for TrieDB<'db> {
|
||||||
fn root(&self) -> &H256 { &self.root }
|
fn root(&self) -> &H256 { &self.root }
|
||||||
|
|
||||||
@ -218,3 +339,22 @@ impl<'db> fmt::Debug for TrieDB<'db> {
|
|||||||
writeln!(f, "]")
|
writeln!(f, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterator() {
|
||||||
|
use memorydb::*;
|
||||||
|
use super::triedbmut::*;
|
||||||
|
|
||||||
|
let d = vec![ &b"A"[..], &b"AA"[..], &b"AB"[..], &b"B"[..] ];
|
||||||
|
|
||||||
|
let mut memdb = MemoryDB::new();
|
||||||
|
let mut root = H256::new();
|
||||||
|
{
|
||||||
|
let mut t = TrieDBMut::new(&mut memdb, &mut root);
|
||||||
|
for x in &d {
|
||||||
|
t.insert(&x, &x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(d.iter().map(|i|i.to_vec()).collect::<Vec<_>>(), TrieDB::new(&memdb, &root).iter().map(|x|x.0).collect::<Vec<_>>());
|
||||||
|
assert_eq!(d, TrieDB::new(&memdb, &root).iter().map(|x|x.1).collect::<Vec<_>>());
|
||||||
|
}
|
||||||
|
@ -65,8 +65,9 @@ impl<'db> TrieDBMut<'db> {
|
|||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new trie with the backing database `db` and `root`
|
/// Create a new trie with the backing database `db` and `root`.
|
||||||
/// Panics, if `root` does not exist
|
/// Panics, if `root` does not exist.
|
||||||
|
// TODO: return Result<Self, TrieError>
|
||||||
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Self {
|
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Self {
|
||||||
if !db.exists(root) {
|
if !db.exists(root) {
|
||||||
flush(format!("Trie root not found {}", root));
|
flush(format!("Trie root not found {}", root));
|
||||||
|
Loading…
Reference in New Issue
Block a user