Merge remote-tracking branch 'origin/master'

This commit is contained in:
Gav Wood 2016-05-21 14:49:09 +02:00
commit 399dfc4c2e
47 changed files with 1565 additions and 1093 deletions

8
Cargo.lock generated
View File

@ -332,7 +332,7 @@ dependencies = [
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rocksdb 0.4.3 (git+https://github.com/arkpar/rust-rocksdb.git)",
"rocksdb 0.4.3 (git+https://github.com/ethcore/rust-rocksdb)",
"rust-crypto 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -628,7 +628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "librocksdb-sys"
version = "0.2.3"
source = "git+https://github.com/arkpar/rust-rocksdb.git#ae44ef33ed1358ffc79aa05ed77839d555daba33"
source = "git+https://github.com/ethcore/rust-rocksdb#6b6ce93e2828182691e00da57fdfb2926226f1f1"
dependencies = [
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -970,10 +970,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rocksdb"
version = "0.4.3"
source = "git+https://github.com/arkpar/rust-rocksdb.git#ae44ef33ed1358ffc79aa05ed77839d555daba33"
source = "git+https://github.com/ethcore/rust-rocksdb#6b6ce93e2828182691e00da57fdfb2926226f1f1"
dependencies = [
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"librocksdb-sys 0.2.3 (git+https://github.com/arkpar/rust-rocksdb.git)",
"librocksdb-sys 0.2.3 (git+https://github.com/ethcore/rust-rocksdb)",
]
[[package]]

24
db/Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
description = "Ethcore Database"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore-db"
version = "1.2.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
syntex = "*"
ethcore-ipc-codegen = { path = "../ipc/codegen" }
[dependencies]
ethcore-util = { path = "../util" }
clippy = { version = "0.0.67", optional = true}
ethcore-devtools = { path = "../devtools" }
ethcore-ipc = { path = "../ipc/rpc" }
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
semver = "0.2"
ethcore-ipc-nano = { path = "../ipc/nano" }
[features]
dev = ["clippy"]

43
db/build.rs Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate syntex;
extern crate ethcore_ipc_codegen as codegen;
use std::env;
use std::path::Path;
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
// ipc pass
{
let src = Path::new("src/lib.rs.in");
let dst = Path::new(&out_dir).join("lib.intermediate.rs.in");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
// binary serialization pass
{
let src = Path::new(&out_dir).join("lib.intermediate.rs.in");
let dst = Path::new(&out_dir).join("lib.rs");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
}

370
db/src/database.rs Normal file
View File

@ -0,0 +1,370 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Ethcore rocksdb ipc service
use traits::*;
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator,
IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
use std::collections::BTreeMap;
use std::sync::{RwLock};
use std::convert::From;
use ipc::IpcConfig;
use std::ops::*;
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
impl From<String> for Error {
fn from(s: String) -> Error {
Error::RocksDb(s)
}
}
pub struct Database {
db: RwLock<Option<DB>>,
transactions: RwLock<BTreeMap<TransactionHandle, WriteBatch>>,
iterators: RwLock<BTreeMap<IteratorHandle, DBIterator>>,
}
impl Database {
pub fn new() -> Database {
Database {
db: RwLock::new(None),
transactions: RwLock::new(BTreeMap::new()),
iterators: RwLock::new(BTreeMap::new()),
}
}
}
#[derive(Ipc)]
impl DatabaseService for Database {
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
let mut db = self.db.write().unwrap();
if db.is_some() { return Err(Error::AlreadyOpen); }
let mut opts = Options::new();
opts.set_max_open_files(256);
opts.create_if_missing(true);
opts.set_use_fsync(false);
opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction);
if let Some(size) = config.prefix_size {
let mut block_opts = BlockBasedOptions::new();
block_opts.set_index_type(IndexType::HashSearch);
opts.set_block_based_table_factory(&block_opts);
opts.set_prefix_extractor_fixed_size(size);
}
*db = Some(try!(DB::open(&opts, &path)));
Ok(())
}
fn close(&self) -> Result<(), Error> {
let mut db = self.db.write().unwrap();
if db.is_none() { return Err(Error::IsClosed); }
// TODO: wait for transactions to expire/close here?
if self.transactions.read().unwrap().len() > 0 { return Err(Error::UncommitedTransactions); }
*db = None;
Ok(())
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
try!(db.put(key, value));
Ok(())
}
fn delete(&self, key: &[u8]) -> Result<(), Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
try!(db.delete(key));
Ok(())
}
fn write(&self, handle: TransactionHandle) -> Result<(), Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
let mut transactions = self.transactions.write().unwrap();
let batch = try!(
transactions.remove(&handle).ok_or(Error::TransactionUnknown)
);
try!(db.write(batch));
Ok(())
}
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
match try!(db.get(key)) {
Some(db_vec) => Ok(Some(db_vec.to_vec())),
None => Ok(None),
}
}
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
let mut iter = db.iterator(IteratorMode::From(prefix, Direction::Forward));
match iter.next() {
// TODO: use prefix_same_as_start read option (not availabele in C API currently)
Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Ok(Some(v.to_vec())) } else { Ok(None) },
_ => Ok(None)
}
}
fn is_empty(&self) -> Result<bool, Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
Ok(db.iterator(IteratorMode::Start).next().is_none())
}
fn iter(&self) -> Result<IteratorHandle, Error> {
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
let mut iterators = self.iterators.write().unwrap();
let next_iterator = iterators.keys().last().unwrap_or(&0) + 1;
iterators.insert(next_iterator, db.iterator(IteratorMode::Start));
Ok(next_iterator)
}
fn iter_next(&self, handle: IteratorHandle) -> Option<KeyValue>
{
let mut iterators = self.iterators.write().unwrap();
let mut iterator = match iterators.get_mut(&handle) {
Some(some_iterator) => some_iterator,
None => { return None; },
};
iterator.next().and_then(|(some_key, some_val)| {
Some(KeyValue {
key: some_key.to_vec(),
value: some_val.to_vec(),
})
})
}
fn transaction_put(&self, transaction: TransactionHandle, key: &[u8], value: &[u8]) -> Result<(), Error>
{
let mut transactions = self.transactions.write().unwrap();
let batch = try!(
transactions.get_mut(&transaction).ok_or(Error::TransactionUnknown)
);
try!(batch.put(&key, &value));
Ok(())
}
fn transaction_delete(&self, transaction: TransactionHandle, key: &[u8]) -> Result<(), Error> {
let mut transactions = self.transactions.write().unwrap();
let batch = try!(
transactions.get_mut(&transaction).ok_or(Error::TransactionUnknown)
);
try!(batch.delete(&key));
Ok(())
}
fn new_transaction(&self) -> TransactionHandle {
let mut transactions = self.transactions.write().unwrap();
let next_transaction = transactions.keys().last().unwrap_or(&0) + 1;
transactions.insert(next_transaction, WriteBatch::new());
next_transaction
}
}
// TODO : put proper at compile-time
impl IpcConfig for Database {}
#[cfg(test)]
mod test {
use super::Database;
use traits::*;
use devtools::*;
#[test]
fn can_be_created() {
let db = Database::new();
assert!(db.is_empty().is_err());
}
#[test]
fn can_be_open_empty() {
let db = Database::new();
let path = RandomTempPath::create_dir();
db.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
assert!(db.is_empty().is_ok());
}
#[test]
fn can_store_key() {
let db = Database::new();
let path = RandomTempPath::create_dir();
db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap();
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
assert!(!db.is_empty().unwrap());
}
#[test]
fn can_retrieve() {
let db = Database::new();
let path = RandomTempPath::create_dir();
db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap();
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
db.close().unwrap();
db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap();
assert_eq!(db.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
}
}
#[cfg(test)]
mod client_tests {
use super::{DatabaseClient, Database};
use traits::*;
use devtools::*;
use nanoipc;
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicBool};
fn init_worker(addr: &str) -> nanoipc::Worker<Database> {
let mut worker = nanoipc::Worker::<Database>::new(&Arc::new(Database::new()));
worker.add_duplex(addr).unwrap();
worker
}
#[test]
fn can_call_handshake() {
let url = "ipc:///tmp/parity-db-ipc-test-10.ipc";
let worker_should_exit = Arc::new(AtomicBool::new(false));
let worker_is_ready = Arc::new(AtomicBool::new(false));
let c_worker_should_exit = worker_should_exit.clone();
let c_worker_is_ready = worker_is_ready.clone();
::std::thread::spawn(move || {
let mut worker = init_worker(url);
while !c_worker_should_exit.load(Ordering::Relaxed) {
worker.poll();
c_worker_is_ready.store(true, Ordering::Relaxed);
}
});
while !worker_is_ready.load(Ordering::Relaxed) { }
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
let hs = client.handshake();
worker_should_exit.store(true, Ordering::Relaxed);
assert!(hs.is_ok());
}
#[test]
fn can_open_db() {
let url = "ipc:///tmp/parity-db-ipc-test-20.ipc";
let path = RandomTempPath::create_dir();
let worker_should_exit = Arc::new(AtomicBool::new(false));
let worker_is_ready = Arc::new(AtomicBool::new(false));
let c_worker_should_exit = worker_should_exit.clone();
let c_worker_is_ready = worker_is_ready.clone();
::std::thread::spawn(move || {
let mut worker = init_worker(url);
while !c_worker_should_exit.load(Ordering::Relaxed) {
worker.poll();
c_worker_is_ready.store(true, Ordering::Relaxed);
}
});
while !worker_is_ready.load(Ordering::Relaxed) { }
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
assert!(client.is_empty().unwrap());
worker_should_exit.store(true, Ordering::Relaxed);
}
#[test]
fn can_put() {
let url = "ipc:///tmp/parity-db-ipc-test-30.ipc";
let path = RandomTempPath::create_dir();
let worker_should_exit = Arc::new(AtomicBool::new(false));
let worker_is_ready = Arc::new(AtomicBool::new(false));
let c_worker_should_exit = worker_should_exit.clone();
let c_worker_is_ready = worker_is_ready.clone();
::std::thread::spawn(move || {
let mut worker = init_worker(url);
while !c_worker_should_exit.load(Ordering::Relaxed) {
worker.poll();
c_worker_is_ready.store(true, Ordering::Relaxed);
}
});
while !worker_is_ready.load(Ordering::Relaxed) { }
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
client.close().unwrap();
worker_should_exit.store(true, Ordering::Relaxed);
}
#[test]
fn can_put_and_read() {
let url = "ipc:///tmp/parity-db-ipc-test-40.ipc";
let path = RandomTempPath::create_dir();
let worker_should_exit = Arc::new(AtomicBool::new(false));
let worker_is_ready = Arc::new(AtomicBool::new(false));
let c_worker_should_exit = worker_should_exit.clone();
let c_worker_is_ready = worker_is_ready.clone();
::std::thread::spawn(move || {
let mut worker = init_worker(url);
while !c_worker_should_exit.load(Ordering::Relaxed) {
worker.poll();
c_worker_is_ready.store(true, Ordering::Relaxed);
}
});
while !worker_is_ready.load(Ordering::Relaxed) { }
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
client.close().unwrap();
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
worker_should_exit.store(true, Ordering::Relaxed);
}
}

20
db/src/lib.rs Normal file
View File

@ -0,0 +1,20 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Database ipc service
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
include!(concat!(env!("OUT_DIR"), "/lib.rs"));

25
db/src/lib.rs.in Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate ethcore_ipc as ipc;
extern crate rocksdb;
extern crate ethcore_devtools as devtools;
extern crate semver;
extern crate ethcore_ipc_nano as nanoipc;
extern crate ethcore_util as util;
pub mod database;
pub mod traits;

0
db/src/service.rs Normal file
View File

73
db/src/traits.rs Normal file
View File

@ -0,0 +1,73 @@
//! Ethcore database trait
use ipc::BinaryConvertable;
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
pub type TransactionHandle = u32;
pub type IteratorHandle = u32;
#[derive(Binary)]
pub struct KeyValue {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
#[derive(Debug, Binary)]
pub enum Error {
AlreadyOpen,
IsClosed,
RocksDb(String),
TransactionUnknown,
IteratorUnknown,
UncommitedTransactions,
}
/// Database configuration
#[derive(Binary)]
pub struct DatabaseConfig {
/// Optional prefix size in bytes. Allows lookup by partial key.
pub prefix_size: Option<usize>
}
pub trait DatabaseService {
/// Opens database in the specified path
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
/// Closes database
fn close(&self) -> Result<(), Error>;
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten.
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error>;
/// Delete value by key.
fn delete(&self, key: &[u8]) -> Result<(), Error>;
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten.
fn transaction_put(&self, transaction: TransactionHandle, key: &[u8], value: &[u8]) -> Result<(), Error>;
/// Delete value by key using transaction
fn transaction_delete(&self, transaction: TransactionHandle, key: &[u8]) -> Result<(), Error>;
/// Commit transaction to database.
fn write(&self, tr: TransactionHandle) -> Result<(), Error>;
/// Initiate new transaction on database
fn new_transaction(&self) -> TransactionHandle;
/// Get value by key.
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>;
/// Get value by partial key. Prefix size should match configured prefix size.
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error>;
/// Check if there is anything in the database.
fn is_empty(&self) -> Result<bool, Error>;
/// Get handle to iterate through keys
fn iter(&self) -> Result<IteratorHandle, Error>;
/// Next key-value for the the given iterator
fn iter_next(&self, iterator: IteratorHandle) -> Option<KeyValue>;
}

View File

@ -21,7 +21,7 @@ use std::path::PathBuf;
use util::*;
use util::panics::*;
use views::BlockView;
use error::*;
use error::{Error, ImportError, ExecutionError, BlockError, ImportResult};
use header::{BlockNumber, Header};
use state::State;
use spec::Spec;
@ -37,7 +37,8 @@ use filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, TraceFilter};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, TraceFilter};
use client::Error as ClientError;
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
@ -101,7 +102,7 @@ const CLIENT_DB_VER_STR: &'static str = "5.3";
impl Client<CanonVerifier> {
/// Create a new client with given spec and DB path.
pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Arc<Client> {
pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, ClientError> {
Client::<CanonVerifier>::new_with_verifier(config, spec, path, message_channel)
}
}
@ -125,11 +126,11 @@ pub fn append_path(path: &Path, item: &str) -> String {
impl<V> Client<V> where V: Verifier {
/// Create a new client with given spec and DB path and custom verifier.
pub fn new_with_verifier(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Arc<Client<V>> {
pub fn new_with_verifier(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client<V>>, ClientError> {
let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
let tracedb = Arc::new(TraceDB::new(config.tracing, &path, chain.clone()));
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone())));
let mut state_db = journaldb::new(&append_path(&path, "state"), config.pruning);
@ -143,7 +144,7 @@ impl<V> Client<V> where V: Verifier {
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);
Arc::new(Client {
let client = Client {
chain: chain,
tracedb: tracedb,
engine: engine,
@ -154,7 +155,9 @@ impl<V> Client<V> where V: Verifier {
panic_handler: panic_handler,
verifier: PhantomData,
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
})
};
Ok(Arc::new(client))
}
/// Flush the block import queue.
@ -371,28 +374,28 @@ impl<V> Client<V> where V: Verifier {
self.chain.configure_cache(pref_cache_size, max_cache_size);
}
fn block_hash(chain: &BlockChain, id: BlockId) -> Option<H256> {
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
match id {
BlockId::Hash(hash) => Some(hash),
BlockId::Number(number) => chain.block_hash(number),
BlockId::Earliest => chain.block_hash(0),
BlockId::Latest => Some(chain.best_block_hash())
BlockID::Hash(hash) => Some(hash),
BlockID::Number(number) => chain.block_hash(number),
BlockID::Earliest => chain.block_hash(0),
BlockID::Latest => Some(chain.best_block_hash())
}
}
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
fn block_number(&self, id: BlockID) -> Option<BlockNumber> {
match id {
BlockId::Number(number) => Some(number),
BlockId::Hash(ref hash) => self.chain.block_number(hash),
BlockId::Earliest => Some(0),
BlockId::Latest => Some(self.chain.best_block_number())
BlockID::Number(number) => Some(number),
BlockID::Hash(ref hash) => self.chain.block_number(hash),
BlockID::Earliest => Some(0),
BlockID::Latest => Some(self.chain.best_block_number())
}
}
fn transaction_address(&self, id: TransactionId) -> Option<TransactionAddress> {
fn transaction_address(&self, id: TransactionID) -> Option<TransactionAddress> {
match id {
TransactionId::Hash(ref hash) => self.chain.transaction_address(hash),
TransactionId::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress {
TransactionID::Hash(ref hash) => self.chain.transaction_address(hash),
TransactionID::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress {
block_hash: hash,
index: index
})
@ -402,7 +405,7 @@ impl<V> Client<V> where V: Verifier {
impl<V> BlockChainClient for Client<V> where V: Verifier {
fn call(&self, t: &SignedTransaction) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockId::Latest).unwrap();
let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash());
let env_info = EnvInfo {
@ -503,11 +506,11 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
(Some(b), invalid_transactions)
}
fn block_header(&self, id: BlockId) -> Option<Bytes> {
fn block_header(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
}
fn block_body(&self, id: BlockId) -> Option<Bytes> {
fn block_body(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| {
self.chain.block(&hash).map(|bytes| {
let rlp = Rlp::new(&bytes);
@ -519,13 +522,13 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
})
}
fn block(&self, id: BlockId) -> Option<Bytes> {
fn block(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| {
self.chain.block(&hash)
})
}
fn block_status(&self, id: BlockId) -> BlockStatus {
fn block_status(&self, id: BlockID) -> BlockStatus {
match Self::block_hash(&self.chain, id) {
Some(ref hash) if self.chain.is_known(hash) => BlockStatus::InChain,
Some(hash) => self.block_queue.block_status(&hash),
@ -533,7 +536,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
}
}
fn block_total_difficulty(&self, id: BlockId) -> Option<U256> {
fn block_total_difficulty(&self, id: BlockID) -> Option<U256> {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
}
@ -541,7 +544,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
self.state().nonce(address)
}
fn block_hash(&self, id: BlockId) -> Option<H256> {
fn block_hash(&self, id: BlockID) -> Option<H256> {
Self::block_hash(&self.chain, id)
}
@ -557,16 +560,16 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
self.state().storage_at(address, position)
}
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> {
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction> {
self.transaction_address(id).and_then(|address| self.chain.transaction(&address))
}
fn uncle(&self, id: UncleId) -> Option<Header> {
fn uncle(&self, id: UncleID) -> Option<Header> {
let index = id.1;
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index))
}
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
self.transaction_address(id).and_then(|address| {
let t = self.chain.block(&address.block_hash)
.and_then(|block| BlockView::new(&block).localized_transaction_at(address.index));
@ -625,7 +628,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
if self.chain.is_known(&header.sha3()) {
return Err(x!(ImportError::AlreadyInChain));
}
if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown {
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
return Err(x!(BlockError::UnknownParent(header.parent_hash())));
}
}
@ -650,7 +653,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
}
}
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>> {
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>> {
match (self.block_number(from_block), self.block_number(to_block)) {
(Some(from), Some(to)) => Some(self.chain.blocks_with_bloom(bloom, from, to)),
_ => None
@ -721,20 +724,20 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
let trace_address = trace.address;
self.transaction_address(trace.transaction)
.and_then(|tx_address| {
self.block_number(BlockId::Hash(tx_address.block_hash))
self.block_number(BlockID::Hash(tx_address.block_hash))
.and_then(|number| self.tracedb.trace(number, tx_address.index, trace_address))
})
}
fn transaction_traces(&self, transaction: TransactionId) -> Option<Vec<LocalizedTrace>> {
fn transaction_traces(&self, transaction: TransactionID) -> Option<Vec<LocalizedTrace>> {
self.transaction_address(transaction)
.and_then(|tx_address| {
self.block_number(BlockId::Hash(tx_address.block_hash))
self.block_number(BlockID::Hash(tx_address.block_hash))
.and_then(|number| self.tracedb.transaction_traces(number, tx_address.index))
})
}
fn block_traces(&self, block: BlockId) -> Option<Vec<LocalizedTrace>> {
fn block_traces(&self, block: BlockID) -> Option<Vec<LocalizedTrace>> {
self.block_number(block)
.and_then(|number| self.tracedb.block_traces(number))
}

View File

@ -0,0 +1,23 @@
use trace::Error as TraceError;
use std::fmt::{Display, Formatter, Error as FmtError};
/// Client configuration errors.
#[derive(Debug)]
pub enum Error {
/// TraceDB configuration error.
Trace(TraceError),
}
impl From<TraceError> for Error {
fn from(err: TraceError) -> Self {
Error::Trace(err)
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match *self {
Error::Trace(ref err) => write!(f, "{}", err)
}
}
}

View File

@ -18,11 +18,13 @@
mod client;
mod config;
mod error;
mod test_client;
mod trace;
pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::error::Error;
pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::trace::Filter as TraceFilter;
@ -48,26 +50,26 @@ use evm::Factory as EvmFactory;
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Get raw block header data by block id.
fn block_header(&self, id: BlockId) -> Option<Bytes>;
fn block_header(&self, id: BlockID) -> Option<Bytes>;
/// Get raw block body data by block id.
/// Block body is an RLP list of two items: uncles and transactions.
fn block_body(&self, id: BlockId) -> Option<Bytes>;
fn block_body(&self, id: BlockID) -> Option<Bytes>;
/// Get raw block data by block header hash.
fn block(&self, id: BlockId) -> Option<Bytes>;
fn block(&self, id: BlockID) -> Option<Bytes>;
/// Get block status by block header hash.
fn block_status(&self, id: BlockId) -> BlockStatus;
fn block_status(&self, id: BlockID) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
fn block_total_difficulty(&self, id: BlockID) -> Option<U256>;
/// Get address nonce.
fn nonce(&self, address: &Address) -> U256;
/// Get block hash.
fn block_hash(&self, id: BlockId) -> Option<H256>;
fn block_hash(&self, id: BlockID) -> Option<H256>;
/// Get address code.
fn code(&self, address: &Address) -> Option<Bytes>;
@ -79,13 +81,13 @@ pub trait BlockChainClient : Sync + Send {
fn storage_at(&self, address: &Address, position: &H256) -> H256;
/// Get transaction with given hash.
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
/// Get uncle with given id.
fn uncle(&self, id: UncleId) -> Option<Header>;
fn uncle(&self, id: UncleID) -> Option<Header>;
/// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
/// Get a tree route between `from` and `to`.
/// See `BlockChain::tree_route`.
@ -112,11 +114,11 @@ pub trait BlockChainClient : Sync + Send {
/// Get the best block header.
fn best_block_header(&self) -> Bytes {
// TODO: lock blockchain only once
self.block_header(BlockId::Hash(self.chain_info().best_block_hash)).unwrap()
self.block_header(BlockID::Hash(self.chain_info().best_block_hash)).unwrap()
}
/// Returns numbers of blocks containing given bloom.
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>;
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>;
/// Returns logs matching given filter.
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
@ -143,10 +145,10 @@ pub trait BlockChainClient : Sync + Send {
fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>;
/// Returns traces created by transaction.
fn transaction_traces(&self, trace: TransactionId) -> Option<Vec<LocalizedTrace>>;
fn transaction_traces(&self, trace: TransactionID) -> Option<Vec<LocalizedTrace>>;
/// Returns traces created by transaction from block.
fn block_traces(&self, trace: BlockId) -> Option<Vec<LocalizedTrace>>;
fn block_traces(&self, trace: BlockID) -> Option<Vec<LocalizedTrace>>;
/// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes;

View File

@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, TraceId, TraceFilter, LastHashes};
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
@ -58,7 +58,7 @@ pub struct TestBlockChainClient {
/// Execution result.
pub execution_result: RwLock<Option<Executed>>,
/// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionId, LocalizedReceipt>>,
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
/// Block queue size.
pub queue_size: AtomicUsize,
}
@ -106,7 +106,7 @@ impl TestBlockChainClient {
}
/// Set the transaction receipt result
pub fn set_transaction_receipt(&self, id: TransactionId, receipt: LocalizedReceipt) {
pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) {
self.receipts.write().unwrap().insert(id, receipt);
}
@ -191,11 +191,23 @@ impl TestBlockChainClient {
}
}
/// TODO:
/// Make a bad block by setting invalid extra data.
pub fn corrupt_block(&mut self, n: BlockNumber) {
let hash = self.block_hash(BlockId::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
header.parent_hash = H256::new();
let hash = self.block_hash(BlockID::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap());
header.extra_data = b"This extra data is way too long to be considered valid".to_vec();
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
rlp.append_raw(&rlp::NULL_RLP, 1);
self.blocks.write().unwrap().insert(hash, rlp.out());
}
/// Make a bad block by setting invalid parent hash.
pub fn corrupt_block_parent(&mut self, n: BlockNumber) {
let hash = self.block_hash(BlockID::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap());
header.parent_hash = H256::from(42);
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
@ -210,12 +222,12 @@ impl TestBlockChainClient {
blocks_read[&index].clone()
}
fn block_hash(&self, id: BlockId) -> Option<H256> {
fn block_hash(&self, id: BlockID) -> Option<H256> {
match id {
BlockId::Hash(hash) => Some(hash),
BlockId::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
BlockId::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
BlockId::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
BlockID::Hash(hash) => Some(hash),
BlockID::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
BlockID::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
BlockID::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
}
}
}
@ -225,12 +237,12 @@ impl BlockChainClient for TestBlockChainClient {
Ok(self.execution_result.read().unwrap().clone().unwrap())
}
fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> {
fn block_total_difficulty(&self, _id: BlockID) -> Option<U256> {
Some(U256::zero())
}
fn block_hash(&self, _id: BlockId) -> Option<H256> {
unimplemented!();
fn block_hash(&self, id: BlockID) -> Option<H256> {
Self::block_hash(self, id)
}
fn nonce(&self, address: &Address) -> U256 {
@ -249,19 +261,19 @@ impl BlockChainClient for TestBlockChainClient {
self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)
}
fn transaction(&self, _id: TransactionId) -> Option<LocalizedTransaction> {
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
unimplemented!();
}
fn uncle(&self, _id: UncleId) -> Option<BlockHeader> {
fn uncle(&self, _id: UncleID) -> Option<BlockHeader> {
unimplemented!();
}
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
self.receipts.read().unwrap().get(&id).cloned()
}
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> {
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockID, _to_block: BlockID) -> Option<Vec<BlockNumber>> {
unimplemented!();
}
@ -281,11 +293,11 @@ impl BlockChainClient for TestBlockChainClient {
Err(block)
}
fn block_header(&self, id: BlockId) -> Option<Bytes> {
fn block_header(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
}
fn block_body(&self, id: BlockId) -> Option<Bytes> {
fn block_body(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
let mut stream = RlpStream::new_list(2);
stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
@ -294,14 +306,14 @@ impl BlockChainClient for TestBlockChainClient {
}))
}
fn block(&self, id: BlockId) -> Option<Bytes> {
fn block(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
}
fn block_status(&self, id: BlockId) -> BlockStatus {
fn block_status(&self, id: BlockID) -> BlockStatus {
match id {
BlockId::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
BlockId::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
BlockID::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
BlockID::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
_ => BlockStatus::Unknown
}
}
@ -442,11 +454,11 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn transaction_traces(&self, _trace: TransactionId) -> Option<Vec<LocalizedTrace>> {
fn transaction_traces(&self, _trace: TransactionID) -> Option<Vec<LocalizedTrace>> {
unimplemented!();
}
fn block_traces(&self, _trace: BlockId) -> Option<Vec<LocalizedTrace>> {
fn block_traces(&self, _trace: BlockID) -> Option<Vec<LocalizedTrace>> {
unimplemented!();
}
}

View File

@ -7,7 +7,7 @@ use header::BlockNumber;
use trace::DatabaseExtras as TraceDatabaseExtras;
use blockchain::{BlockChain, BlockProvider};
use extras::TransactionAddress;
use super::BlockId;
use super::BlockID;
impl TraceDatabaseExtras for BlockChain {
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
@ -30,7 +30,7 @@ impl TraceDatabaseExtras for BlockChain {
/// Easy to use trace filter.
pub struct Filter {
/// Range of filtering.
pub range: Range<BlockId>,
pub range: Range<BlockID>,
/// From address.
pub from_address: Vec<Address>,
/// To address.

View File

@ -19,6 +19,7 @@
use util::*;
use header::BlockNumber;
use basic_types::LogBloom;
use client::Error as ClientError;
pub use types::executed::ExecutionError;
@ -134,6 +135,8 @@ pub enum ImportError {
#[derive(Debug)]
/// General error type which should be capable of representing all errors in ethcore.
pub enum Error {
/// Client configuration error.
Client(ClientError),
/// Error concerning a utility.
Util(UtilError),
/// Error concerning block processing.
@ -155,6 +158,12 @@ pub enum Error {
/// Result of import block operation.
pub type ImportResult = Result<H256, Error>;
impl From<ClientError> for Error {
fn from(err: ClientError) -> Error {
Error::Client(err)
}
}
impl From<TransactionError> for Error {
fn from(err: TransactionError) -> Error {
Error::Transaction(err)

View File

@ -18,16 +18,16 @@
use util::hash::*;
use util::sha3::*;
use client::BlockId;
use client::BlockID;
use log_entry::LogEntry;
/// Blockchain Filter.
pub struct Filter {
/// Blockchain will be searched from this block.
pub from_block: BlockId,
pub from_block: BlockID,
/// Till this block.
pub to_block: BlockId,
pub to_block: BlockID,
/// Search addresses.
///
@ -102,14 +102,14 @@ mod tests {
use std::str::FromStr;
use util::hash::*;
use filter::Filter;
use client::BlockId;
use client::BlockID;
use log_entry::LogEntry;
#[test]
fn test_bloom_possibilities_none() {
let none_filter = Filter {
from_block: BlockId::Earliest,
to_block: BlockId::Latest,
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: None,
topics: [None, None, None, None]
};
@ -123,8 +123,8 @@ mod tests {
#[test]
fn test_bloom_possibilities_single_address_and_topic() {
let filter = Filter {
from_block: BlockId::Earliest,
to_block: BlockId::Latest,
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
@ -139,8 +139,8 @@ mod tests {
#[test]
fn test_bloom_possibilities_single_address_and_many_topics() {
let filter = Filter {
from_block: BlockId::Earliest,
to_block: BlockId::Latest,
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
@ -156,8 +156,8 @@ mod tests {
#[test]
fn test_bloom_possibilites_multiple_addresses_and_topics() {
let filter = Filter {
from_block: BlockId::Earliest,
to_block: BlockId::Latest,
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
@ -185,8 +185,8 @@ mod tests {
#[test]
fn test_filter_matches() {
let filter = Filter {
from_block: BlockId::Earliest,
to_block: BlockId::Latest,
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),

View File

@ -53,7 +53,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
let temp = RandomTempPath::new();
{
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected());
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap();
for b in &blockchain.blocks_rlp() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());

View File

@ -61,7 +61,7 @@ impl ClientService {
info!("Starting {}", net_service.host_info());
info!("Configured for {} using {:?} engine", spec.name, spec.engine.name());
let client = Client::new(config, spec, db_path, net_service.io().channel());
let client = try!(Client::new(config, spec, db_path, net_service.io().channel()));
panic_handler.forward_from(client.deref());
let client_io = Arc::new(ClientIoHandler {
client: client.clone()

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use client::{BlockChainClient, Client, ClientConfig, BlockId};
use client::{BlockChainClient, Client, ClientConfig, BlockID};
use block::IsBlock;
use tests::helpers::*;
use common::*;
@ -23,7 +23,7 @@ use devtools::*;
#[test]
fn imports_from_empty() {
let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
client.import_verified_blocks(&IoChannel::disconnected());
client.flush_queue();
}
@ -41,7 +41,7 @@ fn returns_state_root_basic() {
#[test]
fn imports_good_block() {
let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let good_block = get_good_dummy_block();
if let Err(_) = client.import_block(good_block) {
panic!("error importing block being good by definition");
@ -49,16 +49,16 @@ fn imports_good_block() {
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
let block = client.block_header(BlockId::Number(1)).unwrap();
let block = client.block_header(BlockID::Number(1)).unwrap();
assert!(!block.is_empty());
}
#[test]
fn query_none_block() {
let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let non_existant = client.block_header(BlockId::Number(188));
let non_existant = client.block_header(BlockID::Number(188));
assert!(non_existant.is_none());
}
@ -66,7 +66,7 @@ fn query_none_block() {
fn query_bad_block() {
let client_result = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]);
let client = client_result.reference();
let bad_block:Option<Bytes> = client.block_header(BlockId::Number(1));
let bad_block:Option<Bytes> = client.block_header(BlockID::Number(1));
assert!(bad_block.is_none());
}
@ -87,7 +87,7 @@ fn returns_block_body() {
let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]);
let client = client_result.reference();
let block = BlockView::new(&dummy_block);
let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap();
let body = client.block_body(BlockID::Hash(block.header().hash())).unwrap();
let body = Rlp::new(&body);
assert_eq!(body.item_count(), 2);
assert_eq!(body.at(0).as_raw()[..], block.rlp().at(1).as_raw()[..]);
@ -98,7 +98,7 @@ fn returns_block_body() {
fn imports_block_sequence() {
let client_result = generate_dummy_client(6);
let client = client_result.reference();
let block = client.block_header(BlockId::Number(5)).unwrap();
let block = client.block_header(BlockID::Number(5)).unwrap();
assert!(!block.is_empty());
}

View File

@ -139,7 +139,7 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let test_spec = get_test_spec();
let test_engine = &test_spec.engine;
let state_root = test_spec.genesis_header().state_root;
@ -205,7 +205,7 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
for block in &blocks {
if let Err(_) = client.import_block(block.clone()) {
panic!("panic importing block which is well-formed");

View File

@ -16,6 +16,7 @@
//! Traces config.
use bloomchain::Config as BloomConfig;
use trace::Error;
/// 3-value enum.
#[derive(Debug, Clone, Copy, PartialEq)]
@ -30,10 +31,10 @@ pub enum Switch {
impl Switch {
/// Tries to turn old switch to new value.
pub fn turn_to(&self, to: Switch) -> Result<bool, &'static str> {
pub fn turn_to(&self, to: Switch) -> Result<bool, Error> {
match (*self, to) {
(Switch::On, Switch::On) | (Switch::On, Switch::Auto) | (Switch::Auto, Switch::On) => Ok(true),
(Switch::Off, Switch::On) => Err("Tracing can't be enabled"),
(Switch::Off, Switch::On) => Err(Error::ResyncRequired),
_ => Ok(false),
}
}

View File

@ -25,7 +25,7 @@ use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, Bloo
use util::{H256, H264, Database, DBTransaction};
use header::BlockNumber;
use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest,
DatabaseExtras};
DatabaseExtras, Error};
use db::{Key, Writable, Readable, CacheUpdatePolicy};
use super::bloom::{TraceGroupPosition, BlockTracesBloom, BlockTracesBloomGroup};
use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
@ -103,7 +103,7 @@ impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras {
impl<T> TraceDB<T> where T: DatabaseExtras {
/// Creates new instance of `TraceDB`.
pub fn new(config: Config, path: &Path, extras: Arc<T>) -> Self {
pub fn new(config: Config, path: &Path, extras: Arc<T>) -> Result<Self, Error> {
let mut tracedb_path = path.to_path_buf();
tracedb_path.push("tracedb");
let tracesdb = Database::open_default(tracedb_path.to_str().unwrap()).unwrap();
@ -116,7 +116,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
None => Switch::Auto,
};
let enabled = old_tracing.turn_to(config.enabled).expect("Tracing can't be enabled. Resync required.");
let enabled = try!(old_tracing.turn_to(config.enabled));
let encoded_tracing = match enabled {
true => [0x1],
@ -126,14 +126,16 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
tracesdb.put(b"enabled", &encoded_tracing).unwrap();
tracesdb.put(b"version", TRACE_DB_VER).unwrap();
TraceDB {
let db = TraceDB {
traces: RwLock::new(HashMap::new()),
blooms: RwLock::new(HashMap::new()),
tracesdb: tracesdb,
bloom_config: config.blooms,
enabled: enabled,
extras: extras,
}
};
Ok(db)
}
/// Returns traces for block with hash.
@ -401,19 +403,19 @@ mod tests {
config.enabled = Switch::Auto;
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
config.enabled = Switch::Off;
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
}
@ -427,26 +429,26 @@ mod tests {
config.enabled = Switch::On;
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
config.enabled = Switch::Auto;
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
config.enabled = Switch::Off;
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
}
@ -461,12 +463,12 @@ mod tests {
config.enabled = Switch::Off;
{
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
config.enabled = Switch::On;
TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic!
TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)).unwrap(); // should panic!
}
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
@ -526,7 +528,7 @@ mod tests {
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
extras.transaction_hashes.insert(1, vec![tx_1.clone()]);
let tracedb = TraceDB::new(config, temp.as_path(), Arc::new(extras));
let tracedb = TraceDB::new(config, temp.as_path(), Arc::new(extras)).unwrap();
// import block 0
let request = create_simple_import_request(0, block_0.clone());

View File

@ -0,0 +1,41 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! TraceDB errors.
use std::fmt::{Display, Formatter, Error as FmtError};
const RESYNC_ERR: &'static str =
"Your current parity installation has synced without transaction tracing.
To use Parity with transaction tracing, you'll need to resync with tracing.
To do this, remove or move away your current database and restart parity. e.g.:
> mv ~/.parity/906a34e69aec8c0d /tmp
> parity";
/// TraceDB errors.
#[derive(Debug)]
pub enum Error {
/// Returned when tracing is enabled,
/// but database does not contain traces of old transactions.
ResyncRequired,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
write!(f, "{}", RESYNC_ERR)
}
}

View File

@ -20,6 +20,7 @@ mod block;
mod bloom;
mod config;
mod db;
mod error;
mod executive_tracer;
pub mod flat;
mod import;
@ -29,6 +30,7 @@ pub use types::trace_types::*;
pub use self::block::BlockTraces;
pub use self::config::{Config, Switch};
pub use self::db::TraceDB;
pub use self::error::Error;
pub use types::trace_types::trace::Trace;
pub use self::noop_tracer::NoopTracer;
pub use self::executive_tracer::ExecutiveTracer;

View File

@ -24,7 +24,7 @@ use std::collections::VecDeque;
/// Uniquely identifies block.
#[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)]
pub enum BlockId {
pub enum BlockID {
/// Block's sha3.
/// Querying by hash is always faster.
Hash(H256),
@ -38,27 +38,27 @@ pub enum BlockId {
/// Uniquely identifies transaction.
#[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)]
pub enum TransactionId {
pub enum TransactionID {
/// Transaction's sha3.
Hash(H256),
/// Block id and transaction index within this block.
/// Querying by block position is always faster.
Location(BlockId, usize)
Location(BlockID, usize)
}
/// Uniquely identifies Trace.
pub struct TraceId {
/// Transaction
pub transaction: TransactionId,
pub transaction: TransactionID,
/// Trace address within transaction.
pub address: Vec<usize>,
}
/// Uniquely identifies Uncle.
#[derive(Debug)]
pub struct UncleId (
pub struct UncleID (
/// Block id.
pub BlockId,
pub BlockID,
/// Position in block.
pub usize
);

View File

@ -87,6 +87,13 @@ fn field_name(builder: &aster::AstBuilder, arg: &Arg) -> ast::Ident {
}
}
pub fn replace_slice_u8(builder: &aster::AstBuilder, ty: &P<ast::Ty>) -> P<ast::Ty> {
if ::syntax::print::pprust::ty_to_string(&strip_ptr(ty)) == "[u8]" {
return builder.ty().id("Vec<u8>")
}
ty.clone()
}
fn push_invoke_signature_aster(
builder: &aster::AstBuilder,
implement: &ImplItem,
@ -111,8 +118,8 @@ fn push_invoke_signature_aster(
.attr().word("derive(Binary)")
.attr().word("allow(non_camel_case_types)")
.struct_(name_str.as_str())
.field(arg_name.as_str()).ty()
.build(strip_ptr(arg_ty));
.field(arg_name.as_str())
.ty().build(replace_slice_u8(builder, &strip_ptr(arg_ty)));
arg_names.push(arg_name);
arg_tys.push(arg_ty.clone());
@ -120,7 +127,7 @@ fn push_invoke_signature_aster(
let arg_name = format!("{}", field_name(builder, &arg));
let arg_ty = &arg.ty;
tree = tree.field(arg_name.as_str()).ty().build(strip_ptr(arg_ty));
tree = tree.field(arg_name.as_str()).ty().build(replace_slice_u8(builder, &strip_ptr(arg_ty)));
arg_names.push(arg_name);
arg_tys.push(arg_ty.clone());
}
@ -406,7 +413,7 @@ fn implement_client_method_body(
request_serialization_statements.push(
quote_stmt!(cx, let mut socket = socket_ref.deref_mut()));
request_serialization_statements.push(
quote_stmt!(cx, ::ipc::invoke($index_ident, Vec::new(), &mut socket)));
quote_stmt!(cx, ::ipc::invoke($index_ident, &None, &mut socket)));
request_serialization_statements
};

View File

@ -175,30 +175,46 @@ fn binary_expr_struct(
) -> Result<BinaryExpressions, Error> {
let size_exprs: Vec<P<ast::Expr>> = fields.iter().enumerate().map(|(index, field)| {
if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" {
return quote_expr!(cx, 1);
}
let field_type_ident = builder.id(
&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)));
let field_type_ident_qualified = builder.id(
replace_qualified(&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))));
let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
let index_ident = builder.id(format!("__field{}", index));
value_ident.and_then(|x| {
let field_id = builder.id(field.ident.unwrap());
Some(quote_expr!(cx,
match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => $x. $field_id .size(),
}))
})
.unwrap_or_else(|| quote_expr!(cx, match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => $index_ident .size(),
}))
match raw_ident.as_ref() {
"u8" => {
quote_expr!(cx, 1)
},
"[u8]" => {
value_ident.and_then(|x| {
let field_id = builder.id(field.ident.unwrap());
Some(quote_expr!(cx, $x. $field_id .len()))
})
.unwrap_or_else(|| {
quote_expr!(cx, $index_ident .len())
}
)
}
_ => {
let field_type_ident = builder.id(
&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)));
let field_type_ident_qualified = builder.id(
replace_qualified(&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))));
value_ident.and_then(|x|
{
let field_id = builder.id(field.ident.unwrap());
Some(quote_expr!(cx,
match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => $x. $field_id .size(),
}))
})
.unwrap_or_else(|| {
quote_expr!(cx, match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => $index_ident .size(),
})
})
}
}
}).collect();
let first_size_expr = size_exprs[0].clone();
@ -233,17 +249,26 @@ fn binary_expr_struct(
},
};
if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" {
write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap());
write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).unwrap());
}
else {
write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => { let size = $member_expr .size(); length_stack.push_back(size); size },
}).unwrap());
write_stmts.push(quote_stmt!(cx,
if let Err(e) = $member_expr .to_bytes(&mut buffer[offset..next_line], length_stack) { return Err(e) };).unwrap());
let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
match raw_ident.as_ref() {
"u8" => {
write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap());
write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).unwrap());
},
"[u8]" => {
write_stmts.push(quote_stmt!(cx, let size = $member_expr .len();).unwrap());
write_stmts.push(quote_stmt!(cx, let next_line = offset + size;).unwrap());
write_stmts.push(quote_stmt!(cx, length_stack.push_back(size);).unwrap());
write_stmts.push(quote_stmt!(cx, buffer[offset..next_line].clone_from_slice($member_expr); ).unwrap());
}
_ => {
write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => { let size = $member_expr .size(); length_stack.push_back(size); size },
}).unwrap());
write_stmts.push(quote_stmt!(cx,
if let Err(e) = $member_expr .to_bytes(&mut buffer[offset..next_line], length_stack) { return Err(e) };).unwrap());
}
}
write_stmts.push(quote_stmt!(cx, offset = next_line; ).unwrap());
@ -251,15 +276,21 @@ fn binary_expr_struct(
let field_index = builder.id(&format!("{}", index));
map_stmts.push(quote_stmt!(cx, map[$field_index] = total;).unwrap());
if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" {
map_stmts.push(quote_stmt!(cx, total += 1;).unwrap());
}
else {
map_stmts.push(quote_stmt!(cx, let size = match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => length_stack.pop_front().unwrap(),
}).unwrap());
map_stmts.push(quote_stmt!(cx, total += size;).unwrap());
match raw_ident.as_ref() {
"u8" => {
map_stmts.push(quote_stmt!(cx, total = total + 1;).unwrap());
},
"[u8]" => {
map_stmts.push(quote_stmt!(cx, let size = length_stack.pop_front().unwrap();).unwrap());
map_stmts.push(quote_stmt!(cx, total = total + size;).unwrap());
},
_ => {
map_stmts.push(quote_stmt!(cx, let size = match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(),
_ => length_stack.pop_front().unwrap(),
}).unwrap());
map_stmts.push(quote_stmt!(cx, total = total + size;).unwrap());
}
}
};
@ -419,6 +450,30 @@ fn fields_sequence(
continue;
}
// special case for [u8], it just takes a byte sequence
if ::syntax::print::pprust::ty_to_string(&field.ty) == "[u8]" {
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
tt.push(Token(_sp, token::DotDot));
if idx+1 != fields.len() {
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)))));
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
}
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
tt.push(Token(_sp, token::Comma));
continue;
}
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!"))));
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
tt.push(
@ -513,6 +568,30 @@ fn named_fields_sequence(
continue;
}
// special case for [u8], it just takes a byte sequence
if ::syntax::print::pprust::ty_to_string(&field.ty) == "[u8]" {
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
tt.push(Token(_sp, token::DotDot));
if idx+1 != fields.len() {
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)))));
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
}
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
tt.push(Token(_sp, token::Comma));
continue;
}
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!"))));
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
tt.push(Token(

View File

@ -116,6 +116,8 @@ impl<S> Worker<S> where S: IpcInterface<S> {
/// Polls all sockets, reads and dispatches method invocations
pub fn poll(&mut self) {
use std::io::Write;
let mut request = PollRequest::new(&mut self.polls[..]);
let _result_guard = Socket::poll(&mut request, POLL_TIMEOUT);
@ -135,7 +137,7 @@ impl<S> Worker<S> where S: IpcInterface<S> {
// dispatching for ipc interface
let result = self.service.dispatch_buf(method_num, payload);
if let Err(e) = socket.nb_write(&result) {
if let Err(e) = socket.write(&result) {
warn!(target: "ipc", "Failed to write response: {:?}", e);
}
}

View File

@ -67,7 +67,7 @@ impl<T> BinaryConvertable for Option<T> where T: BinaryConvertable {
impl<E: BinaryConvertable> BinaryConvertable for Result<(), E> {
fn size(&self) -> usize {
1usize + match *self {
match *self {
Ok(_) => 0,
Err(ref e) => e.size(),
}
@ -75,17 +75,17 @@ impl<E: BinaryConvertable> BinaryConvertable for Result<(), E> {
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
match *self {
Ok(_) => Ok(()),
Ok(_) => Err(BinaryConvertError),
Err(ref e) => Ok(try!(e.to_bytes(buffer, length_stack))),
}
}
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
match buffer[0] {
0 => Ok(Ok(())),
1 => Ok(Err(try!(E::from_bytes(&buffer[1..], length_stack)))),
_ => Err(BinaryConvertError)
}
Ok(Err(try!(E::from_bytes(&buffer, length_stack))))
}
fn from_empty_bytes() -> Result<Self, BinaryConvertError> {
Ok(Ok(()))
}
fn len_params() -> usize {
@ -104,8 +104,8 @@ impl<R: BinaryConvertable, E: BinaryConvertable> BinaryConvertable for Result<R,
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
match *self {
Ok(ref r) => Ok(try!(r.to_bytes(buffer, length_stack))),
Err(ref e) => Ok(try!(e.to_bytes(buffer, length_stack))),
Ok(ref r) => { buffer[0] = 0; Ok(try!(r.to_bytes(&mut buffer[1..], length_stack))) },
Err(ref e) => { buffer[1] = 1; Ok(try!(e.to_bytes(&mut buffer[1..], length_stack))) },
}
}
@ -297,29 +297,29 @@ pub fn deserialize_from<T, R>(r: &mut R) -> Result<T, BinaryConvertError>
T::from_bytes(&payload_buffer[..], &mut fake_stack)
},
_ => {
let mut length_stack = VecDeque::<usize>::new();
let mut size_buffer = [0u8; 8];
try!(r.read(&mut size_buffer[..]).map_err(|_| BinaryConvertError));
let stack_len = try!(u64::from_bytes(&mut size_buffer[..], &mut fake_stack)) as usize;
if stack_len > 0 {
let mut header_buffer = Vec::with_capacity(stack_len * 8);
unsafe { header_buffer.set_len(stack_len * 8); };
let mut payload = Vec::new();
try!(r.read_to_end(&mut payload).map_err(|_| BinaryConvertError));
try!(r.read(&mut header_buffer[..]).map_err(|_| BinaryConvertError));
let mut length_stack = VecDeque::<usize>::new();
let stack_len = try!(u64::from_bytes(&payload[0..8], &mut fake_stack)) as usize;
if stack_len > 0 {
for idx in 0..stack_len {
let stack_item = try!(u64::from_bytes(&header_buffer[idx*8..(idx+1)*8], &mut fake_stack));
let stack_item = try!(u64::from_bytes(&payload[8 + idx*8..8 + (idx+1)*8], &mut fake_stack));
length_stack.push_back(stack_item as usize);
}
}
try!(r.read(&mut size_buffer[..]).map_err(|_| BinaryConvertError));
let size = try!(u64::from_bytes(&size_buffer[..], &mut fake_stack)) as usize;
let mut data = Vec::with_capacity(size);
unsafe { data.set_len(size) };
try!(r.read(&mut data).map_err(|_| BinaryConvertError));
T::from_bytes(&data[..], &mut length_stack)
//try!(r.read(&mut size_buffer).map_err(|_| BinaryConvertError));
let size = try!(u64::from_bytes(&payload[8+stack_len*8..16+stack_len*8], &mut fake_stack)) as usize;
match size {
0 => {
T::from_empty_bytes()
},
_ => {
T::from_bytes(&payload[16+stack_len*8..], &mut length_stack)
}
}
},
}
}
@ -350,6 +350,12 @@ pub fn serialize_into<T, W>(t: &T, w: &mut W) -> Result<(), BinaryConvertError>
let mut size_buffer = [0u8; 8];
let size = t.size();
if size == 0 {
try!(w.write(&size_buffer).map_err(|_| BinaryConvertError));
try!(w.write(&size_buffer).map_err(|_| BinaryConvertError));
return Ok(());
}
let mut buffer = Vec::with_capacity(size);
unsafe { buffer.set_len(size); }
try!(t.to_bytes(&mut buffer[..], &mut length_stack));
@ -386,7 +392,8 @@ pub fn serialize<T: BinaryConvertable>(t: &T) -> Result<Vec<u8>, BinaryConvertEr
use std::io::Cursor;
let mut buff = Cursor::new(Vec::new());
try!(serialize_into(t, &mut buff));
Ok(buff.into_inner())
let into_inner = buff.into_inner();
Ok(into_inner)
}
#[macro_export]
@ -544,7 +551,7 @@ fn deserialize_from_ok() {
fn serialize_into_deserialize_from() {
use std::io::{Cursor, SeekFrom, Seek};
let mut buff = Cursor::new(vec![0u8; 1024]);
let mut buff = Cursor::new(Vec::new());
let mut v = Vec::new();
v.push(Some(5u64));
v.push(None);
@ -557,3 +564,58 @@ fn serialize_into_deserialize_from() {
let de_v = deserialize_from::<Vec<Option<u64>>, _>(&mut buff).unwrap();
assert_eq!(v, de_v);
}
#[test]
fn serialize_opt_vec() {
use std::io::Cursor;
let mut buff = Cursor::new(Vec::new());
let optional_vec: Option<Vec<u8>> = None;
serialize_into(&optional_vec, &mut buff).unwrap();
assert_eq!(&vec![0u8; 16], buff.get_ref());
}
#[test]
fn serialize_opt_vec_payload() {
use std::io::Cursor;
let optional_vec: Option<Vec<u8>> = None;
let payload = serialize(&optional_vec).unwrap();
assert_eq!(vec![0u8;16], payload);
}
#[test]
fn deserialize_opt_vec() {
use std::io::Cursor;
let mut buff = Cursor::new(vec![0u8; 16]);
let vec = deserialize_from::<Option<Vec<u8>>, _>(&mut buff).unwrap();
assert!(vec.is_none());
}
#[test]
fn deserialize_simple_err() {
use std::io::Cursor;
let mut buff = Cursor::new(vec![0u8; 16]);
let result = deserialize_from::<Result<(), u32>, _>(&mut buff).unwrap();
assert!(result.is_ok());
}
#[test]
fn deserialize_opt_vec_in_out() {
use std::io::{Cursor, SeekFrom, Seek};
let mut buff = Cursor::new(Vec::new());
let optional_vec: Option<Vec<u8>> = None;
serialize_into(&optional_vec, &mut buff).unwrap();
buff.seek(SeekFrom::Start(0)).unwrap();
let vec = deserialize_from::<Option<Vec<u8>>, _>(&mut buff).unwrap();
assert!(vec.is_none());
}

View File

@ -42,3 +42,17 @@ pub enum EnumWithStruct {
Left,
Right { how_much: u64 },
}
#[derive(Binary)]
pub struct TwoVec {
v1: Vec<u8>,
v2: Vec<u8>,
}
#[test]
fn opt_two_vec() {
let example: Option<TwoVec> = None;
let serialized = ::ipc::binary::serialize(&example).unwrap();
assert_eq!(serialized, vec![0u8; 16]);
}

View File

@ -30,6 +30,7 @@ pub struct DB<L: Sized> {
pub trait DBWriter {
fn write(&self, data: Vec<u8>) -> Result<(), DBError>;
fn write_slice(&self, data: &[u8]) -> Result<(), DBError>;
}
impl<L: Sized> IpcConfig for DB<L> {}
@ -44,5 +45,11 @@ impl<L: Sized> DBWriter for DB<L> {
*writes = *writes + data.len() as u64;
Ok(())
}
fn write_slice(&self, data: &[u8]) -> Result<(), DBError> {
let mut writes = self.writes.write().unwrap();
*writes = *writes + data.len() as u64;
Ok(())
}
}

View File

@ -20,7 +20,7 @@ use std::sync::atomic::AtomicBool;
use util::*;
use util::keys::store::{AccountService, AccountProvider};
use ethcore::views::{BlockView, HeaderView};
use ethcore::client::{BlockChainClient, BlockId};
use ethcore::client::{BlockChainClient, BlockID};
use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::error::*;
use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions};
@ -475,7 +475,7 @@ impl MinerService for Miner {
fn chain_new_blocks(&self, chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
let block = chain
.block(BlockId::Hash(*hash))
.block(BlockID::Hash(*hash))
// Client should send message after commit to db and inserting to chain.
.expect("Expected in-chain blocks.");
let block = BlockView::new(&block);

View File

@ -16,6 +16,7 @@
use std;
use ethcore;
use ethcore::client::Error as ClientError;
use util::UtilError;
use std::process::exit;
@ -29,6 +30,7 @@ pub fn die_with_error(module: &'static str, e: ethcore::error::Error) -> ! {
match e {
Error::Util(UtilError::StdIo(e)) => die_with_io_error(module, e),
Error::Client(ClientError::Trace(e)) => die_with_message(&format!("{}", e)),
_ => die!("{}: {:?}", module, e),
}
}

View File

@ -28,7 +28,7 @@ use util::numbers::*;
use util::sha3::*;
use util::bytes::{ToPretty};
use util::rlp::{encode, decode, UntrustedRlp, View};
use ethcore::client::*;
use ethcore::client::{BlockChainClient, BlockID, TransactionID, UncleID};
use ethcore::block::IsBlock;
use ethcore::views::*;
use ethcore::ethereum::Ethash;
@ -75,7 +75,7 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
}
}
fn block(&self, id: BlockId, include_txs: bool) -> Result<Value, Error> {
fn block(&self, id: BlockID, include_txs: bool) -> Result<Value, Error> {
let client = take_weak!(self.client);
match (client.block(id.clone()), client.block_total_difficulty(id)) {
(Some(bytes), Some(total_difficulty)) => {
@ -114,16 +114,16 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
}
}
fn transaction(&self, id: TransactionId) -> Result<Value, Error> {
fn transaction(&self, id: TransactionID) -> Result<Value, Error> {
match take_weak!(self.client).transaction(id) {
Some(t) => to_value(&Transaction::from(t)),
None => Ok(Value::Null)
}
}
fn uncle(&self, id: UncleId) -> Result<Value, Error> {
fn uncle(&self, id: UncleID) -> Result<Value, Error> {
let client = take_weak!(self.client);
match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockId::Hash(u.parent_hash().clone())).map(|diff| (diff, u))) {
match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockID::Hash(u.parent_hash().clone())).map(|diff| (diff, u))) {
Some((parent_difficulty, uncle)) => {
let block = Block {
hash: OptionalValue::Value(uncle.hash()),
@ -233,8 +233,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
Params::None => {
let status = take_weak!(self.sync).status();
let res = match status.state {
SyncState::NotSynced | SyncState::Idle => SyncStatus::None,
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks => SyncStatus::Info(SyncInfo {
SyncState::Idle => SyncStatus::None,
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => SyncStatus::Info(SyncInfo {
starting_block: U256::from(status.start_block_number),
current_block: U256::from(take_weak!(self.client).chain_info().best_block_number),
highest_block: U256::from(status.highest_block_number.unwrap_or(status.start_block_number))
@ -322,7 +322,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256,)>(params)
.and_then(|(hash,)| // match
take_weak!(self.client).block(BlockId::Hash(hash))
take_weak!(self.client).block(BlockID::Hash(hash))
.map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count()))))
}
@ -340,7 +340,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn block_uncles_count_by_hash(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256,)>(params)
.and_then(|(hash,)|
take_weak!(self.client).block(BlockId::Hash(hash))
take_weak!(self.client).block(BlockID::Hash(hash))
.map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count()))))
}
@ -364,7 +364,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn block_by_hash(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256, bool)>(params)
.and_then(|(hash, include_txs)| self.block(BlockId::Hash(hash), include_txs))
.and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash), include_txs))
}
fn block_by_number(&self, params: Params) -> Result<Value, Error> {
@ -378,38 +378,38 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
let miner = take_weak!(self.miner);
match miner.transaction(&hash) {
Some(pending_tx) => to_value(&Transaction::from(pending_tx)),
None => self.transaction(TransactionId::Hash(hash))
None => self.transaction(TransactionID::Hash(hash))
}
})
}
fn transaction_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256, Index)>(params)
.and_then(|(hash, index)| self.transaction(TransactionId::Location(BlockId::Hash(hash), index.value())))
.and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash), index.value())))
}
fn transaction_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(BlockNumber, Index)>(params)
.and_then(|(number, index)| self.transaction(TransactionId::Location(number.into(), index.value())))
.and_then(|(number, index)| self.transaction(TransactionID::Location(number.into(), index.value())))
}
fn transaction_receipt(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256,)>(params)
.and_then(|(hash,)| {
let client = take_weak!(self.client);
let receipt = client.transaction_receipt(TransactionId::Hash(hash));
let receipt = client.transaction_receipt(TransactionID::Hash(hash));
to_value(&receipt.map(Receipt::from))
})
}
fn uncle_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256, Index)>(params)
.and_then(|(hash, index)| self.uncle(UncleId(BlockId::Hash(hash), index.value())))
.and_then(|(hash, index)| self.uncle(UncleID(BlockID::Hash(hash), index.value())))
}
fn uncle_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(BlockNumber, Index)>(params)
.and_then(|(number, index)| self.uncle(UncleId(number.into(), index.value())))
.and_then(|(number, index)| self.uncle(UncleID(number.into(), index.value())))
}
fn compilers(&self, params: Params) -> Result<Value, Error> {
@ -617,7 +617,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
// + 1, cause we want to return hashes including current block hash.
let current_number = client.chain_info().best_block_number + 1;
let hashes = (*block_number..current_number).into_iter()
.map(BlockId::Number)
.map(BlockID::Number)
.filter_map(|id| client.block_hash(id))
.collect::<Vec<H256>>();
@ -641,8 +641,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
},
PollFilter::Logs(ref mut block_number, ref filter) => {
let mut filter = filter.clone();
filter.from_block = BlockId::Number(*block_number);
filter.to_block = BlockId::Latest;
filter.from_block = BlockID::Number(*block_number);
filter.to_block = BlockID::Latest;
let logs = client.logs(filter)
.into_iter()
.map(From::from)

View File

@ -19,7 +19,7 @@
use std::sync::{Weak, Arc};
use jsonrpc_core::*;
use util::H256;
use ethcore::client::{BlockChainClient, TransactionId, TraceId};
use ethcore::client::{BlockChainClient, TransactionID, TraceId};
use v1::traits::Traces;
use v1::types::{TraceFilter, Trace, BlockNumber, Index};
@ -62,7 +62,7 @@ impl<C> Traces for TracesClient<C> where C: BlockChainClient + 'static {
from_params::<(H256,)>(params)
.and_then(|(transaction_hash,)| {
let client = take_weak!(self.client);
let traces = client.transaction_traces(TransactionId::Hash(transaction_hash));
let traces = client.transaction_traces(TransactionID::Hash(transaction_hash));
let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect());
to_value(&traces)
})
@ -73,7 +73,7 @@ impl<C> Traces for TracesClient<C> where C: BlockChainClient + 'static {
.and_then(|(transaction_hash, address)| {
let client = take_weak!(self.client);
let id = TraceId {
transaction: TransactionId::Hash(transaction_hash),
transaction: TransactionID::Hash(transaction_hash),
address: address.into_iter().map(|i| i.value()).collect()
};
let trace = client.trace(id);

View File

@ -21,7 +21,7 @@ use jsonrpc_core::IoHandler;
use util::hash::{Address, H256, FixedHash};
use util::numbers::{Uint, U256};
use util::keys::{TestAccount, TestAccountProvider};
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::receipt::LocalizedReceipt;
use ethcore::transaction::{Transaction, Action};
@ -562,7 +562,7 @@ fn rpc_eth_transaction_receipt() {
let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap();
let tester = EthTester::default();
tester.client.set_transaction_receipt(TransactionId::Hash(hash), receipt);
tester.client.set_transaction_receipt(TransactionID::Hash(hash), receipt);
let request = r#"{
"jsonrpc": "2.0",

View File

@ -39,7 +39,7 @@ impl TestSyncProvider {
pub fn new(config: Config) -> Self {
TestSyncProvider {
status: RwLock::new(SyncStatus {
state: SyncState::NotSynced,
state: SyncState::Idle,
network_id: config.network_id,
protocol_version: 63,
start_block_number: 0,

View File

@ -16,7 +16,7 @@
use serde::{Deserialize, Deserializer, Error};
use serde::de::Visitor;
use ethcore::client::BlockId;
use ethcore::client::BlockID;
/// Represents rpc api block number param.
#[derive(Debug, PartialEq)]
@ -54,20 +54,20 @@ impl Visitor for BlockNumberVisitor {
}
}
impl Into<BlockId> for BlockNumber {
fn into(self) -> BlockId {
impl Into<BlockID> for BlockNumber {
fn into(self) -> BlockID {
match self {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Num(n) => BlockID::Number(n),
BlockNumber::Earliest => BlockID::Earliest,
// TODO: change this once blockid support pendingst,
BlockNumber::Pending | BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending | BlockNumber::Latest => BlockID::Latest,
}
}
}
#[cfg(test)]
mod tests {
use ethcore::client::BlockId;
use ethcore::client::BlockID;
use super::*;
use serde_json;
@ -80,10 +80,10 @@ mod tests {
#[test]
fn block_number_into() {
assert_eq!(BlockId::Number(100), BlockNumber::Num(100).into());
assert_eq!(BlockId::Earliest, BlockNumber::Earliest.into());
assert_eq!(BlockId::Latest, BlockNumber::Latest.into());
assert_eq!(BlockId::Latest, BlockNumber::Pending.into());
assert_eq!(BlockID::Number(100), BlockNumber::Num(100).into());
assert_eq!(BlockID::Earliest, BlockNumber::Earliest.into());
assert_eq!(BlockID::Latest, BlockNumber::Latest.into());
assert_eq!(BlockID::Latest, BlockNumber::Pending.into());
}
}

View File

@ -20,7 +20,7 @@ use jsonrpc_core::Value;
use util::numbers::*;
use v1::types::BlockNumber;
use ethcore::filter::Filter as EthFilter;
use ethcore::client::BlockId;
use ethcore::client::BlockID;
#[derive(Debug, PartialEq)]
pub enum VariadicValue<T> where T: Deserialize {
@ -61,8 +61,8 @@ pub struct Filter {
impl Into<EthFilter> for Filter {
fn into(self) -> EthFilter {
EthFilter {
from_block: self.from_block.map_or_else(|| BlockId::Latest, Into::into),
to_block: self.to_block.map_or_else(|| BlockId::Latest, Into::into),
from_block: self.from_block.map_or_else(|| BlockID::Latest, Into::into),
to_block: self.to_block.map_or_else(|| BlockID::Latest, Into::into),
address: self.address.and_then(|address| match address {
VariadicValue::Null => None,
VariadicValue::Single(a) => Some(vec![a]),

View File

@ -17,7 +17,7 @@
//! Trace filter deserialization.
use util::Address;
use ethcore::client::BlockId;
use ethcore::client::BlockID;
use ethcore::client;
use super::BlockNumber;
@ -35,8 +35,8 @@ pub struct TraceFilter {
impl Into<client::TraceFilter> for TraceFilter {
fn into(self) -> client::TraceFilter {
let start = self.from_block.map_or(BlockId::Latest, Into::into);
let end = self.to_block.map_or(BlockId::Latest, Into::into);
let start = self.from_block.map_or(BlockID::Latest, Into::into);
let end = self.to_block.map_or(BlockID::Latest, Into::into);
client::TraceFilter {
range: start..end,
from_address: self.from_address.unwrap_or_else(Vec::new),

View File

@ -307,7 +307,7 @@ impl BlockCollection {
#[cfg(test)]
mod test {
use super::BlockCollection;
use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockId, BlockChainClient};
use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockID, BlockChainClient};
use ethcore::views::HeaderView;
use ethcore::header::BlockNumber;
use util::*;
@ -328,7 +328,7 @@ mod test {
assert!(is_empty(&bc));
let client = TestBlockChainClient::new();
client.add_blocks(100, EachBlockWith::Nothing);
let hashes = (0 .. 100).map(|i| (&client as &BlockChainClient).block_hash(BlockId::Number(i)).unwrap()).collect();
let hashes = (0 .. 100).map(|i| (&client as &BlockChainClient).block_hash(BlockID::Number(i)).unwrap()).collect();
bc.reset_to(hashes);
assert!(!is_empty(&bc));
bc.clear();
@ -342,7 +342,7 @@ mod test {
let client = TestBlockChainClient::new();
let nblocks = 200;
client.add_blocks(nblocks, EachBlockWith::Nothing);
let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect();
let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockID::Number(i as BlockNumber)).unwrap()).collect();
let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect();
let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect();
let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect();
@ -395,7 +395,7 @@ mod test {
let client = TestBlockChainClient::new();
let nblocks = 200;
client.add_blocks(nblocks, EachBlockWith::Nothing);
let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect();
let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockID::Number(i as BlockNumber)).unwrap()).collect();
let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect();
let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect();
let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect();

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@
//! fn main() {
//! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap();
//! let dir = env::temp_dir();
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, service.io().channel());
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, service.io().channel()).unwrap();
//! let miner = Miner::new(false, ethereum::new_frontier());
//! EthSync::register(&mut service, SyncConfig::default(), client, miner);
//! }
@ -74,8 +74,8 @@ use io::NetSyncIo;
use chain::ChainSync;
mod chain;
mod blocks;
mod io;
mod range_collection;
#[cfg(test)]
mod tests;
@ -116,9 +116,10 @@ pub use self::chain::{SyncStatus, SyncState};
impl EthSync {
/// Creates and register protocol with the network service
pub fn register(service: &mut NetworkService<SyncMessage>, config: SyncConfig, chain: Arc<Client>, miner: Arc<Miner>) -> Arc<EthSync> {
let sync = ChainSync::new(config, miner, chain.deref());
let sync = Arc::new(EthSync {
chain: chain,
sync: RwLock::new(ChainSync::new(config, miner)),
sync: RwLock::new(sync),
});
service.register_protocol(sync.clone(), "eth", &[62u8, 63u8]).expect("Error registering eth protocol handler");
sync

View File

@ -1,317 +0,0 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
/// This module defines a trait for a collection of ranged values and an implementation
/// for this trait over sorted vector.
use std::ops::{Add, Sub, Range};
pub trait ToUsize {
fn to_usize(&self) -> usize;
}
pub trait FromUsize {
fn from_usize(s: usize) -> Self;
}
/// A key-value collection orderd by key with sequential key-value pairs grouped together.
/// Such group is called a range.
/// E.g. a set of collection of 5 pairs {1, a}, {2, b}, {10, x}, {11, y}, {12, z} will be grouped into two ranges: {1, [a,b]}, {10, [x,y,z]}
pub trait RangeCollection<K, V> {
/// Check if the given key is present in the collection.
fn have_item(&self, key: &K) -> bool;
/// Get value by key.
fn find_item(&self, key: &K) -> Option<&V>;
/// Get a range of keys from `key` till the end of the range that has `key`
/// Returns an empty range is key does not exist.
fn get_tail(&mut self, key: &K) -> Range<K>;
/// Remove all elements < `start` in the range that contains `start` - 1
fn remove_head(&mut self, start: &K);
/// Remove all elements >= `start` in the range that contains `start`
fn remove_tail(&mut self, start: &K);
/// Remove all elements >= `start`
fn remove_from(&mut self, start: &K);
/// Remove all elements >= `tail`
fn insert_item(&mut self, key: K, value: V);
/// Get an iterator over ranges
fn range_iter(& self) -> RangeIterator<K, V>;
}
/// Range iterator. For each range yelds a key for the first element of the range and a vector of values.
pub struct RangeIterator<'c, K:'c, V:'c> {
range: usize,
collection: &'c Vec<(K, Vec<V>)>
}
impl<'c, K:'c, V:'c> Iterator for RangeIterator<'c, K, V> where K: Add<Output = K> + FromUsize + ToUsize + Copy {
type Item = (K, &'c [V]);
// The 'Iterator' trait only requires the 'next' method to be defined. The
// return type is 'Option<T>', 'None' is returned when the 'Iterator' is
// over, otherwise the next value is returned wrapped in 'Some'
fn next(&mut self) -> Option<(K, &'c [V])> {
if self.range > 0 {
self.range -= 1;
}
else {
return None;
}
match self.collection.get(self.range) {
Some(&(ref k, ref vec)) => {
Some((*k, vec))
},
None => None
}
}
}
impl<K, V> RangeCollection<K, V> for Vec<(K, Vec<V>)> where K: Ord + PartialEq + Add<Output = K> + Sub<Output = K> + Copy + FromUsize + ToUsize {
fn range_iter(&self) -> RangeIterator<K, V> {
RangeIterator {
range: self.len(),
collection: self
}
}
fn have_item(&self, key: &K) -> bool {
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
Ok(_) => true,
Err(index) => match self.get(index) {
Some(&(ref k, ref v)) => k <= key && (*k + FromUsize::from_usize(v.len())) > *key,
_ => false
},
}
}
fn find_item(&self, key: &K) -> Option<&V> {
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
Ok(index) => self.get(index).unwrap().1.get(0),
Err(index) => match self.get(index) {
Some(&(ref k, ref v)) if k <= key && (*k + FromUsize::from_usize(v.len())) > *key => v.get((*key - *k).to_usize()),
_ => None
},
}
}
fn get_tail(&mut self, key: &K) -> Range<K> {
let kv = *key;
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
Ok(index) => kv..(kv + FromUsize::from_usize(self[index].1.len())),
Err(index) => {
match self.get_mut(index) {
Some(&mut (ref k, ref mut v)) if k <= key && (*k + FromUsize::from_usize(v.len())) > *key => {
kv..(*k + FromUsize::from_usize(v.len()))
}
_ => kv..kv
}
},
}
}
/// Remove element key and following elements in the same range
fn remove_tail(&mut self, key: &K) {
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
Ok(index) => { self.remove(index); },
Err(index) =>{
let mut empty = false;
match self.get_mut(index) {
Some(&mut (ref k, ref mut v)) if k <= key && (*k + FromUsize::from_usize(v.len())) > *key => {
v.truncate((*key - *k).to_usize());
empty = v.is_empty();
}
_ => {}
}
if empty {
self.remove(index);
}
},
}
}
/// Remove the element and all following it.
fn remove_from(&mut self, key: &K) {
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
Ok(index) => { self.drain(.. index + 1); },
Err(index) =>{
let mut empty = false;
match self.get_mut(index) {
Some(&mut (ref k, ref mut v)) if k <= key && (*k + FromUsize::from_usize(v.len())) > *key => {
v.truncate((*key - *k).to_usize());
empty = v.is_empty();
}
_ => {}
}
if empty {
self.drain(.. index + 1);
} else {
self.drain(.. index);
}
},
}
}
/// Remove range elements up to key
fn remove_head(&mut self, key: &K) {
if *key == FromUsize::from_usize(0) {
return
}
let prev = *key - FromUsize::from_usize(1);
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
Ok(_) => { }, //start of range, do nothing.
Err(index) => {
let mut empty = false;
match self.get_mut(index) {
Some(&mut (ref mut k, ref mut v)) if *k <= prev && (*k + FromUsize::from_usize(v.len())) > prev => {
let tail = v.split_off((*key - *k).to_usize());
empty = tail.is_empty();
let removed = ::std::mem::replace(v, tail);
let new_k = *k + FromUsize::from_usize(removed.len());
::std::mem::replace(k, new_k);
}
_ => {}
}
if empty {
self.remove(index);
}
},
}
}
fn insert_item(&mut self, key: K, value: V) {
assert!(!self.have_item(&key));
// todo: fix warning
let lower = match self.binary_search_by(|&(k, _)| k.cmp(&key).reverse()) {
Ok(index) | Err(index) => index
};
let mut to_remove: Option<usize> = None;
if lower < self.len() && self[lower].0 + FromUsize::from_usize(self[lower].1.len()) == key {
// extend into existing chunk
self[lower].1.push(value);
}
else {
// insert a new chunk
let range: Vec<V> = vec![value];
self.insert(lower, (key, range));
};
if lower > 0 {
let next = lower - 1;
if next < self.len()
{
{
let (mut next, mut inserted) = self.split_at_mut(lower);
let mut next = next.last_mut().unwrap();
let mut inserted = inserted.first_mut().unwrap();
if next.0 == key + FromUsize::from_usize(1)
{
inserted.1.append(&mut next.1);
to_remove = Some(lower - 1);
}
}
if let Some(r) = to_remove {
self.remove(r);
}
}
}
}
}
#[test]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn test_range() {
use std::cmp::{Ordering};
let mut ranges: Vec<(u64, Vec<char>)> = Vec::new();
assert_eq!(ranges.range_iter().next(), None);
assert_eq!(ranges.find_item(&1), None);
assert!(!ranges.have_item(&1));
assert_eq!(ranges.get_tail(&0), 0..0);
ranges.insert_item(17, 'q');
assert_eq!(ranges.range_iter().cmp(vec![(17, &['q'][..])]), Ordering::Equal);
assert_eq!(ranges.find_item(&17), Some(&'q'));
assert!(ranges.have_item(&17));
assert_eq!(ranges.get_tail(&17), 17..18);
ranges.insert_item(18, 'r');
assert_eq!(ranges.range_iter().cmp(vec![(17, &['q', 'r'][..])]), Ordering::Equal);
assert_eq!(ranges.find_item(&18), Some(&'r'));
assert!(ranges.have_item(&18));
assert_eq!(ranges.get_tail(&17), 17..19);
ranges.insert_item(16, 'p');
assert_eq!(ranges.range_iter().cmp(vec![(16, &['p', 'q', 'r'][..])]), Ordering::Equal);
assert_eq!(ranges.find_item(&16), Some(&'p'));
assert_eq!(ranges.find_item(&17), Some(&'q'));
assert_eq!(ranges.find_item(&18), Some(&'r'));
assert!(ranges.have_item(&16));
assert_eq!(ranges.get_tail(&17), 17..19);
assert_eq!(ranges.get_tail(&16), 16..19);
ranges.insert_item(2, 'b');
assert_eq!(ranges.range_iter().cmp(vec![(2, &['b'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
assert_eq!(ranges.find_item(&2), Some(&'b'));
ranges.insert_item(3, 'c');
ranges.insert_item(4, 'd');
assert_eq!(ranges.get_tail(&3), 3..5);
assert_eq!(ranges.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
let mut r = ranges.clone();
r.remove_head(&1);
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_head(&2);
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_head(&3);
assert_eq!(r.range_iter().cmp(vec![(3, &['c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_head(&10);
assert_eq!(r.range_iter().cmp(vec![(3, &['c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_head(&5);
assert_eq!(r.range_iter().cmp(vec![(16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_head(&19);
assert_eq!(r.range_iter().next(), None);
let mut r = ranges.clone();
r.remove_tail(&20);
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_tail(&17);
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p'][..])]), Ordering::Equal);
r.remove_tail(&16);
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..])]), Ordering::Equal);
r.remove_tail(&3);
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
r.remove_tail(&2);
assert_eq!(r.range_iter().next(), None);
let mut r = ranges.clone();
r.remove_from(&20);
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
r.remove_from(&18);
assert!(!r.have_item(&18));
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q'][..])]), Ordering::Equal);
r.remove_from(&16);
assert!(!r.have_item(&16));
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..])]), Ordering::Equal);
r.remove_from(&3);
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
r.remove_from(&1);
assert_eq!(r.range_iter().next(), None);
let mut r = ranges.clone();
r.remove_from(&2);
assert_eq!(r.range_iter().next(), None);
}

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::*;
use ethcore::client::{BlockChainClient, BlockId, EachBlockWith};
use ethcore::client::{BlockChainClient, BlockID, EachBlockWith};
use chain::{SyncState};
use super::helpers::*;
@ -26,7 +26,17 @@ fn two_peers() {
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
assert!(net.peer(0).chain.block(BlockID::Number(1000)).is_some());
assert_eq!(net.peer(0).chain.blocks.read().unwrap().deref(), net.peer(1).chain.blocks.read().unwrap().deref());
}
#[test]
fn long_chain() {
::env_logger::init().ok();
let mut net = TestNet::new(2);
net.peer_mut(1).chain.add_blocks(50000, EachBlockWith::Nothing);
net.sync();
assert!(net.peer(0).chain.block(BlockID::Number(50000)).is_some());
assert_eq!(net.peer(0).chain.blocks.read().unwrap().deref(), net.peer(1).chain.blocks.read().unwrap().deref());
}
@ -47,7 +57,7 @@ fn takes_few_steps() {
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(100, EachBlockWith::Uncle);
let total_steps = net.sync();
assert!(total_steps < 7);
assert!(total_steps < 20);
}
#[test]
@ -60,7 +70,7 @@ fn empty_blocks() {
net.peer_mut(2).chain.add_blocks(5, with);
}
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
assert!(net.peer(0).chain.block(BlockID::Number(1000)).is_some());
assert_eq!(net.peer(0).chain.blocks.read().unwrap().deref(), net.peer(1).chain.blocks.read().unwrap().deref());
}
@ -79,6 +89,7 @@ fn forked() {
// peer 1 has the best chain of 601 blocks
let peer1_chain = net.peer(1).chain.numbers.read().unwrap().clone();
net.sync();
assert_eq!(net.peer(0).chain.difficulty.read().unwrap().deref(), net.peer(1).chain.difficulty.read().unwrap().deref());
assert_eq!(net.peer(0).chain.numbers.read().unwrap().deref(), &peer1_chain);
assert_eq!(net.peer(1).chain.numbers.read().unwrap().deref(), &peer1_chain);
assert_eq!(net.peer(2).chain.numbers.read().unwrap().deref(), &peer1_chain);
@ -97,13 +108,13 @@ fn restart() {
net.restart_peer(0);
let status = net.peer(0).sync.status();
assert_eq!(status.state, SyncState::NotSynced);
assert_eq!(status.state, SyncState::ChainHead);
}
#[test]
fn status_empty() {
let net = TestNet::new(2);
assert_eq!(net.peer(0).sync.status().state, SyncState::NotSynced);
assert_eq!(net.peer(0).sync.status().state, SyncState::Idle);
}
#[test]
@ -166,8 +177,17 @@ fn restart_on_malformed_block() {
let mut net = TestNet::new(2);
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle);
net.peer_mut(1).chain.corrupt_block(6);
net.sync_steps(10);
net.sync_steps(20);
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5);
}
#[test]
fn restart_on_broken_chain() {
let mut net = TestNet::new(2);
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle);
net.peer_mut(1).chain.corrupt_block_parent(6);
net.sync_steps(20);
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5);
}

View File

@ -92,9 +92,11 @@ impl TestNet {
started: false,
};
for _ in 0..n {
let chain = TestBlockChainClient::new();
let sync = ChainSync::new(SyncConfig::default(), Miner::new(false, Spec::new_test()), &chain);
net.peers.push(TestPeer {
chain: TestBlockChainClient::new(),
sync: ChainSync::new(SyncConfig::default(), Miner::new(false, Spec::new_test())),
sync: sync,
chain: chain,
queue: VecDeque::new(),
});
}

View File

@ -17,7 +17,7 @@ nix ="0.5.0"
rand = "0.3.12"
time = "0.1.34"
tiny-keccak = "1.0"
rocksdb = { git = "https://github.com/arkpar/rust-rocksdb.git" }
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
lazy_static = "0.1"
eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" }
rust-crypto = "0.2.34"

View File

@ -55,11 +55,11 @@ pub struct DatabaseConfig {
}
/// Database iterator
pub struct DatabaseIterator<'a> {
iter: DBIterator<'a>,
pub struct DatabaseIterator {
iter: DBIterator,
}
impl<'a> Iterator for DatabaseIterator<'a> {
impl<'a> Iterator for DatabaseIterator {
type Item = (Box<[u8]>, Box<[u8]>);
fn next(&mut self) -> Option<Self::Item> {
@ -135,7 +135,7 @@ impl Database {
/// Get value by partial key. Prefix size should match configured prefix size.
pub fn get_by_prefix(&self, prefix: &[u8]) -> Option<Box<[u8]>> {
let mut iter = self.db.iterator(IteratorMode::From(prefix, Direction::forward));
let mut iter = self.db.iterator(IteratorMode::From(prefix, Direction::Forward));
match iter.next() {
// TODO: use prefix_same_as_start read option (not availabele in C API currently)
Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Some(v) } else { None },