Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
399dfc4c2e
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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
24
db/Cargo.toml
Normal 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
43
db/build.rs
Normal 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
370
db/src/database.rs
Normal 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
20
db/src/lib.rs
Normal 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
25
db/src/lib.rs.in
Normal 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
0
db/src/service.rs
Normal file
73
db/src/traits.rs
Normal file
73
db/src/traits.rs
Normal 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>;
|
||||
}
|
@ -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))
|
||||
}
|
||||
|
23
ethcore/src/client/error.rs
Normal file
23
ethcore/src/client/error.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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()]),
|
||||
|
@ -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());
|
||||
|
@ -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()
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
41
ethcore/src/trace/error.rs
Normal file
41
ethcore/src/trace/error.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -175,19 +175,31 @@ 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 raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
|
||||
let index_ident = builder.id(format!("__field{}", index));
|
||||
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))));
|
||||
|
||||
let index_ident = builder.id(format!("__field{}", index));
|
||||
value_ident.and_then(|x| {
|
||||
value_ident.and_then(|x|
|
||||
{
|
||||
let field_id = builder.id(field.ident.unwrap());
|
||||
Some(quote_expr!(cx,
|
||||
match $field_type_ident_qualified::len_params() {
|
||||
@ -195,10 +207,14 @@ fn binary_expr_struct(
|
||||
_ => $x. $field_id .size(),
|
||||
}))
|
||||
})
|
||||
.unwrap_or_else(|| quote_expr!(cx, match $field_type_ident_qualified::len_params() {
|
||||
.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,11 +249,19 @@ fn binary_expr_struct(
|
||||
},
|
||||
};
|
||||
|
||||
if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" {
|
||||
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());
|
||||
}
|
||||
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 },
|
||||
@ -245,21 +269,28 @@ fn binary_expr_struct(
|
||||
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());
|
||||
|
||||
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 {
|
||||
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 += size;).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(
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]),
|
||||
|
@ -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),
|
||||
|
@ -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
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user