Merge branch 'master' into on-demand-les-request
This commit is contained in:
commit
c2ad769fb7
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -108,6 +108,17 @@ dependencies = [
|
||||
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bigint"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.4.0"
|
||||
@ -377,10 +388,10 @@ dependencies = [
|
||||
name = "ethcore-bigint"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1491,7 +1502,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#8d16476c138e94a643ad4624df9825f34b57e1b0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#d95a7dd2cc7469dc58af77743ec3ebc65e51cf36"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -2357,6 +2368,7 @@ dependencies = [
|
||||
"checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975"
|
||||
"checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a"
|
||||
"checksum aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4df293303e8a52e1df7984ac1415e195f5fcbf51e4bb7bda54557861a3954a08"
|
||||
"checksum bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2311bcd71b281e142a095311c22509f0d6bcd87b3000d7dbaa810929b9d6f6ae"
|
||||
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
||||
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
||||
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
|
||||
|
@ -58,18 +58,18 @@ Parity is fully compatible with Stable Rust.
|
||||
|
||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
||||
|
||||
- Linux and OSX:
|
||||
- Linux:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
Parity also requires `gcc`, `g++` and `make` packages to be installed.
|
||||
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl` and `pkg-config` packages to be installed.
|
||||
- OSX:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
`clang` and `make` are required. These come with Xcode command line tools or can be installed with homebrew.
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
- Windows
|
||||
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from
|
||||
|
@ -9,7 +9,7 @@ build = "build.rs"
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
rand = "0.3.14"
|
||||
rand = "0.3"
|
||||
log = "0.3"
|
||||
env_logger = "0.3"
|
||||
futures = "0.1"
|
||||
|
BIN
dapps/res/gavcoin.zip
Normal file
BIN
dapps/res/gavcoin.zip
Normal file
Binary file not shown.
@ -17,14 +17,13 @@
|
||||
//! Fetchable Dapps support.
|
||||
|
||||
use std::fs;
|
||||
use std::sync::{Arc};
|
||||
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use page::LocalPageEndpoint;
|
||||
use handlers::FetchControl;
|
||||
|
||||
pub enum ContentStatus {
|
||||
Fetching(Arc<FetchControl>),
|
||||
Fetching(FetchControl),
|
||||
Ready(LocalPageEndpoint),
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ use util::H256;
|
||||
|
||||
use util::sha3::sha3;
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use handlers::ContentValidator;
|
||||
use handlers::{ContentValidator, ValidatorResponse};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
|
||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||
@ -35,30 +35,30 @@ fn write_response_and_check_hash(
|
||||
response: fetch::Response
|
||||
) -> Result<(fs::File, PathBuf), ValidationError> {
|
||||
// try to parse id
|
||||
let id = try!(id.parse().map_err(|_| ValidationError::InvalidContentId));
|
||||
let id = id.parse().map_err(|_| ValidationError::InvalidContentId)?;
|
||||
|
||||
// check if content exists
|
||||
if content_path.exists() {
|
||||
warn!(target: "dapps", "Overwriting existing content at 0x{:?}", id);
|
||||
try!(fs::remove_dir_all(&content_path))
|
||||
fs::remove_dir_all(&content_path)?
|
||||
}
|
||||
|
||||
// create directory
|
||||
try!(fs::create_dir_all(&content_path));
|
||||
fs::create_dir_all(&content_path)?;
|
||||
|
||||
// append filename
|
||||
content_path.push(filename);
|
||||
|
||||
// Now write the response
|
||||
let mut file = io::BufWriter::new(try!(fs::File::create(&content_path)));
|
||||
let mut file = io::BufWriter::new(fs::File::create(&content_path)?);
|
||||
let mut reader = io::BufReader::new(response);
|
||||
try!(io::copy(&mut reader, &mut file));
|
||||
try!(file.flush());
|
||||
io::copy(&mut reader, &mut file)?;
|
||||
file.flush()?;
|
||||
|
||||
// Validate hash
|
||||
// TODO [ToDr] calculate sha3 in-flight while reading the response
|
||||
let mut file = io::BufReader::new(try!(fs::File::open(&content_path)));
|
||||
let hash = try!(sha3(&mut file));
|
||||
let mut file = io::BufReader::new(fs::File::open(&content_path)?);
|
||||
let hash = sha3(&mut file)?;
|
||||
if id == hash {
|
||||
Ok((file.into_inner(), content_path))
|
||||
} else {
|
||||
@ -90,10 +90,10 @@ impl Content {
|
||||
impl ContentValidator for Content {
|
||||
type Error = ValidationError;
|
||||
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<LocalPageEndpoint, ValidationError> {
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let validate = |content_path: PathBuf| {
|
||||
// Create dir
|
||||
let (_, content_path) = try!(write_response_and_check_hash(self.id.as_str(), content_path.clone(), self.id.as_str(), response));
|
||||
let (_, content_path) = write_response_and_check_hash(self.id.as_str(), content_path.clone(), self.id.as_str(), response)?;
|
||||
|
||||
Ok(LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled))
|
||||
};
|
||||
@ -108,7 +108,7 @@ impl ContentValidator for Content {
|
||||
let _ = fs::remove_dir_all(&content_path);
|
||||
}
|
||||
(self.on_done)(result.as_ref().ok().cloned());
|
||||
result
|
||||
result.map(ValidatorResponse::Local)
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ impl Dapp {
|
||||
|
||||
fn find_manifest(zip: &mut zip::ZipArchive<fs::File>) -> Result<(Manifest, PathBuf), ValidationError> {
|
||||
for i in 0..zip.len() {
|
||||
let mut file = try!(zip.by_index(i));
|
||||
let mut file = zip.by_index(i)?;
|
||||
|
||||
if !file.name().ends_with(MANIFEST_FILENAME) {
|
||||
continue;
|
||||
@ -157,20 +157,20 @@ impl Dapp {
|
||||
impl ContentValidator for Dapp {
|
||||
type Error = ValidationError;
|
||||
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<LocalPageEndpoint, ValidationError> {
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let validate = |dapp_path: PathBuf| {
|
||||
let (file, zip_path) = try!(write_response_and_check_hash(self.id.as_str(), dapp_path.clone(), &format!("{}.zip", self.id), response));
|
||||
let (file, zip_path) = write_response_and_check_hash(self.id.as_str(), dapp_path.clone(), &format!("{}.zip", self.id), response)?;
|
||||
trace!(target: "dapps", "Opening dapp bundle at {:?}", zip_path);
|
||||
// Unpack archive
|
||||
let mut zip = try!(zip::ZipArchive::new(file));
|
||||
let mut zip = zip::ZipArchive::new(file)?;
|
||||
// First find manifest file
|
||||
let (mut manifest, manifest_dir) = try!(Self::find_manifest(&mut zip));
|
||||
let (mut manifest, manifest_dir) = Self::find_manifest(&mut zip)?;
|
||||
// Overwrite id to match hash
|
||||
manifest.id = self.id.clone();
|
||||
|
||||
// Unpack zip
|
||||
for i in 0..zip.len() {
|
||||
let mut file = try!(zip.by_index(i));
|
||||
let mut file = zip.by_index(i)?;
|
||||
let is_dir = file.name().chars().rev().next() == Some('/');
|
||||
|
||||
let file_path = PathBuf::from(file.name());
|
||||
@ -180,22 +180,22 @@ impl ContentValidator for Dapp {
|
||||
let p = dapp_path.join(location_in_manifest_base);
|
||||
// Check if it's a directory
|
||||
if is_dir {
|
||||
try!(fs::create_dir_all(p));
|
||||
fs::create_dir_all(p)?;
|
||||
} else {
|
||||
let mut target = try!(fs::File::create(p));
|
||||
try!(io::copy(&mut file, &mut target));
|
||||
let mut target = fs::File::create(p)?;
|
||||
io::copy(&mut file, &mut target)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove zip
|
||||
try!(fs::remove_file(&zip_path));
|
||||
fs::remove_file(&zip_path)?;
|
||||
|
||||
// Write manifest
|
||||
let manifest_str = try!(serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization));
|
||||
let manifest_str = serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization)?;
|
||||
let manifest_path = dapp_path.join(MANIFEST_FILENAME);
|
||||
let mut manifest_file = try!(fs::File::create(manifest_path));
|
||||
try!(manifest_file.write_all(manifest_str.as_bytes()));
|
||||
let mut manifest_file = fs::File::create(manifest_path)?;
|
||||
manifest_file.write_all(manifest_str.as_bytes())?;
|
||||
// Create endpoint
|
||||
let endpoint = LocalPageEndpoint::new(dapp_path, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone());
|
||||
Ok(endpoint)
|
||||
@ -211,7 +211,7 @@ impl ContentValidator for Dapp {
|
||||
let _ = fs::remove_dir_all(&target);
|
||||
}
|
||||
(self.on_done)(result.as_ref().ok().cloned());
|
||||
result
|
||||
result.map(ValidatorResponse::Local)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,12 +129,12 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
||||
(None, endpoint.to_async_handler(path, control))
|
||||
},
|
||||
// Content is already being fetched
|
||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) => {
|
||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) if !fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
||||
(None, fetch_control.to_async_handler(path, control))
|
||||
},
|
||||
// We need to start fetching the content
|
||||
None => {
|
||||
_ => {
|
||||
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
|
||||
let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||
let content = self.resolver.resolve(content_hex);
|
||||
@ -156,7 +156,7 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
||||
},
|
||||
Some(URLHintResult::Dapp(dapp)) => {
|
||||
let (handler, fetch_control) = ContentFetcherHandler::new(
|
||||
let handler = ContentFetcherHandler::new(
|
||||
dapp.url(),
|
||||
path,
|
||||
control,
|
||||
@ -171,10 +171,10 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
||||
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
|
||||
},
|
||||
Some(URLHintResult::Content(content)) => {
|
||||
let (handler, fetch_control) = ContentFetcherHandler::new(
|
||||
let handler = ContentFetcherHandler::new(
|
||||
content.url,
|
||||
path,
|
||||
control,
|
||||
@ -189,7 +189,7 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
||||
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
|
||||
},
|
||||
None if self.sync.is_major_importing() => {
|
||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
||||
@ -224,7 +224,7 @@ mod tests {
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use util::Bytes;
|
||||
use fetch::Client;
|
||||
use fetch::{Fetch, Client};
|
||||
use hash_fetch::urlhint::{URLHint, URLHintResult};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
|
@ -37,8 +37,8 @@ fn local_dapps(dapps_path: String) -> Vec<LocalDapp> {
|
||||
|
||||
let files = files.expect("Check is done earlier");
|
||||
files.map(|dir| {
|
||||
let entry = try!(dir);
|
||||
let file_type = try!(entry.file_type());
|
||||
let entry = dir?;
|
||||
let file_type = entry.file_type()?;
|
||||
|
||||
// skip files
|
||||
if file_type.is_file() {
|
||||
@ -79,7 +79,7 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
.and_then(|mut f| {
|
||||
// Reat file
|
||||
let mut s = String::new();
|
||||
try!(f.read_to_string(&mut s).map_err(|e| format!("{:?}", e)));
|
||||
f.read_to_string(&mut s).map_err(|e| format!("{:?}", e))?;
|
||||
// Try to deserialize manifest
|
||||
deserialize_manifest(s)
|
||||
})
|
||||
|
@ -14,6 +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 std::sync::Arc;
|
||||
use endpoint::{Endpoints, Endpoint};
|
||||
use page::PageEndpoint;
|
||||
use proxypac::ProxyPac;
|
||||
@ -21,6 +22,7 @@ use web::Web;
|
||||
use fetch::Fetch;
|
||||
use parity_dapps::WebApp;
|
||||
use parity_reactor::Remote;
|
||||
use {WebProxyTokens};
|
||||
|
||||
mod cache;
|
||||
mod fs;
|
||||
@ -40,14 +42,20 @@ pub fn utils() -> Box<Endpoint> {
|
||||
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
|
||||
}
|
||||
|
||||
pub fn all_endpoints<F: Fetch>(dapps_path: String, signer_address: Option<(String, u16)>, remote: Remote, fetch: F) -> Endpoints {
|
||||
pub fn all_endpoints<F: Fetch>(
|
||||
dapps_path: String,
|
||||
signer_address: Option<(String, u16)>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Endpoints {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
|
||||
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(signer_address));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(remote, fetch));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(signer_address.clone()));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(signer_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||
|
||||
pages
|
||||
}
|
||||
|
@ -31,33 +31,41 @@ use hyper::uri::RequestUri;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::ContentHandler;
|
||||
use handlers::{ContentHandler, StreamingHandler};
|
||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||
|
||||
const FETCH_TIMEOUT: u64 = 30;
|
||||
|
||||
pub enum ValidatorResponse {
|
||||
Local(LocalPageEndpoint),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
}
|
||||
|
||||
pub trait ContentValidator: Send + 'static {
|
||||
type Error: fmt::Debug + fmt::Display;
|
||||
|
||||
fn validate_and_install(&self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
||||
}
|
||||
|
||||
enum FetchState {
|
||||
Waiting,
|
||||
NotStarted(String),
|
||||
Error(ContentHandler),
|
||||
InProgress(mpsc::Receiver<FetchState>),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
Done(LocalPageEndpoint, Box<PageHandlerWaiting>),
|
||||
}
|
||||
|
||||
enum WaitResult {
|
||||
Error(ContentHandler),
|
||||
Done(LocalPageEndpoint),
|
||||
NonAwaitable,
|
||||
}
|
||||
|
||||
pub trait ContentValidator: Send + 'static {
|
||||
type Error: fmt::Debug + fmt::Display;
|
||||
|
||||
fn validate_and_install(&self, fetch::Response) -> Result<LocalPageEndpoint, Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FetchControl {
|
||||
abort: Arc<AtomicBool>,
|
||||
listeners: Mutex<Vec<(Control, mpsc::Sender<WaitResult>)>>,
|
||||
listeners: Arc<Mutex<Vec<(Control, mpsc::Sender<WaitResult>)>>>,
|
||||
deadline: Instant,
|
||||
}
|
||||
|
||||
@ -65,7 +73,7 @@ impl Default for FetchControl {
|
||||
fn default() -> Self {
|
||||
FetchControl {
|
||||
abort: Arc::new(AtomicBool::new(false)),
|
||||
listeners: Mutex::new(Vec::new()),
|
||||
listeners: Arc::new(Mutex::new(Vec::new())),
|
||||
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
|
||||
}
|
||||
}
|
||||
@ -88,10 +96,15 @@ impl FetchControl {
|
||||
match *status {
|
||||
FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())),
|
||||
FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())),
|
||||
FetchState::Streaming(_) => self.notify(|| WaitResult::NonAwaitable),
|
||||
FetchState::NotStarted(_) | FetchState::InProgress(_) | FetchState::Waiting => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_deadline_reached(&self) -> bool {
|
||||
self.deadline < Instant::now()
|
||||
}
|
||||
|
||||
pub fn abort(&self) {
|
||||
self.abort.store(true, Ordering::SeqCst);
|
||||
}
|
||||
@ -131,7 +144,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
||||
page_handler.set_uri(&self.uri);
|
||||
FetchState::Done(endpoint, page_handler)
|
||||
},
|
||||
None => {
|
||||
_ => {
|
||||
warn!("A result for waiting request was not received.");
|
||||
FetchState::Waiting
|
||||
},
|
||||
@ -139,6 +152,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
||||
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_request_readable(decoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_request_readable(decoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_request_readable(decoder),
|
||||
_ => Next::write(),
|
||||
}
|
||||
@ -147,6 +161,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response(res),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response(res),
|
||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||
_ => Next::end(),
|
||||
}
|
||||
@ -155,22 +170,69 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Errors {
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
fn download_error<E: fmt::Debug>(&self, e: E) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Download Error",
|
||||
"There was an error when fetching the content.",
|
||||
Some(&format!("{:?}", e)),
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn invalid_content<E: fmt::Debug>(&self, e: E) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Invalid Dapp",
|
||||
"Downloaded bundle does not contain a valid content.",
|
||||
Some(&format!("{:?}", e)),
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn timeout_error(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::GatewayTimeout,
|
||||
"Download Timeout",
|
||||
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn method_not_allowed(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::MethodNotAllowed,
|
||||
"Method Not Allowed",
|
||||
"Only <code>GET</code> requests are allowed.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContentFetcherHandler<H: ContentValidator, F: Fetch> {
|
||||
fetch_control: Arc<FetchControl>,
|
||||
fetch_control: FetchControl,
|
||||
control: Control,
|
||||
remote: Remote,
|
||||
status: FetchState,
|
||||
fetch: F,
|
||||
installer: Option<H>,
|
||||
path: EndpointPath,
|
||||
uri: RequestUri,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
errors: Errors,
|
||||
}
|
||||
|
||||
impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
@ -178,70 +240,64 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
url: String,
|
||||
path: EndpointPath,
|
||||
control: Control,
|
||||
handler: H,
|
||||
installer: H,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> (Self, Arc<FetchControl>) {
|
||||
let fetch_control = Arc::new(FetchControl::default());
|
||||
let handler = ContentFetcherHandler {
|
||||
fetch_control: fetch_control.clone(),
|
||||
) -> Self {
|
||||
ContentFetcherHandler {
|
||||
fetch_control: FetchControl::default(),
|
||||
control: control,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
status: FetchState::NotStarted(url),
|
||||
installer: Some(handler),
|
||||
installer: Some(installer),
|
||||
path: path,
|
||||
uri: RequestUri::default(),
|
||||
embeddable_on: embeddable_on,
|
||||
};
|
||||
|
||||
(handler, fetch_control)
|
||||
errors: Errors {
|
||||
embeddable_on: embeddable_on,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_content(&self, url: &str, installer: H) -> mpsc::Receiver<FetchState> {
|
||||
pub fn fetch_control(&self) -> FetchControl {
|
||||
self.fetch_control.clone()
|
||||
}
|
||||
|
||||
fn fetch_content(&self, uri: RequestUri, url: &str, installer: H) -> mpsc::Receiver<FetchState> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let abort = self.fetch_control.abort.clone();
|
||||
|
||||
let control = self.control.clone();
|
||||
let embeddable_on = self.embeddable_on.clone();
|
||||
let uri = self.uri.clone();
|
||||
let path = self.path.clone();
|
||||
let tx2 = tx.clone();
|
||||
let control = self.control.clone();
|
||||
let errors = self.errors.clone();
|
||||
|
||||
let future = self.fetch.fetch_with_abort(url, abort.into()).then(move |result| {
|
||||
trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result);
|
||||
let new_state = match result {
|
||||
Ok(response) => match installer.validate_and_install(response) {
|
||||
Ok(endpoint) => {
|
||||
Ok(ValidatorResponse::Local(endpoint)) => {
|
||||
trace!(target: "dapps", "Validation OK. Returning response.");
|
||||
let mut handler = endpoint.to_page_handler(path);
|
||||
handler.set_uri(&uri);
|
||||
FetchState::Done(endpoint, handler)
|
||||
},
|
||||
Ok(ValidatorResponse::Streaming(handler)) => {
|
||||
trace!(target: "dapps", "Validation OK. Streaming response.");
|
||||
FetchState::Streaming(handler)
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(target: "dapps", "Error while validating content: {:?}", e);
|
||||
FetchState::Error(ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Invalid Dapp",
|
||||
"Downloaded bundle does not contain a valid content.",
|
||||
Some(&format!("{:?}", e)),
|
||||
embeddable_on,
|
||||
))
|
||||
FetchState::Error(errors.invalid_content(e))
|
||||
},
|
||||
},
|
||||
Err(e) => {
|
||||
warn!(target: "dapps", "Unable to fetch content: {:?}", e);
|
||||
FetchState::Error(ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Download Error",
|
||||
"There was an error when fetching the content.",
|
||||
Some(&format!("{:?}", e)),
|
||||
embeddable_on,
|
||||
))
|
||||
FetchState::Error(errors.download_error(e))
|
||||
},
|
||||
};
|
||||
// Content may be resolved when the connection is already dropped.
|
||||
let _ = tx.send(new_state);
|
||||
let _ = tx2.send(new_state);
|
||||
// Ignoring control errors
|
||||
let _ = control.ready(Next::read());
|
||||
Ok(()) as Result<(), ()>
|
||||
@ -250,7 +306,14 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
// make sure to run within fetch thread pool.
|
||||
let future = self.fetch.process(future);
|
||||
// spawn to event loop
|
||||
self.remote.spawn(future);
|
||||
let control = self.control.clone();
|
||||
let errors = self.errors.clone();
|
||||
self.remote.spawn_with_timeout(|| future, Duration::from_secs(FETCH_TIMEOUT), move || {
|
||||
// Notify about the timeout
|
||||
let _ = tx.send(FetchState::Error(errors.timeout_error()));
|
||||
// Ignoring control errors
|
||||
let _ = control.ready(Next::read());
|
||||
});
|
||||
|
||||
rx
|
||||
}
|
||||
@ -258,24 +321,19 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
|
||||
impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetcherHandler<H, F> {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
self.uri = request.uri().clone();
|
||||
let installer = self.installer.take().expect("Installer always set initialy; installer used only in on_request; on_request invoked only once; qed");
|
||||
let status = if let FetchState::NotStarted(ref url) = self.status {
|
||||
let uri = request.uri().clone();
|
||||
let installer = self.installer.take().expect("Installer always set initialy; installer used only in on_request; on_request invoked only once; qed");
|
||||
|
||||
Some(match *request.method() {
|
||||
// Start fetching content
|
||||
Method::Get => {
|
||||
trace!(target: "dapps", "Fetching content from: {:?}", url);
|
||||
let receiver = self.fetch_content(url, installer);
|
||||
let receiver = self.fetch_content(uri, url, installer);
|
||||
FetchState::InProgress(receiver)
|
||||
},
|
||||
// or return error
|
||||
_ => FetchState::Error(ContentHandler::error(
|
||||
StatusCode::MethodNotAllowed,
|
||||
"Method Not Allowed",
|
||||
"Only <code>GET</code> requests are allowed.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)),
|
||||
_ => FetchState::Error(self.errors.method_not_allowed()),
|
||||
})
|
||||
} else { None };
|
||||
|
||||
@ -290,16 +348,9 @@ impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetch
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
let (status, next) = match self.status {
|
||||
// Request may time out
|
||||
FetchState::InProgress(_) if self.fetch_control.deadline < Instant::now() => {
|
||||
FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
||||
let timeout = ContentHandler::error(
|
||||
StatusCode::GatewayTimeout,
|
||||
"Download Timeout",
|
||||
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
);
|
||||
(Some(FetchState::Error(timeout)), Next::write())
|
||||
(Some(FetchState::Error(self.errors.timeout_error())), Next::write())
|
||||
},
|
||||
FetchState::InProgress(ref receiver) => {
|
||||
// Check if there is an answer
|
||||
@ -326,6 +377,7 @@ impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetch
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.status {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response(res),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response(res),
|
||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||
_ => Next::end(),
|
||||
}
|
||||
@ -334,6 +386,7 @@ impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetch
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.status {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
_ => Next::end(),
|
||||
}
|
||||
|
@ -17,16 +17,18 @@
|
||||
//! Hyper handlers implementations.
|
||||
|
||||
mod auth;
|
||||
mod echo;
|
||||
mod content;
|
||||
mod redirect;
|
||||
mod echo;
|
||||
mod fetch;
|
||||
mod redirect;
|
||||
mod streaming;
|
||||
|
||||
pub use self::auth::AuthRequiredHandler;
|
||||
pub use self::echo::EchoHandler;
|
||||
pub use self::content::ContentHandler;
|
||||
pub use self::echo::EchoHandler;
|
||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
||||
pub use self::redirect::Redirection;
|
||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl};
|
||||
pub use self::streaming::StreamingHandler;
|
||||
|
||||
use url::Url;
|
||||
use hyper::{server, header, net, uri};
|
||||
@ -49,20 +51,30 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Extracts URL part from the Request.
|
||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||
match *req.uri() {
|
||||
convert_uri_to_url(req.uri(), req.headers().get::<header::Host>())
|
||||
}
|
||||
|
||||
/// Extracts URL given URI and Host header.
|
||||
pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) -> Option<Url> {
|
||||
match *uri {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
uri::RequestUri::AbsolutePath { ref path, .. } => {
|
||||
uri::RequestUri::AbsolutePath { ref path, ref query } => {
|
||||
let query = match *query {
|
||||
Some(ref query) => format!("?{}", query),
|
||||
None => "".into(),
|
||||
};
|
||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||
let url_string = match req.headers().get::<header::Host>() {
|
||||
let url_string = match host {
|
||||
Some(ref host) => {
|
||||
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
||||
format!("http://{}:{}{}{}", host.hostname, host.port.unwrap_or(80), path, query)
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
101
dapps/src/handlers/streaming.rs
Normal file
101
dapps/src/handlers/streaming.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
//! Content Stream Response
|
||||
|
||||
use std::io::{self, Read};
|
||||
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub struct StreamingHandler<R: io::Read> {
|
||||
buffer: [u8; BUFFER_SIZE],
|
||||
buffer_leftover: usize,
|
||||
status: StatusCode,
|
||||
content: io::BufReader<R>,
|
||||
mimetype: Mime,
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl<R: io::Read> StreamingHandler<R> {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
StreamingHandler {
|
||||
buffer: [0; BUFFER_SIZE],
|
||||
buffer_leftover: 0,
|
||||
status: status,
|
||||
content: io::BufReader::new(content),
|
||||
mimetype: mimetype,
|
||||
safe_to_embed_on: embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_initial_content(&mut self, content: &str) {
|
||||
assert_eq!(self.buffer_leftover, 0);
|
||||
let bytes = content.as_bytes();
|
||||
self.buffer_leftover = bytes.len();
|
||||
self.buffer[0..self.buffer_leftover].copy_from_slice(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.status);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
fn handle_error(e: io::Error) -> Next {
|
||||
match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
let write_pos = self.buffer_leftover;
|
||||
match self.content.read(&mut self.buffer[write_pos..]) {
|
||||
Err(e) => handle_error(e),
|
||||
Ok(read) => match encoder.write(&self.buffer[..write_pos + read]) {
|
||||
Err(e) => handle_error(e),
|
||||
Ok(0) => Next::end(),
|
||||
Ok(wrote) => {
|
||||
self.buffer_leftover = write_pos + read - wrote;
|
||||
if self.buffer_leftover > 0 {
|
||||
for i in self.buffer_leftover..write_pos + read {
|
||||
self.buffer.swap(i, i - self.buffer_leftover);
|
||||
}
|
||||
}
|
||||
Next::write()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -111,18 +111,29 @@ impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
||||
fn is_major_importing(&self) -> bool { self() }
|
||||
}
|
||||
|
||||
/// Validates Web Proxy tokens
|
||||
pub trait WebProxyTokens: Send + Sync {
|
||||
/// Should return true if token is a valid web proxy access token.
|
||||
fn is_web_proxy_token_valid(&self, token: &String) -> bool;
|
||||
}
|
||||
|
||||
impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
|
||||
fn is_web_proxy_token_valid(&self, token: &String) -> bool { self(token.to_owned()) }
|
||||
}
|
||||
|
||||
/// Webapps HTTP+RPC server build.
|
||||
pub struct ServerBuilder {
|
||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
dapps_path: String,
|
||||
handler: Arc<IoHandler>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
remote: Remote,
|
||||
fetch: Option<FetchClient>,
|
||||
fetch: Option<T>,
|
||||
}
|
||||
|
||||
impl Extendable for ServerBuilder {
|
||||
impl<T: Fetch> Extendable for ServerBuilder<T> {
|
||||
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
@ -136,30 +147,50 @@ impl ServerBuilder {
|
||||
handler: Arc::new(IoHandler::new()),
|
||||
registrar: registrar,
|
||||
sync_status: Arc::new(|| false),
|
||||
web_proxy_tokens: Arc::new(|_| false),
|
||||
signer_address: None,
|
||||
remote: remote,
|
||||
fetch: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Fetch> ServerBuilder<T> {
|
||||
/// Set a fetch client to use.
|
||||
pub fn with_fetch(&mut self, fetch: FetchClient) {
|
||||
self.fetch = Some(fetch);
|
||||
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
|
||||
ServerBuilder {
|
||||
dapps_path: self.dapps_path,
|
||||
handler: self.handler,
|
||||
registrar: self.registrar,
|
||||
sync_status: self.sync_status,
|
||||
web_proxy_tokens: self.web_proxy_tokens,
|
||||
signer_address: self.signer_address,
|
||||
remote: self.remote,
|
||||
fetch: Some(fetch),
|
||||
}
|
||||
}
|
||||
|
||||
/// Change default sync status.
|
||||
pub fn with_sync_status(&mut self, status: Arc<SyncStatus>) {
|
||||
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
|
||||
self.sync_status = status;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change default web proxy tokens validator.
|
||||
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
|
||||
self.web_proxy_tokens = tokens;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change default signer port.
|
||||
pub fn with_signer_address(&mut self, signer_address: Option<(String, u16)>) {
|
||||
pub fn signer_address(mut self, signer_address: Option<(String, u16)>) -> Self {
|
||||
self.signer_address = signer_address;
|
||||
self
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||
pub fn start_unsecured_http(self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
hosts,
|
||||
@ -169,14 +200,15 @@ impl ServerBuilder {
|
||||
self.signer_address.clone(),
|
||||
self.registrar.clone(),
|
||||
self.sync_status.clone(),
|
||||
self.web_proxy_tokens.clone(),
|
||||
self.remote.clone(),
|
||||
try!(self.fetch()),
|
||||
self.fetch_client()?,
|
||||
)
|
||||
}
|
||||
|
||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||
/// return result with `Server` handle on success or an error.
|
||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||
pub fn start_basic_auth_http(self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
hosts,
|
||||
@ -186,15 +218,16 @@ impl ServerBuilder {
|
||||
self.signer_address.clone(),
|
||||
self.registrar.clone(),
|
||||
self.sync_status.clone(),
|
||||
self.web_proxy_tokens.clone(),
|
||||
self.remote.clone(),
|
||||
try!(self.fetch()),
|
||||
self.fetch_client()?,
|
||||
)
|
||||
}
|
||||
|
||||
fn fetch(&self) -> Result<FetchClient, ServerError> {
|
||||
fn fetch_client(&self) -> Result<T, ServerError> {
|
||||
match self.fetch.clone() {
|
||||
Some(fetch) => Ok(fetch),
|
||||
None => FetchClient::new().map_err(|_| ServerError::FetchInitialization),
|
||||
None => T::new().map_err(|_| ServerError::FetchInitialization),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -241,6 +274,7 @@ impl Server {
|
||||
signer_address: Option<(String, u16)>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Result<Server, ServerError> {
|
||||
@ -253,7 +287,7 @@ impl Server {
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
));
|
||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone(), remote.clone(), fetch.clone()));
|
||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone(), web_proxy_tokens, remote.clone(), fetch.clone()));
|
||||
let cors_domains = Self::cors_domains(signer_address.clone());
|
||||
|
||||
let special = Arc::new({
|
||||
@ -268,7 +302,7 @@ impl Server {
|
||||
});
|
||||
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||
|
||||
try!(hyper::Server::http(addr))
|
||||
hyper::Server::http(addr)?
|
||||
.handle(move |ctrl| router::Router::new(
|
||||
ctrl,
|
||||
signer_address.clone(),
|
||||
|
@ -21,15 +21,16 @@ pub mod auth;
|
||||
mod host_validation;
|
||||
|
||||
use address;
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use url::{Url, Host};
|
||||
use hyper::{self, server, Next, Encoder, Decoder, Control, StatusCode};
|
||||
use hyper::{self, server, header, Next, Encoder, Decoder, Control, StatusCode};
|
||||
use hyper::net::HttpStream;
|
||||
use apps::{self, DAPPS_DOMAIN};
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||
use handlers::{Redirection, extract_url, ContentHandler};
|
||||
use handlers::{self, Redirection, ContentHandler};
|
||||
use self::auth::{Authorization, Authorized};
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
@ -57,9 +58,11 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||
|
||||
// Choose proper handler depending on path / domain
|
||||
let url = extract_url(&req);
|
||||
let url = handlers::extract_url(&req);
|
||||
let endpoint = extract_endpoint(&url);
|
||||
let referer = extract_referer_endpoint(&req);
|
||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||
let is_get_request = *req.method() == hyper::Method::Get;
|
||||
|
||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
|
||||
|
||||
@ -83,25 +86,42 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
|
||||
|
||||
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
|
||||
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
||||
self.handler = match endpoint {
|
||||
self.handler = match (endpoint.0, endpoint.1, referer) {
|
||||
// Handle invalid web requests that we can recover from
|
||||
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
|
||||
if is_get_request
|
||||
&& referer.app_id == apps::WEB_PATH
|
||||
&& self.endpoints.contains_key(apps::WEB_PATH)
|
||||
&& !is_web_endpoint(path)
|
||||
=>
|
||||
{
|
||||
trace!(target: "dapps", "Redirecting to correct web request: {:?}", referer_url);
|
||||
// TODO [ToDr] Some nice util for this!
|
||||
let using_domain = if referer.using_dapps_domains { 0 } else { 1 };
|
||||
let len = cmp::min(referer_url.path.len(), using_domain + 3); // token + protocol + hostname
|
||||
let base = referer_url.path[..len].join("/");
|
||||
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
|
||||
Redirection::boxed(&format!("/{}/{}", base, requested))
|
||||
},
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||
self.special.get(endpoint)
|
||||
.expect("special known to contain key; qed")
|
||||
.to_async_handler(path.clone().unwrap_or_default(), control)
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||
(Some(ref path), _, _) if self.endpoints.contains_key(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||
self.endpoints.get(&path.app_id)
|
||||
.expect("special known to contain key; qed")
|
||||
.expect("endpoints known to contain key; qed")
|
||||
.to_async_handler(path.clone(), control)
|
||||
},
|
||||
// Try to resolve and fetch the dapp
|
||||
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
||||
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to fetchable content.");
|
||||
self.fetch.to_async_handler(path.clone(), control)
|
||||
},
|
||||
@ -110,7 +130,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
// It should be safe to remove it in (near) future.
|
||||
//
|
||||
// 404 for non-existent content
|
||||
(Some(ref path), _) if *req.method() == hyper::Method::Get && path.app_id != "home" => {
|
||||
(Some(ref path), _, _) if is_get_request && path.app_id != "home" => {
|
||||
trace!(target: "dapps", "Resolving to 404.");
|
||||
Box::new(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
@ -121,7 +141,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
))
|
||||
},
|
||||
// Redirect any other GET request to signer.
|
||||
_ if *req.method() == hyper::Method::Get => {
|
||||
_ if is_get_request => {
|
||||
if let Some(signer_address) = self.signer_address.clone() {
|
||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||
Redirection::boxed(&format!("http://{}", address(signer_address)))
|
||||
@ -192,6 +212,23 @@ impl<A: Authorization> Router<A> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
|
||||
match *path {
|
||||
Some(ref path) if path.app_id == apps::WEB_PATH => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_referer_endpoint(req: &server::Request<HttpStream>) -> Option<(EndpointPath, Url)> {
|
||||
let referer = req.headers().get::<header::Referer>();
|
||||
|
||||
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
|
||||
url.and_then(|url| {
|
||||
let option = Some(url);
|
||||
extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||
if url.path.len() <= 1 {
|
||||
|
@ -51,16 +51,16 @@ impl Endpoint for RpcEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
const MIDDLEWARE_METHOD: &'static str = "eth_accounts";
|
||||
|
||||
struct RpcMiddleware {
|
||||
handler: Arc<IoHandler>,
|
||||
methods: Vec<String>,
|
||||
}
|
||||
|
||||
impl RpcMiddleware {
|
||||
fn new(handler: Arc<IoHandler>) -> Self {
|
||||
RpcMiddleware {
|
||||
handler: handler,
|
||||
methods: vec!["eth_accounts".into(), "parity_accountsInfo".into()],
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,9 +68,9 @@ impl RpcMiddleware {
|
||||
fn augment_request(&self, request: &mut Request, meta: Option<Meta>) {
|
||||
use jsonrpc_core::{Call, Params, to_value};
|
||||
|
||||
fn augment_call(call: &mut Call, meta: Option<&Meta>) {
|
||||
fn augment_call(call: &mut Call, meta: Option<&Meta>, methods: &Vec<String>) {
|
||||
match (call, meta) {
|
||||
(&mut Call::MethodCall(ref mut method_call), Some(meta)) if &method_call.method == MIDDLEWARE_METHOD => {
|
||||
(&mut Call::MethodCall(ref mut method_call), Some(meta)) if methods.contains(&method_call.method) => {
|
||||
let session = to_value(&meta.app_id);
|
||||
|
||||
let params = match method_call.params {
|
||||
@ -86,10 +86,10 @@ impl RpcMiddleware {
|
||||
}
|
||||
|
||||
match *request {
|
||||
Request::Single(ref mut call) => augment_call(call, meta.as_ref()),
|
||||
Request::Single(ref mut call) => augment_call(call, meta.as_ref(), &self.methods),
|
||||
Request::Batch(ref mut vec) => {
|
||||
for mut call in vec {
|
||||
augment_call(call, meta.as_ref())
|
||||
augment_call(call, meta.as_ref(), &self.methods)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -14,7 +14,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers_for_embed};
|
||||
use devtools::http_client;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use tests::helpers::{
|
||||
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
||||
serve_with_registrar_and_fetch, serve_with_registrar_and_fetch_and_threads,
|
||||
request, assert_security_headers_for_embed,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn should_resolve_dapp() {
|
||||
@ -32,7 +38,7 @@ fn should_resolve_dapp() {
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
@ -41,14 +47,6 @@ fn should_resolve_dapp() {
|
||||
fn should_return_503_when_syncing_but_should_make_the_calls() {
|
||||
// given
|
||||
let (server, registrar) = serve_with_registrar_and_sync();
|
||||
{
|
||||
let mut responses = registrar.responses.lock();
|
||||
let res1 = responses.get(0).unwrap().clone();
|
||||
let res2 = responses.get(1).unwrap().clone();
|
||||
// Registrar will be called twice - fill up the responses.
|
||||
responses.push(res1);
|
||||
responses.push(res2);
|
||||
}
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@ -61,7 +59,378 @@ fn should_return_503_when_syncing_but_should_make_the_calls() {
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 503 Service Unavailable".to_owned());
|
||||
response.assert_status("HTTP/1.1 503 Service Unavailable");
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
const GAVCOIN_DAPP: &'static str = "00000000000000000000000000000000000000000000000000000000000000609faf32e1e3845e237cc6efd27187cee13b3b99db000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e00000000000000000000000000000000000000000000000000000000000000116761766f66796f726b2f676176636f696e000000000000000000000000000000";
|
||||
const GAVCOIN_ICON: &'static str = "00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e000000000000000000000000000000000000000000000000000000000000007768747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f657468636f72652f646170702d6173736574732f623838653938336162616131613661363334356238643934343863313562313137646462353430652f746f6b656e732f676176636f696e2d36347836342e706e67000000000000000000";
|
||||
|
||||
#[test]
|
||||
fn should_return_502_on_hash_mismatch() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response.assert_status("HTTP/1.1 502 Bad Gateway");
|
||||
assert!(response.body.contains("HashMismatch"), "Expected hash mismatch response, got: {:?}", response.body);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_for_invalid_dapp_zip() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response.assert_status("HTTP/1.1 502 Bad Gateway");
|
||||
assert!(response.body.contains("InvalidArchive"), "Expected invalid zip response, got: {:?}", response.body);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_fetched_dapp_content() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
fetch.set_response(include_bytes!("../../res/gavcoin.zip"));
|
||||
|
||||
// when
|
||||
let response1 = http_client::request(server.addr(),
|
||||
"\
|
||||
GET /index.html HTTP/1.1\r\n\
|
||||
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
let response2 = http_client::request(server.addr(),
|
||||
"\
|
||||
GET /manifest.json HTTP/1.1\r\n\
|
||||
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response1.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response1.headers);
|
||||
assert_eq!(
|
||||
response1.body,
|
||||
r#"18
|
||||
<h1>Hello Gavcoin!</h1>
|
||||
|
||||
"#
|
||||
);
|
||||
|
||||
response2.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response2.headers);
|
||||
assert_eq!(
|
||||
response2.body,
|
||||
r#"BE
|
||||
{
|
||||
"id": "9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a",
|
||||
"name": "Gavcoin",
|
||||
"description": "Gavcoin",
|
||||
"version": "1.0.0",
|
||||
"author": "",
|
||||
"iconUrl": "icon.png"
|
||||
}
|
||||
0
|
||||
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_fetched_content() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_security_headers_present(None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_cache_content() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
let request_str = "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
";
|
||||
|
||||
let response = http_client::request(server.addr(), request_str);
|
||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
||||
fetch.assert_no_more_requests();
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
|
||||
// when
|
||||
let response = http_client::request(server.addr(), request_str);
|
||||
|
||||
// then
|
||||
fetch.assert_no_more_requests();
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_request_content_twice() {
|
||||
use std::thread;
|
||||
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch_and_threads(true);
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
let request_str = "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
";
|
||||
let fire_request = || {
|
||||
let addr = server.addr().to_owned();
|
||||
let req = request_str.to_owned();
|
||||
thread::spawn(move || {
|
||||
http_client::request(&addr, &req)
|
||||
})
|
||||
};
|
||||
let control = fetch.manual();
|
||||
|
||||
// when
|
||||
|
||||
// Fire two requests at the same time
|
||||
let r1 = fire_request();
|
||||
let r2 = fire_request();
|
||||
|
||||
// wait for single request in fetch, the second one should go into waiting state.
|
||||
control.wait_for_requests(1);
|
||||
control.respond();
|
||||
|
||||
let response1 = r1.join().unwrap();
|
||||
let response2 = r2.join().unwrap();
|
||||
|
||||
// then
|
||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
||||
fetch.assert_no_more_requests();
|
||||
response1.assert_status("HTTP/1.1 200 OK");
|
||||
response2.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_stream_web_content() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /web/token/https/parity.io/ HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_requested("https://parity.io/");
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_on_invalid_token() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /web/invalidtoken/https/parity.io/ HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_on_invalid_protocol() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /web/token/ftp/parity.io/ HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_redirect_if_trailing_slash_is_missing() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /web/token/https/parity.io HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
response.assert_header("Location", "/web/token/https/parity.io/");
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_disallow_non_get_requests() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /token/https/parity.io/ HTTP/1.1\r\n\
|
||||
Host: web.parity\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
123\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 405 Method Not Allowed");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fix_absolute_requests_based_on_referer() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /styles.css HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Referer: http://localhost:8080/web/token/https/parity.io/\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
response.assert_header("Location", "/web/token/https/parity.io/styles.css");
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
122
dapps/src/tests/helpers/fetch.rs
Normal file
122
dapps/src/tests/helpers/fetch.rs
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
use std::{io, thread, time};
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use util::Mutex;
|
||||
|
||||
use futures::{self, Future};
|
||||
use fetch::{self, Fetch};
|
||||
|
||||
pub struct FetchControl {
|
||||
sender: mpsc::Sender<()>,
|
||||
fetch: FakeFetch,
|
||||
}
|
||||
|
||||
impl FetchControl {
|
||||
pub fn respond(self) {
|
||||
self.sender.send(())
|
||||
.expect("Fetch cannot be finished without sending a response at least once.");
|
||||
}
|
||||
|
||||
pub fn wait_for_requests(&self, len: usize) {
|
||||
const MAX_TIMEOUT_MS: u64 = 5000;
|
||||
const ATTEMPTS: u64 = 10;
|
||||
let mut attempts_left = ATTEMPTS;
|
||||
loop {
|
||||
let current = self.fetch.requested.lock().len();
|
||||
|
||||
if current == len {
|
||||
break;
|
||||
} else if attempts_left == 0 {
|
||||
panic!(
|
||||
"Timeout reached when waiting for pending requests. Expected: {}, current: {}",
|
||||
len, current
|
||||
);
|
||||
} else {
|
||||
attempts_left -= 1;
|
||||
// Should we handle spurious timeouts better?
|
||||
thread::park_timeout(time::Duration::from_millis(MAX_TIMEOUT_MS / ATTEMPTS));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FakeFetch {
|
||||
manual: Arc<Mutex<Option<mpsc::Receiver<()>>>>,
|
||||
response: Arc<Mutex<Option<&'static [u8]>>>,
|
||||
asserted: Arc<atomic::AtomicUsize>,
|
||||
requested: Arc<Mutex<Vec<String>>>,
|
||||
}
|
||||
|
||||
impl FakeFetch {
|
||||
pub fn set_response(&self, data: &'static [u8]) {
|
||||
*self.response.lock() = Some(data);
|
||||
}
|
||||
|
||||
pub fn manual(&self) -> FetchControl {
|
||||
assert!(self.manual.lock().is_none(), "Only one manual control may be active.");
|
||||
let (tx, rx) = mpsc::channel();
|
||||
*self.manual.lock() = Some(rx);
|
||||
|
||||
FetchControl {
|
||||
sender: tx,
|
||||
fetch: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_requested(&self, url: &str) {
|
||||
let requests = self.requested.lock();
|
||||
let idx = self.asserted.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
|
||||
assert_eq!(requests.get(idx), Some(&url.to_owned()), "Expected fetch from specific URL.");
|
||||
}
|
||||
|
||||
pub fn assert_no_more_requests(&self) {
|
||||
let requests = self.requested.lock();
|
||||
let len = self.asserted.load(atomic::Ordering::SeqCst);
|
||||
assert_eq!(requests.len(), len, "Didn't expect any more requests, got: {:?}", &requests[len..]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Fetch for FakeFetch {
|
||||
type Result = futures::BoxFuture<fetch::Response, fetch::Error>;
|
||||
|
||||
fn new() -> Result<Self, fetch::Error> where Self: Sized {
|
||||
Ok(FakeFetch::default())
|
||||
}
|
||||
|
||||
fn fetch_with_abort(&self, url: &str, _abort: fetch::Abort) -> Self::Result {
|
||||
self.requested.lock().push(url.into());
|
||||
let manual = self.manual.clone();
|
||||
let response = self.response.clone();
|
||||
|
||||
let (tx, rx) = futures::oneshot();
|
||||
thread::spawn(move || {
|
||||
if let Some(rx) = manual.lock().take() {
|
||||
// wait for manual resume
|
||||
let _ = rx.recv();
|
||||
}
|
||||
|
||||
let data = response.lock().take().unwrap_or(b"Some content");
|
||||
let cursor = io::Cursor::new(data);
|
||||
tx.complete(fetch::Response::from_reader(cursor));
|
||||
});
|
||||
|
||||
rx.map_err(|_| fetch::Error::Aborted).boxed()
|
||||
}
|
||||
}
|
@ -17,50 +17,22 @@
|
||||
use std::env;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use env_logger::LogBuilder;
|
||||
|
||||
use ServerBuilder;
|
||||
use Server;
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use util::{Bytes, Address, Mutex, ToPretty};
|
||||
use fetch::Fetch;
|
||||
use devtools::http_client;
|
||||
use parity_reactor::Remote;
|
||||
|
||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||
mod registrar;
|
||||
mod fetch;
|
||||
|
||||
use self::registrar::FakeRegistrar;
|
||||
use self::fetch::FakeFetch;
|
||||
|
||||
const SIGNER_PORT: u16 = 18180;
|
||||
|
||||
pub struct FakeRegistrar {
|
||||
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
||||
pub responses: Mutex<Vec<Result<Bytes, String>>>,
|
||||
}
|
||||
|
||||
impl FakeRegistrar {
|
||||
fn new() -> Self {
|
||||
FakeRegistrar {
|
||||
calls: Arc::new(Mutex::new(Vec::new())),
|
||||
responses: Mutex::new(
|
||||
vec![
|
||||
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
||||
Ok(Vec::new())
|
||||
]
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContractClient for FakeRegistrar {
|
||||
fn registrar(&self) -> Result<Address, String> {
|
||||
Ok(REGISTRAR.parse().unwrap())
|
||||
}
|
||||
|
||||
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
self.calls.lock().push((address.to_hex(), data.to_hex()));
|
||||
self.responses.lock().remove(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
// Initialize logger
|
||||
if let Ok(log) = env::var("RUST_LOG") {
|
||||
@ -70,16 +42,21 @@ fn init_logger() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_server(hosts: Option<Vec<String>>, is_syncing: bool) -> (Server, Arc<FakeRegistrar>) {
|
||||
pub fn init_server<F, B>(hosts: Option<Vec<String>>, process: F, remote: Remote) -> (Server, Arc<FakeRegistrar>) where
|
||||
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
||||
B: Fetch,
|
||||
{
|
||||
init_logger();
|
||||
let registrar = Arc::new(FakeRegistrar::new());
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone(), Remote::new_sync());
|
||||
builder.with_sync_status(Arc::new(move || is_syncing));
|
||||
builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)));
|
||||
let server = process(ServerBuilder::new(
|
||||
dapps_path.to_str().unwrap().into(), registrar.clone(), remote,
|
||||
))
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap();
|
||||
(
|
||||
builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(),
|
||||
server,
|
||||
registrar,
|
||||
)
|
||||
}
|
||||
@ -89,25 +66,53 @@ pub fn serve_with_auth(user: &str, pass: &str) -> Server {
|
||||
let registrar = Arc::new(FakeRegistrar::new());
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone(), Remote::new_sync());
|
||||
builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)));
|
||||
builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
|
||||
ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone(), Remote::new_sync())
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
|
||||
}
|
||||
|
||||
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
||||
init_server(hosts, false).0
|
||||
init_server(hosts, |builder| builder, Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(None, false)
|
||||
init_server(None, |builder| builder, Remote::new_sync())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(None, true)
|
||||
init_server(None, |builder| {
|
||||
builder.sync_status(Arc::new(|| true))
|
||||
}, Remote::new_sync())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||
serve_with_registrar_and_fetch_and_threads(false)
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, reg) = init_server(None, move |builder| {
|
||||
builder.fetch(f.clone())
|
||||
}, if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
|
||||
|
||||
(server, fetch, reg)
|
||||
}
|
||||
|
||||
pub fn serve_with_fetch(web_token: &'static str) -> (Server, FakeFetch) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, _) = init_server(None, move |builder| {
|
||||
builder
|
||||
.fetch(f.clone())
|
||||
.web_proxy_tokens(Arc::new(move |token| &token == web_token))
|
||||
}, Remote::new_sync());
|
||||
|
||||
(server, fetch)
|
||||
}
|
||||
|
||||
pub fn serve() -> Server {
|
||||
init_server(None, false).0
|
||||
init_server(None, |builder| builder, Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
72
dapps/src/tests/helpers/registrar.rs
Normal file
72
dapps/src/tests/helpers/registrar.rs
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use util::{Bytes, Address, Mutex, H256, ToPretty};
|
||||
|
||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||
const URLHINT_RESOLVE: &'static str = "267b6922";
|
||||
const DEFAULT_HASH: &'static str = "1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d";
|
||||
|
||||
pub struct FakeRegistrar {
|
||||
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
||||
pub responses: Mutex<HashMap<(String, String), Result<Bytes, String>>>,
|
||||
}
|
||||
|
||||
impl FakeRegistrar {
|
||||
pub fn new() -> Self {
|
||||
FakeRegistrar {
|
||||
calls: Arc::new(Mutex::new(Vec::new())),
|
||||
responses: Mutex::new({
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
(REGISTRAR.into(), "6795dbcd058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000000".into()),
|
||||
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
||||
);
|
||||
map.insert(
|
||||
(URLHINT.into(), format!("{}{}", URLHINT_RESOLVE, DEFAULT_HASH)),
|
||||
Ok(vec![])
|
||||
);
|
||||
map
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_result(&self, hash: H256, result: Result<Bytes, String>) {
|
||||
self.responses.lock().insert(
|
||||
(URLHINT.into(), format!("{}{:?}", URLHINT_RESOLVE, hash)),
|
||||
result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl ContractClient for FakeRegistrar {
|
||||
fn registrar(&self) -> Result<Address, String> {
|
||||
Ok(REGISTRAR.parse().unwrap())
|
||||
}
|
||||
|
||||
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
let call = (address.to_hex(), data.to_hex());
|
||||
self.calls.lock().push(call.clone());
|
||||
self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call))
|
||||
}
|
||||
}
|
@ -37,6 +37,9 @@ pub struct Url {
|
||||
/// Empty entries of `""` correspond to trailing slashes.
|
||||
pub path: Vec<String>,
|
||||
|
||||
/// The URL query.
|
||||
pub query: Option<String>,
|
||||
|
||||
/// The URL username field, from the userinfo section of the URL.
|
||||
///
|
||||
/// `None` if the `@` character was not part of the input OR
|
||||
@ -82,15 +85,17 @@ impl Url {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let port = try!(raw_url.port_or_known_default().ok_or_else(|| format!("Unknown port for scheme: `{}`", raw_url.scheme())));
|
||||
let host = try!(raw_url.host().ok_or_else(|| "Valid host, because only data:, mailto: protocols does not have host.".to_owned())).to_owned();
|
||||
let path = try!(raw_url.path_segments().ok_or_else(|| "Valid path segments. In HTTP we won't get cannot-be-a-base URLs".to_owned()))
|
||||
let port = raw_url.port_or_known_default().ok_or_else(|| format!("Unknown port for scheme: `{}`", raw_url.scheme()))?;
|
||||
let host = raw_url.host().ok_or_else(|| "Valid host, because only data:, mailto: protocols does not have host.".to_owned())?.to_owned();
|
||||
let path = raw_url.path_segments().ok_or_else(|| "Valid path segments. In HTTP we won't get cannot-be-a-base URLs".to_owned())?
|
||||
.map(|part| part.to_owned()).collect();
|
||||
let query = raw_url.query().map(|x| x.to_owned());
|
||||
|
||||
Ok(Url {
|
||||
port: port,
|
||||
host: host,
|
||||
path: path,
|
||||
query: query,
|
||||
raw: raw_url,
|
||||
username: username,
|
||||
password: password,
|
||||
|
106
dapps/src/web.rs
106
dapps/src/web.rs
@ -16,24 +16,36 @@
|
||||
|
||||
//! Serving web-based content (proxying)
|
||||
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::{ContentFetcherHandler, ContentHandler, ContentValidator, Redirection, extract_url};
|
||||
use page::{LocalPageEndpoint};
|
||||
use std::sync::Arc;
|
||||
use fetch::{self, Fetch};
|
||||
use url::Url;
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use hyper::{self, server, net, Next, Encoder, Decoder};
|
||||
use hyper::status::StatusCode;
|
||||
use parity_reactor::Remote;
|
||||
use apps::WEB_PATH;
|
||||
|
||||
use apps;
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::{
|
||||
ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse,
|
||||
StreamingHandler, Redirection, extract_url,
|
||||
};
|
||||
use url::Url;
|
||||
use WebProxyTokens;
|
||||
|
||||
pub type Embeddable = Option<(String, u16)>;
|
||||
|
||||
pub struct Web<F> {
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
}
|
||||
|
||||
impl<F: Fetch> Web<F> {
|
||||
pub fn boxed(remote: Remote, fetch: F) -> Box<Endpoint> {
|
||||
pub fn boxed(embeddable_on: Embeddable, web_proxy_tokens: Arc<WebProxyTokens>, remote: Remote, fetch: F) -> Box<Endpoint> {
|
||||
Box::new(Web {
|
||||
embeddable_on: embeddable_on,
|
||||
web_proxy_tokens: web_proxy_tokens,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
})
|
||||
@ -48,20 +60,33 @@ impl<F: Fetch> Endpoint for Web<F> {
|
||||
path: path,
|
||||
remote: self.remote.clone(),
|
||||
fetch: self.fetch.clone(),
|
||||
web_proxy_tokens: self.web_proxy_tokens.clone(),
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebInstaller;
|
||||
struct WebInstaller {
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl ContentValidator for WebInstaller {
|
||||
type Error = String;
|
||||
|
||||
fn validate_and_install(&self, _response: fetch::Response) -> Result<LocalPageEndpoint, String> {
|
||||
// let path = unimplemented!();
|
||||
// let mime = response.content_type().unwrap_or(mime!(Text/Html));
|
||||
// Ok(LocalPageEndpoint::single_file(path, mime, PageCache::Enabled))
|
||||
Err("unimplemented".into())
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
||||
let status = StatusCode::from_u16(response.status().to_u16());
|
||||
let is_html = response.is_html();
|
||||
let mime = response.content_type().unwrap_or(mime!(Text/Html));
|
||||
let mut handler = StreamingHandler::new(
|
||||
response,
|
||||
status,
|
||||
mime,
|
||||
self.embeddable_on.clone(),
|
||||
);
|
||||
if is_html {
|
||||
handler.set_initial_content(&format!(r#"<script src="/{}/inject.js"></script>"#, apps::UTILS_PATH));
|
||||
}
|
||||
Ok(ValidatorResponse::Streaming(handler))
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,46 +103,61 @@ struct WebHandler<F: Fetch> {
|
||||
path: EndpointPath,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl<F: Fetch> WebHandler<F> {
|
||||
fn extract_target_url(url: Option<Url>) -> Result<String, State<F>> {
|
||||
let path = match url {
|
||||
Some(url) => url.path,
|
||||
fn extract_target_url(&self, url: Option<Url>) -> Result<String, State<F>> {
|
||||
let (path, query) = match url {
|
||||
Some(url) => (url.path, url.query),
|
||||
None => {
|
||||
return Err(State::Error(
|
||||
ContentHandler::error(StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, None)
|
||||
));
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
// TODO [ToDr] Check if token supplied in URL is correct.
|
||||
|
||||
// Support domain based routing.
|
||||
let idx = match path.get(0).map(|m| m.as_ref()) {
|
||||
Some(WEB_PATH) => 1,
|
||||
Some(apps::WEB_PATH) => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// Check if token supplied in URL is correct.
|
||||
match path.get(idx) {
|
||||
Some(ref token) if self.web_proxy_tokens.is_web_proxy_token_valid(token) => {},
|
||||
_ => {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate protocol
|
||||
let protocol = match path.get(idx).map(|a| a.as_str()) {
|
||||
let protocol = match path.get(idx + 1).map(|a| a.as_str()) {
|
||||
Some("http") => "http",
|
||||
Some("https") => "https",
|
||||
_ => {
|
||||
return Err(State::Error(
|
||||
ContentHandler::error(StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used", None, None)
|
||||
));
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
// Redirect if address to main page does not end with /
|
||||
if let None = path.get(idx + 2) {
|
||||
if let None = path.get(idx + 3) {
|
||||
return Err(State::Redirecting(
|
||||
Redirection::new(&format!("/{}/", path.join("/")))
|
||||
));
|
||||
}
|
||||
|
||||
Ok(format!("{}://{}", protocol, path[2..].join("/")))
|
||||
let query = match query {
|
||||
Some(query) => format!("?{}", query),
|
||||
None => "".into(),
|
||||
};
|
||||
|
||||
Ok(format!("{}://{}{}", protocol, path[idx + 2..].join("/"), query))
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +166,7 @@ impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
|
||||
let url = extract_url(&request);
|
||||
|
||||
// First extract the URL (reject invalid URLs)
|
||||
let target_url = match Self::extract_target_url(url) {
|
||||
let target_url = match self.extract_target_url(url) {
|
||||
Ok(url) => url,
|
||||
Err(error) => {
|
||||
self.state = error;
|
||||
@ -134,12 +174,14 @@ impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
|
||||
}
|
||||
};
|
||||
|
||||
let (mut handler, _control) = ContentFetcherHandler::new(
|
||||
let mut handler = ContentFetcherHandler::new(
|
||||
target_url,
|
||||
self.path.clone(),
|
||||
self.control.clone(),
|
||||
WebInstaller,
|
||||
None,
|
||||
WebInstaller {
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
},
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
@ -73,10 +73,10 @@ impl WriteCache {
|
||||
|
||||
match *cache_entry {
|
||||
WriteCacheEntry::Write(ref val) => {
|
||||
try!(batch.put(&key, val));
|
||||
batch.put(&key, val)?;
|
||||
},
|
||||
WriteCacheEntry::Remove => {
|
||||
try!(batch.delete(&key));
|
||||
batch.delete(&key)?;
|
||||
},
|
||||
}
|
||||
key.clone()
|
||||
@ -87,14 +87,14 @@ impl WriteCache {
|
||||
removed_so_far = removed_so_far + 1;
|
||||
}
|
||||
if removed_so_far > 0 {
|
||||
try!(db.write(batch));
|
||||
db.write(batch)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// flushes until cache is empty
|
||||
fn flush_all(&mut self, db: &DB) -> Result<(), Error> {
|
||||
while !self.is_empty() { try!(self.flush(db, FLUSH_BATCH_SIZE)); }
|
||||
while !self.is_empty() { self.flush(db, FLUSH_BATCH_SIZE)?; }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ impl WriteCache {
|
||||
|
||||
fn try_shrink(&mut self, db: &DB) -> Result<(), Error> {
|
||||
if self.entries.len() > self.preferred_len {
|
||||
try!(self.flush(db, FLUSH_BATCH_SIZE));
|
||||
self.flush(db, FLUSH_BATCH_SIZE)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -135,7 +135,7 @@ impl Database {
|
||||
if db_lock.is_none() { return Ok(()); }
|
||||
let db = db_lock.as_ref().unwrap();
|
||||
|
||||
try!(cache_lock.try_shrink(&db));
|
||||
cache_lock.try_shrink(&db)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ impl Database {
|
||||
if db_lock.is_none() { return Ok(()); }
|
||||
let db = db_lock.as_ref().expect("we should have exited with Ok(()) on the previous step");
|
||||
|
||||
try!(cache_lock.flush_all(&db));
|
||||
cache_lock.flush_all(&db)?;
|
||||
Ok(())
|
||||
|
||||
}
|
||||
@ -174,7 +174,7 @@ impl DatabaseService for Database {
|
||||
opts.set_block_based_table_factory(&block_opts);
|
||||
opts.set_prefix_extractor_fixed_size(size);
|
||||
}
|
||||
*db = Some(try!(DB::open(&opts, &path)));
|
||||
*db = Some(DB::open(&opts, &path)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -185,7 +185,7 @@ impl DatabaseService for Database {
|
||||
}
|
||||
|
||||
fn close(&self) -> Result<(), Error> {
|
||||
try!(self.flush_all());
|
||||
self.flush_all()?;
|
||||
|
||||
let mut db = self.db.write();
|
||||
if db.is_none() { return Err(Error::IsClosed); }
|
||||
@ -231,9 +231,9 @@ impl DatabaseService for Database {
|
||||
}
|
||||
}
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
match try!(db.get(key)) {
|
||||
match db.get(key)? {
|
||||
Some(db_vec) => {
|
||||
Ok(Some(db_vec.to_vec()))
|
||||
},
|
||||
@ -243,7 +243,7 @@ impl DatabaseService for Database {
|
||||
|
||||
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
let mut iter = db.iterator(IteratorMode::From(prefix, Direction::Forward));
|
||||
match iter.next() {
|
||||
@ -255,14 +255,14 @@ impl DatabaseService for Database {
|
||||
|
||||
fn is_empty(&self) -> Result<bool, Error> {
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
let db = 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();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
let mut iterators = self.iterators.write();
|
||||
let next_iterator = iterators.keys().last().unwrap_or(&0) + 1;
|
||||
|
@ -52,27 +52,27 @@ impl std::convert::From<nanoipc::SocketError> for ServiceError {
|
||||
|
||||
pub fn blocks_service_url(db_path: &str) -> Result<String, std::io::Error> {
|
||||
let mut path = PathBuf::from(db_path);
|
||||
try!(::std::fs::create_dir_all(db_path));
|
||||
::std::fs::create_dir_all(db_path)?;
|
||||
path.push("blocks.ipc");
|
||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
||||
}
|
||||
|
||||
pub fn extras_service_url(db_path: &str) -> Result<String, ::std::io::Error> {
|
||||
let mut path = PathBuf::from(db_path);
|
||||
try!(::std::fs::create_dir_all(db_path));
|
||||
::std::fs::create_dir_all(db_path)?;
|
||||
path.push("extras.ipc");
|
||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
||||
}
|
||||
|
||||
pub fn blocks_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
||||
let url = try!(blocks_service_url(db_path));
|
||||
let client = try!(nanoipc::generic_client::<DatabaseClient<_>>(&url));
|
||||
let url = blocks_service_url(db_path)?;
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(&url)?;
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub fn extras_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
||||
let url = try!(extras_service_url(db_path));
|
||||
let client = try!(nanoipc::generic_client::<DatabaseClient<_>>(&url));
|
||||
let url = extras_service_url(db_path)?;
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(&url)?;
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,21 @@ pub struct Response {
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn assert_header(&self, header: &str, value: &str) {
|
||||
let header = format!("{}: {}", header, value);
|
||||
assert!(self.headers.iter().find(|h| *h == &header).is_some(), "Couldn't find header {} in {:?}", header, &self.headers)
|
||||
}
|
||||
|
||||
pub fn assert_status(&self, status: &str) {
|
||||
assert_eq!(self.status, status.to_owned(), "Got unexpected code. Body: {:?}", self.body);
|
||||
}
|
||||
|
||||
pub fn assert_security_headers_present(&self, port: Option<u16>) {
|
||||
assert_security_headers_present(&self.headers, port)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_block(lines: &mut Lines, all: bool) -> String {
|
||||
let mut block = String::new();
|
||||
loop {
|
||||
@ -65,7 +80,7 @@ fn connect(address: &SocketAddr) -> TcpStream {
|
||||
|
||||
pub fn request(address: &SocketAddr, request: &str) -> Response {
|
||||
let mut req = connect(address);
|
||||
req.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
|
||||
req.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
|
||||
req.write_all(request.as_bytes()).unwrap();
|
||||
|
||||
let mut response = String::new();
|
||||
|
@ -116,17 +116,17 @@ impl Light {
|
||||
pub fn from_file(block_number: u64) -> io::Result<Light> {
|
||||
let seed_compute = SeedHashCompute::new();
|
||||
let path = Light::file_path(seed_compute.get_seedhash(block_number));
|
||||
let mut file = try!(File::open(path));
|
||||
let mut file = File::open(path)?;
|
||||
|
||||
let cache_size = get_cache_size(block_number);
|
||||
if try!(file.metadata()).len() != cache_size as u64 {
|
||||
if file.metadata()?.len() != cache_size as u64 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Cache file size mismatch"));
|
||||
}
|
||||
let num_nodes = cache_size / NODE_BYTES;
|
||||
let mut nodes: Vec<Node> = Vec::new();
|
||||
nodes.resize(num_nodes, unsafe { mem::uninitialized() });
|
||||
let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) };
|
||||
try!(file.read_exact(buf));
|
||||
file.read_exact(buf)?;
|
||||
Ok(Light {
|
||||
cache: nodes,
|
||||
block_number: block_number,
|
||||
@ -144,16 +144,16 @@ impl Light {
|
||||
|
||||
if deprecated.exists() {
|
||||
debug!(target: "ethash", "removing: {:?}", &deprecated);
|
||||
try!(fs::remove_file(deprecated));
|
||||
fs::remove_file(deprecated)?;
|
||||
}
|
||||
}
|
||||
|
||||
try!(fs::create_dir_all(path.parent().unwrap()));
|
||||
let mut file = try!(File::create(&path));
|
||||
fs::create_dir_all(path.parent().unwrap())?;
|
||||
let mut file = File::create(&path)?;
|
||||
|
||||
let cache_size = self.cache.len() * NODE_BYTES;
|
||||
let buf = unsafe { slice::from_raw_parts(self.cache.as_ptr() as *const u8, cache_size) };
|
||||
try!(file.write(buf));
|
||||
file.write(buf)?;
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ use ethcore::transaction::PendingTransaction;
|
||||
use ethcore::blockchain_info::BlockChainInfo;
|
||||
use ethcore::spec::Spec;
|
||||
use ethcore::service::ClientIoMessage;
|
||||
use ethcore::encoded;
|
||||
use io::IoChannel;
|
||||
|
||||
use util::hash::{H256, H256FastMap};
|
||||
@ -233,11 +234,11 @@ impl Provider for Client {
|
||||
None
|
||||
}
|
||||
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||
self.chain.get_header(id)
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
||||
self.chain.get_header(id).map(encoded::Header::new)
|
||||
}
|
||||
|
||||
fn block_body(&self, _id: BlockId) -> Option<Bytes> {
|
||||
fn block_body(&self, _id: BlockId) -> Option<encoded::Body> {
|
||||
None
|
||||
}
|
||||
|
||||
@ -253,7 +254,7 @@ impl Provider for Client {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn header_proof(&self, _req: request::HeaderProof) -> Option<(Bytes, Vec<Bytes>)> {
|
||||
fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -135,10 +135,10 @@ impl RlpDecodable for CostTable {
|
||||
let mut header_proofs = None;
|
||||
|
||||
for row in rlp.iter() {
|
||||
let msg_id: u8 = try!(row.val_at(0));
|
||||
let msg_id: u8 = row.val_at(0)?;
|
||||
let cost = {
|
||||
let base = try!(row.val_at(1));
|
||||
let per = try!(row.val_at(2));
|
||||
let base = row.val_at(1)?;
|
||||
let per = row.val_at(2)?;
|
||||
|
||||
Cost(base, per)
|
||||
};
|
||||
@ -155,12 +155,12 @@ impl RlpDecodable for CostTable {
|
||||
}
|
||||
|
||||
Ok(CostTable {
|
||||
headers: try!(headers.ok_or(DecoderError::Custom("No headers cost specified"))),
|
||||
bodies: try!(bodies.ok_or(DecoderError::Custom("No bodies cost specified"))),
|
||||
receipts: try!(receipts.ok_or(DecoderError::Custom("No receipts cost specified"))),
|
||||
state_proofs: try!(state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))),
|
||||
contract_codes: try!(contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))),
|
||||
header_proofs: try!(header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))),
|
||||
headers: headers.ok_or(DecoderError::Custom("No headers cost specified"))?,
|
||||
bodies: bodies.ok_or(DecoderError::Custom("No bodies cost specified"))?,
|
||||
receipts: receipts.ok_or(DecoderError::Custom("No receipts cost specified"))?,
|
||||
state_proofs: state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))?,
|
||||
contract_codes: contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))?,
|
||||
header_proofs: header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ impl Peer {
|
||||
flow_params.recharge(&mut self.local_buffer);
|
||||
|
||||
let max_cost = flow_params.compute_cost(kind, max);
|
||||
try!(self.local_buffer.deduct_cost(max_cost));
|
||||
self.local_buffer.deduct_cost(max_cost)?;
|
||||
Ok(max_cost)
|
||||
}
|
||||
|
||||
@ -298,7 +298,7 @@ impl LightProtocol {
|
||||
/// with an event.
|
||||
pub fn request_from(&self, io: &IoContext, peer_id: &PeerId, request: Request) -> Result<ReqId, Error> {
|
||||
let peers = self.peers.read();
|
||||
let peer = try!(peers.get(peer_id).ok_or_else(|| Error::UnknownPeer));
|
||||
let peer = peers.get(peer_id).ok_or_else(|| Error::UnknownPeer)?;
|
||||
let mut peer = peer.lock();
|
||||
|
||||
if !peer.idle { return Err(Error::Overburdened) }
|
||||
@ -307,7 +307,7 @@ impl LightProtocol {
|
||||
Some((ref mut buf, ref flow)) => {
|
||||
flow.recharge(buf);
|
||||
let max = flow.compute_cost(request.kind(), request.amount());
|
||||
try!(buf.deduct_cost(max));
|
||||
buf.deduct_cost(max)?;
|
||||
}
|
||||
None => return Err(Error::NotServer),
|
||||
}
|
||||
@ -414,8 +414,8 @@ impl LightProtocol {
|
||||
// - check whether request was made
|
||||
// - check whether request kinds match
|
||||
fn pre_verify_response(&self, peer: &PeerId, kind: request::Kind, raw: &UntrustedRlp) -> Result<ReqId, Error> {
|
||||
let req_id: usize = try!(raw.val_at(0));
|
||||
let cur_buffer: U256 = try!(raw.val_at(1));
|
||||
let req_id: usize = raw.val_at(0)?;
|
||||
let cur_buffer: U256 = raw.val_at(1)?;
|
||||
|
||||
trace!(target: "les", "pre-verifying response from peer {}, kind={:?}", peer, kind);
|
||||
|
||||
@ -621,7 +621,7 @@ impl LightProtocol {
|
||||
}
|
||||
};
|
||||
|
||||
let (status, capabilities, flow_params) = try!(status::parse_handshake(data));
|
||||
let (status, capabilities, flow_params) = status::parse_handshake(data)?;
|
||||
|
||||
trace!(target: "les", "Connected peer with chain head {:?}", (status.head_hash, status.head_num));
|
||||
|
||||
@ -663,7 +663,7 @@ impl LightProtocol {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let announcement = try!(status::parse_announcement(data));
|
||||
let announcement = status::parse_announcement(data)?;
|
||||
|
||||
// scope to ensure locks are dropped before moving into handler-space.
|
||||
{
|
||||
@ -716,25 +716,25 @@ impl LightProtocol {
|
||||
|
||||
let mut peer = peer.lock();
|
||||
|
||||
let req_id: u64 = try!(data.val_at(0));
|
||||
let data = try!(data.at(1));
|
||||
let req_id: u64 = data.val_at(0)?;
|
||||
let data = data.at(1)?;
|
||||
|
||||
let start_block = {
|
||||
if try!(data.at(0)).size() == 32 {
|
||||
HashOrNumber::Hash(try!(data.val_at(0)))
|
||||
if data.at(0)?.size() == 32 {
|
||||
HashOrNumber::Hash(data.val_at(0)?)
|
||||
} else {
|
||||
HashOrNumber::Number(try!(data.val_at(0)))
|
||||
HashOrNumber::Number(data.val_at(0)?)
|
||||
}
|
||||
};
|
||||
|
||||
let req = request::Headers {
|
||||
start: start_block,
|
||||
max: ::std::cmp::min(MAX_HEADERS, try!(data.val_at(1))),
|
||||
skip: try!(data.val_at(2)),
|
||||
reverse: try!(data.val_at(3)),
|
||||
max: ::std::cmp::min(MAX_HEADERS, data.val_at(1)?),
|
||||
skip: data.val_at(2)?,
|
||||
reverse: data.val_at(3)?,
|
||||
};
|
||||
|
||||
let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Headers, req.max));
|
||||
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Headers, req.max)?;
|
||||
|
||||
let response = self.provider.block_headers(req);
|
||||
let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len());
|
||||
@ -746,7 +746,7 @@ impl LightProtocol {
|
||||
stream.append(&req_id).append(&cur_buffer).begin_list(response.len());
|
||||
|
||||
for header in response {
|
||||
stream.append_raw(&header, 1);
|
||||
stream.append_raw(&header.into_inner(), 1);
|
||||
}
|
||||
|
||||
stream.out()
|
||||
@ -757,8 +757,8 @@ impl LightProtocol {
|
||||
|
||||
// Receive a response for block headers.
|
||||
fn block_headers(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||
let req_id = try!(self.pre_verify_response(peer, request::Kind::Headers, &raw));
|
||||
let raw_headers: Vec<_> = try!(raw.at(2)).iter().map(|x| x.as_raw().to_owned()).collect();
|
||||
let req_id = self.pre_verify_response(peer, request::Kind::Headers, &raw)?;
|
||||
let raw_headers: Vec<_> = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect();
|
||||
|
||||
for handler in &self.handlers {
|
||||
handler.on_block_headers(&Ctx {
|
||||
@ -785,16 +785,19 @@ impl LightProtocol {
|
||||
};
|
||||
let mut peer = peer.lock();
|
||||
|
||||
let req_id: u64 = try!(data.val_at(0));
|
||||
let req_id: u64 = data.val_at(0)?;
|
||||
|
||||
let req = request::Bodies {
|
||||
block_hashes: try!(try!(data.at(1)).iter().take(MAX_BODIES).map(|x| x.as_val()).collect())
|
||||
block_hashes: data.at(1)?.iter()
|
||||
.take(MAX_BODIES)
|
||||
.map(|x| x.as_val())
|
||||
.collect::<Result<_, _>>()?
|
||||
};
|
||||
|
||||
let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len()));
|
||||
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len())?;
|
||||
|
||||
let response = self.provider.block_bodies(req);
|
||||
let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count();
|
||||
let response_len = response.iter().filter(|x| x.is_some()).count();
|
||||
let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len);
|
||||
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
|
||||
|
||||
@ -805,7 +808,10 @@ impl LightProtocol {
|
||||
stream.append(&req_id).append(&cur_buffer).begin_list(response.len());
|
||||
|
||||
for body in response {
|
||||
stream.append_raw(&body, 1);
|
||||
match body {
|
||||
Some(body) => stream.append_raw(&body.into_inner(), 1),
|
||||
None => stream.append_empty_data(),
|
||||
};
|
||||
}
|
||||
|
||||
stream.out()
|
||||
@ -816,8 +822,8 @@ impl LightProtocol {
|
||||
|
||||
// Receive a response for block bodies.
|
||||
fn block_bodies(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||
let req_id = try!(self.pre_verify_response(peer, request::Kind::Bodies, &raw));
|
||||
let raw_bodies: Vec<Bytes> = try!(raw.at(2)).iter().map(|x| x.as_raw().to_owned()).collect();
|
||||
let req_id = self.pre_verify_response(peer, request::Kind::Bodies, &raw)?;
|
||||
let raw_bodies: Vec<Bytes> = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect();
|
||||
|
||||
for handler in &self.handlers {
|
||||
handler.on_block_bodies(&Ctx {
|
||||
@ -844,13 +850,16 @@ impl LightProtocol {
|
||||
};
|
||||
let mut peer = peer.lock();
|
||||
|
||||
let req_id: u64 = try!(data.val_at(0));
|
||||
let req_id: u64 = data.val_at(0)?;
|
||||
|
||||
let req = request::Receipts {
|
||||
block_hashes: try!(try!(data.at(1)).iter().take(MAX_RECEIPTS).map(|x| x.as_val()).collect())
|
||||
block_hashes: data.at(1)?.iter()
|
||||
.take(MAX_RECEIPTS)
|
||||
.map(|x| x.as_val())
|
||||
.collect::<Result<_,_>>()?
|
||||
};
|
||||
|
||||
let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Receipts, req.block_hashes.len()));
|
||||
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Receipts, req.block_hashes.len())?;
|
||||
|
||||
let response = self.provider.receipts(req);
|
||||
let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count();
|
||||
@ -875,11 +884,11 @@ impl LightProtocol {
|
||||
|
||||
// Receive a response for receipts.
|
||||
fn receipts(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||
let req_id = try!(self.pre_verify_response(peer, request::Kind::Receipts, &raw));
|
||||
let raw_receipts: Vec<Vec<Receipt>> = try!(try!(raw.at(2))
|
||||
let req_id = self.pre_verify_response(peer, request::Kind::Receipts, &raw)?;
|
||||
let raw_receipts: Vec<Vec<Receipt>> = raw.at(2)?
|
||||
.iter()
|
||||
.map(|x| x.as_val())
|
||||
.collect());
|
||||
.collect::<Result<_,_>>()?;
|
||||
|
||||
for handler in &self.handlers {
|
||||
handler.on_receipts(&Ctx {
|
||||
@ -906,24 +915,24 @@ impl LightProtocol {
|
||||
};
|
||||
let mut peer = peer.lock();
|
||||
|
||||
let req_id: u64 = try!(data.val_at(0));
|
||||
let req_id: u64 = data.val_at(0)?;
|
||||
|
||||
let req = {
|
||||
let requests: Result<Vec<_>, Error> = try!(data.at(1)).iter().take(MAX_PROOFS).map(|x| {
|
||||
let requests: Result<Vec<_>, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| {
|
||||
Ok(request::StateProof {
|
||||
block: try!(x.val_at(0)),
|
||||
key1: try!(x.val_at(1)),
|
||||
key2: if try!(x.at(2)).is_empty() { None } else { Some(try!(x.val_at(2))) },
|
||||
from_level: try!(x.val_at(3)),
|
||||
block: x.val_at(0)?,
|
||||
key1: x.val_at(1)?,
|
||||
key2: if x.at(2)?.is_empty() { None } else { Some(x.val_at(2)?) },
|
||||
from_level: x.val_at(3)?,
|
||||
})
|
||||
}).collect();
|
||||
|
||||
request::StateProofs {
|
||||
requests: try!(requests),
|
||||
requests: requests?,
|
||||
}
|
||||
};
|
||||
|
||||
let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::StateProofs, req.requests.len()));
|
||||
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::StateProofs, req.requests.len())?;
|
||||
|
||||
let response = self.provider.proofs(req);
|
||||
let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count();
|
||||
@ -948,9 +957,9 @@ impl LightProtocol {
|
||||
|
||||
// Receive a response for proofs.
|
||||
fn proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||
let req_id = try!(self.pre_verify_response(peer, request::Kind::StateProofs, &raw));
|
||||
let req_id = self.pre_verify_response(peer, request::Kind::StateProofs, &raw)?;
|
||||
|
||||
let raw_proofs: Vec<Vec<Bytes>> = try!(raw.at(2)).iter()
|
||||
let raw_proofs: Vec<Vec<Bytes>> = raw.at(2)?.iter()
|
||||
.map(|x| x.iter().map(|node| node.as_raw().to_owned()).collect())
|
||||
.collect();
|
||||
|
||||
@ -979,22 +988,22 @@ impl LightProtocol {
|
||||
};
|
||||
let mut peer = peer.lock();
|
||||
|
||||
let req_id: u64 = try!(data.val_at(0));
|
||||
let req_id: u64 = data.val_at(0)?;
|
||||
|
||||
let req = {
|
||||
let requests: Result<Vec<_>, Error> = try!(data.at(1)).iter().take(MAX_CODES).map(|x| {
|
||||
let requests: Result<Vec<_>, Error> = data.at(1)?.iter().take(MAX_CODES).map(|x| {
|
||||
Ok(request::ContractCode {
|
||||
block_hash: try!(x.val_at(0)),
|
||||
account_key: try!(x.val_at(1)),
|
||||
block_hash: x.val_at(0)?,
|
||||
account_key: x.val_at(1)?,
|
||||
})
|
||||
}).collect();
|
||||
|
||||
request::ContractCodes {
|
||||
code_requests: try!(requests),
|
||||
code_requests: requests?,
|
||||
}
|
||||
};
|
||||
|
||||
let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Codes, req.code_requests.len()));
|
||||
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Codes, req.code_requests.len())?;
|
||||
|
||||
let response = self.provider.contract_codes(req);
|
||||
let response_len = response.iter().filter(|x| !x.is_empty()).count();
|
||||
@ -1019,9 +1028,11 @@ impl LightProtocol {
|
||||
|
||||
// Receive a response for contract code.
|
||||
fn contract_code(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||
let req_id = try!(self.pre_verify_response(peer, request::Kind::Codes, &raw));
|
||||
let req_id = self.pre_verify_response(peer, request::Kind::Codes, &raw)?;
|
||||
|
||||
let raw_code: Vec<Bytes> = try!(try!(raw.at(2)).iter().map(|x| x.as_val()).collect());
|
||||
let raw_code: Vec<Bytes> = raw.at(2)?.iter()
|
||||
.map(|x| x.as_val())
|
||||
.collect::<Result<_,_>>()?;
|
||||
|
||||
for handler in &self.handlers {
|
||||
handler.on_code(&Ctx {
|
||||
@ -1048,23 +1059,23 @@ impl LightProtocol {
|
||||
};
|
||||
let mut peer = peer.lock();
|
||||
|
||||
let req_id: u64 = try!(data.val_at(0));
|
||||
let req_id: u64 = data.val_at(0)?;
|
||||
|
||||
let req = {
|
||||
let requests: Result<Vec<_>, Error> = try!(data.at(1)).iter().take(MAX_PROOFS).map(|x| {
|
||||
let requests: Result<Vec<_>, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| {
|
||||
Ok(request::HeaderProof {
|
||||
cht_number: try!(x.val_at(0)),
|
||||
block_number: try!(x.val_at(1)),
|
||||
from_level: try!(x.val_at(2)),
|
||||
cht_number: x.val_at(0)?,
|
||||
block_number: x.val_at(1)?,
|
||||
from_level: x.val_at(2)?,
|
||||
})
|
||||
}).collect();
|
||||
|
||||
request::HeaderProofs {
|
||||
requests: try!(requests),
|
||||
requests: requests?,
|
||||
}
|
||||
};
|
||||
|
||||
let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::HeaderProofs, req.requests.len()));
|
||||
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::HeaderProofs, req.requests.len())?;
|
||||
|
||||
let response = self.provider.header_proofs(req);
|
||||
let response_len = response.iter().filter(|x| &x[..] != ::rlp::EMPTY_LIST_RLP).count();
|
||||
@ -1091,13 +1102,15 @@ impl LightProtocol {
|
||||
fn header_proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||
fn decode_res(raw: UntrustedRlp) -> Result<(Bytes, Vec<Bytes>), ::rlp::DecoderError> {
|
||||
Ok((
|
||||
try!(raw.val_at(0)),
|
||||
try!(raw.at(1)).iter().map(|x| x.as_raw().to_owned()).collect(),
|
||||
raw.val_at(0)?,
|
||||
raw.at(1)?.iter().map(|x| x.as_raw().to_owned()).collect(),
|
||||
))
|
||||
}
|
||||
|
||||
let req_id = try!(self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw));
|
||||
let raw_proofs: Vec<_> = try!(try!(raw.at(2)).iter().map(decode_res).collect());
|
||||
let req_id = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?;
|
||||
let raw_proofs: Vec<_> = raw.at(2)?.iter()
|
||||
.map(decode_res)
|
||||
.collect::<Result<_,_>>()?;
|
||||
|
||||
for handler in &self.handlers {
|
||||
handler.on_header_proofs(&Ctx {
|
||||
@ -1114,7 +1127,10 @@ impl LightProtocol {
|
||||
fn relay_transactions(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
|
||||
const MAX_TRANSACTIONS: usize = 256;
|
||||
|
||||
let txs: Vec<_> = try!(data.iter().take(MAX_TRANSACTIONS).map(|x| x.as_val::<SignedTransaction>()).collect());
|
||||
let txs: Vec<_> = data.iter()
|
||||
.take(MAX_TRANSACTIONS)
|
||||
.map(|x| x.as_val::<SignedTransaction>())
|
||||
.collect::<Result<_,_>>()?;
|
||||
|
||||
debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer);
|
||||
|
||||
|
@ -100,7 +100,7 @@ impl<'a> Parser<'a> {
|
||||
fn expect_raw(&mut self, key: Key) -> Result<UntrustedRlp<'a>, DecoderError> {
|
||||
trace!(target: "les", "Expecting key {}", key.as_str());
|
||||
let pre_pos = self.pos;
|
||||
if let Some((k, val)) = try!(self.get_next()) {
|
||||
if let Some((k, val)) = self.get_next()? {
|
||||
if k == key { return Ok(val) }
|
||||
}
|
||||
|
||||
@ -111,12 +111,12 @@ impl<'a> Parser<'a> {
|
||||
// get the next key and value RLP.
|
||||
fn get_next(&mut self) -> Result<Option<(Key, UntrustedRlp<'a>)>, DecoderError> {
|
||||
while self.pos < self.rlp.item_count() {
|
||||
let pair = try!(self.rlp.at(self.pos));
|
||||
let k: String = try!(pair.val_at(0));
|
||||
let pair = self.rlp.at(self.pos)?;
|
||||
let k: String = pair.val_at(0)?;
|
||||
|
||||
self.pos += 1;
|
||||
match Key::from_str(&k) {
|
||||
Some(key) => return Ok(Some((key , try!(pair.at(1))))),
|
||||
Some(key) => return Ok(Some((key , pair.at(1)?))),
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
@ -205,12 +205,12 @@ pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, Optio
|
||||
};
|
||||
|
||||
let status = Status {
|
||||
protocol_version: try!(parser.expect(Key::ProtocolVersion)),
|
||||
network_id: try!(parser.expect(Key::NetworkId)),
|
||||
head_td: try!(parser.expect(Key::HeadTD)),
|
||||
head_hash: try!(parser.expect(Key::HeadHash)),
|
||||
head_num: try!(parser.expect(Key::HeadNum)),
|
||||
genesis_hash: try!(parser.expect(Key::GenesisHash)),
|
||||
protocol_version: parser.expect(Key::ProtocolVersion)?,
|
||||
network_id: parser.expect(Key::NetworkId)?,
|
||||
head_td: parser.expect(Key::HeadTD)?,
|
||||
head_hash: parser.expect(Key::HeadHash)?,
|
||||
head_num: parser.expect(Key::HeadNum)?,
|
||||
genesis_hash: parser.expect(Key::GenesisHash)?,
|
||||
last_head: None,
|
||||
};
|
||||
|
||||
@ -298,10 +298,10 @@ pub fn parse_announcement(rlp: UntrustedRlp) -> Result<Announcement, DecoderErro
|
||||
let mut last_key = None;
|
||||
|
||||
let mut announcement = Announcement {
|
||||
head_hash: try!(rlp.val_at(0)),
|
||||
head_num: try!(rlp.val_at(1)),
|
||||
head_td: try!(rlp.val_at(2)),
|
||||
reorg_depth: try!(rlp.val_at(3)),
|
||||
head_hash: rlp.val_at(0)?,
|
||||
head_num: rlp.val_at(1)?,
|
||||
head_td: rlp.val_at(2)?,
|
||||
reorg_depth: rlp.val_at(3)?,
|
||||
serve_headers: false,
|
||||
serve_state_since: None,
|
||||
serve_chain_since: None,
|
||||
@ -313,14 +313,14 @@ pub fn parse_announcement(rlp: UntrustedRlp) -> Result<Announcement, DecoderErro
|
||||
rlp: rlp,
|
||||
};
|
||||
|
||||
while let Some((key, item)) = try!(parser.get_next()) {
|
||||
while let Some((key, item)) = parser.get_next()? {
|
||||
if Some(key) <= last_key { return Err(DecoderError::Custom("Invalid announcement key ordering")) }
|
||||
last_key = Some(key);
|
||||
|
||||
match key {
|
||||
Key::ServeHeaders => announcement.serve_headers = true,
|
||||
Key::ServeStateSince => announcement.serve_state_since = Some(try!(item.as_val())),
|
||||
Key::ServeChainSince => announcement.serve_chain_since = Some(try!(item.as_val())),
|
||||
Key::ServeStateSince => announcement.serve_state_since = Some(item.as_val()?),
|
||||
Key::ServeChainSince => announcement.serve_chain_since = Some(item.as_val()?),
|
||||
Key::TxRelay => announcement.tx_relay = true,
|
||||
_ => return Err(DecoderError::Custom("Nonsensical key in announcement")),
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use ethcore::blockchain_info::BlockChainInfo;
|
||||
use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient};
|
||||
use ethcore::ids::BlockId;
|
||||
use ethcore::transaction::PendingTransaction;
|
||||
use ethcore::encoded;
|
||||
use network::{PeerId, NodeId};
|
||||
|
||||
use net::buffer_flow::FlowParams;
|
||||
@ -94,11 +95,11 @@ impl Provider for TestProvider {
|
||||
None
|
||||
}
|
||||
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
||||
self.0.client.block_header(id)
|
||||
}
|
||||
|
||||
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
|
||||
self.0.client.block_body(id)
|
||||
}
|
||||
|
||||
@ -122,7 +123,7 @@ impl Provider for TestProvider {
|
||||
req.account_key.iter().chain(req.account_key.iter()).cloned().collect()
|
||||
}
|
||||
|
||||
fn header_proof(&self, _req: request::HeaderProof) -> Option<(Bytes, Vec<Bytes>)> {
|
||||
fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
|
||||
None
|
||||
}
|
||||
|
||||
@ -273,7 +274,7 @@ fn get_block_headers() {
|
||||
|
||||
response_stream.append(&req_id).append(&new_buf).begin_list(10);
|
||||
for header in headers {
|
||||
response_stream.append_raw(&header, 1);
|
||||
response_stream.append_raw(&header.into_inner(), 1);
|
||||
}
|
||||
|
||||
response_stream.out()
|
||||
@ -320,7 +321,7 @@ fn get_block_bodies() {
|
||||
|
||||
response_stream.append(&req_id).append(&new_buf).begin_list(10);
|
||||
for body in bodies {
|
||||
response_stream.append_raw(&body, 1);
|
||||
response_stream.append_raw(&body.into_inner(), 1);
|
||||
}
|
||||
|
||||
response_stream.out()
|
||||
|
@ -22,6 +22,7 @@ use std::collections::HashMap;
|
||||
|
||||
use ethcore::ids::BlockId;
|
||||
use ethcore::block::Block;
|
||||
use ethcore::encoded;
|
||||
use ethcore::header::Header;
|
||||
use ethcore::receipt::Receipt;
|
||||
|
||||
@ -33,7 +34,8 @@ use client::Client;
|
||||
use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
|
||||
use util::{Address, H256, RwLock};
|
||||
|
||||
struct Account;
|
||||
/// Dummy type for "basic account"
|
||||
pub struct Account;
|
||||
|
||||
// relevant peer info.
|
||||
struct Peer {
|
||||
@ -43,12 +45,12 @@ struct Peer {
|
||||
|
||||
// request info and where to send the result.
|
||||
enum Request {
|
||||
HeaderByNumber(u64, H256, Sender<Header>), // num + CHT root
|
||||
HeaderByHash(H256, Sender<Header>),
|
||||
Block(Header, Sender<Block>),
|
||||
BlockReceipts(Header, Sender<Vec<Receipt>>),
|
||||
Account(Header, Address, Sender<Account>),
|
||||
Storage(Header, Address, H256, Sender<H256>),
|
||||
HeaderByNumber(u64, H256, Sender<encoded::Header>), // num + CHT root
|
||||
HeaderByHash(H256, Sender<encoded::Header>),
|
||||
Block(encoded::Header, Sender<encoded::Block>),
|
||||
BlockReceipts(encoded::Header, Sender<Vec<Receipt>>),
|
||||
Account(encoded::Header, Address, Sender<Account>),
|
||||
Storage(encoded::Header, Address, H256, Sender<H256>),
|
||||
}
|
||||
|
||||
/// On demand request service. See module docs for more details.
|
||||
@ -61,7 +63,7 @@ pub struct OnDemand {
|
||||
|
||||
impl Handler for OnDemand {
|
||||
fn on_connect(&self, ctx: &EventContext, status: &Status, capabilities: &Capabilities) {
|
||||
self.peers.write().insert(ctx.peer(), Peer { status: status.clone(), capabilities: capabilities.clone() })
|
||||
self.peers.write().insert(ctx.peer(), Peer { status: status.clone(), capabilities: capabilities.clone() });
|
||||
}
|
||||
|
||||
fn on_disconnect(&self, ctx: &EventContext, unfulfilled: &[ReqId]) {
|
||||
@ -71,7 +73,7 @@ impl Handler for OnDemand {
|
||||
|
||||
impl OnDemand {
|
||||
/// Request a header by block number and CHT root hash.
|
||||
pub fn header_by_number(&self, ctx: &BasicContext, num: u64, cht_root: H256) -> Receiver<Header> {
|
||||
pub fn header_by_number(&self, ctx: &BasicContext, num: u64, cht_root: H256) -> Receiver<encoded::Header> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.dispatch_request(ctx, Request::HeaderByNumber(num, cht_root, sender));
|
||||
receiver
|
||||
@ -80,7 +82,7 @@ impl OnDemand {
|
||||
/// Request a header by hash. This is less accurate than by-number because we don't know
|
||||
/// where in the chain this header lies, and therefore can't find a peer who is supposed to have
|
||||
/// it as easily.
|
||||
pub fn header_by_hash(&self, ctx: &BasicContext, hash: H256) -> Receiver<Header> {
|
||||
pub fn header_by_hash(&self, ctx: &BasicContext, hash: H256) -> Receiver<encoded::Header> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.dispatch_request(ctx, Request::HeaderByHash(hash, sender));
|
||||
receiver
|
||||
@ -89,7 +91,7 @@ impl OnDemand {
|
||||
/// Request a block, given its header. Block bodies are requestable by hash only,
|
||||
/// and the header is required anyway to verify and complete the block body
|
||||
/// -- this just doesn't obscure the network query.
|
||||
pub fn block(&self, ctx: &BasicContext, header: Header) -> Receiver<Block> {
|
||||
pub fn block(&self, ctx: &BasicContext, header: encoded::Header) -> Receiver<encoded::Block> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.dispatch_request(ctx, Request::Block(header, sender));
|
||||
receiver
|
||||
@ -97,7 +99,7 @@ impl OnDemand {
|
||||
|
||||
/// Request the receipts for a block. The header serves two purposes:
|
||||
/// provide the block hash to fetch receipts for, and for verification of the receipts root.
|
||||
pub fn block_receipts(&self, ctx: &BasicContext, header: Header) -> Receiver<Vec<Receipt>> {
|
||||
pub fn block_receipts(&self, ctx: &BasicContext, header: encoded::Header) -> Receiver<Vec<Receipt>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.dispatch_request(ctx, Request::BlockReceipts(header, sender));
|
||||
receiver
|
||||
@ -105,14 +107,14 @@ impl OnDemand {
|
||||
|
||||
/// Request an account by address and block header -- which gives a hash to query and a state root
|
||||
/// to verify against.
|
||||
pub fn account(&self, ctx: &BasicContext, header: Header, address: Address) -> Receiver<Account> {
|
||||
pub fn account(&self, ctx: &BasicContext, header: encoded::Header, address: Address) -> Receiver<Account> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.dispatch_request(ctx, Request::Account(header, address, sender));
|
||||
receiver
|
||||
}
|
||||
|
||||
/// Request account storage value by block header, address, and key.
|
||||
pub fn storage(&self, ctx: &BasicContext, header: Header, address: Address, key: H256) -> Receiver<H256> {
|
||||
pub fn storage(&self, ctx: &BasicContext, header: encoded::Header, address: Address, key: H256) -> Receiver<H256> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.dispatch_request(ctx, Request::Storage(header, address, key, sender));
|
||||
receiver
|
||||
|
@ -21,6 +21,7 @@ use ethcore::blockchain_info::BlockChainInfo;
|
||||
use ethcore::client::{BlockChainClient, ProvingBlockChainClient};
|
||||
use ethcore::transaction::PendingTransaction;
|
||||
use ethcore::ids::BlockId;
|
||||
use ethcore::encoded;
|
||||
|
||||
use util::{Bytes, H256};
|
||||
|
||||
@ -52,9 +53,8 @@ pub trait Provider: Send + Sync {
|
||||
///
|
||||
/// The returned vector may have any length in the range [0, `max`], but the
|
||||
/// results within must adhere to the `skip` and `reverse` parameters.
|
||||
fn block_headers(&self, req: request::Headers) -> Vec<Bytes> {
|
||||
fn block_headers(&self, req: request::Headers) -> Vec<encoded::Header> {
|
||||
use request::HashOrNumber;
|
||||
use ethcore::views::HeaderView;
|
||||
|
||||
if req.max == 0 { return Vec::new() }
|
||||
|
||||
@ -67,9 +67,9 @@ pub trait Provider: Send + Sync {
|
||||
return Vec::new();
|
||||
}
|
||||
Some(header) => {
|
||||
let num = HeaderView::new(&header).number();
|
||||
let num = header.number();
|
||||
let canon_hash = self.block_header(BlockId::Number(num))
|
||||
.map(|h| HeaderView::new(&h).hash());
|
||||
.map(|h| h.hash());
|
||||
|
||||
if req.max == 1 || canon_hash != Some(hash) {
|
||||
// Non-canonical header or single header requested.
|
||||
@ -92,19 +92,18 @@ pub trait Provider: Send + Sync {
|
||||
}
|
||||
|
||||
/// Get a block header by id.
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes>;
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
|
||||
|
||||
/// Provide as many as possible of the requested blocks (minus the headers) encoded
|
||||
/// in RLP format.
|
||||
fn block_bodies(&self, req: request::Bodies) -> Vec<Bytes> {
|
||||
fn block_bodies(&self, req: request::Bodies) -> Vec<Option<encoded::Body>> {
|
||||
req.block_hashes.into_iter()
|
||||
.map(|hash| self.block_body(BlockId::Hash(hash)))
|
||||
.map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get a block body by id.
|
||||
fn block_body(&self, id: BlockId) -> Option<Bytes>;
|
||||
fn block_body(&self, id: BlockId) -> Option<encoded::Body>;
|
||||
|
||||
/// Provide the receipts as many as possible of the requested blocks.
|
||||
/// Returns a vector of RLP-encoded lists of receipts.
|
||||
@ -169,7 +168,7 @@ pub trait Provider: Send + Sync {
|
||||
None => rlp::EMPTY_LIST_RLP.to_vec(),
|
||||
Some((header, proof)) => {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append_raw(&header, 1).begin_list(proof.len());
|
||||
stream.append_raw(&header.into_inner(), 1).begin_list(proof.len());
|
||||
|
||||
for node in proof {
|
||||
stream.append_raw(&node, 1);
|
||||
@ -184,7 +183,7 @@ pub trait Provider: Send + Sync {
|
||||
/// Provide a header proof from a given Canonical Hash Trie as well as the
|
||||
/// corresponding header. The first element is the block header and the
|
||||
/// second is a merkle proof of the CHT.
|
||||
fn header_proof(&self, req: request::HeaderProof) -> Option<(Bytes, Vec<Bytes>)>;
|
||||
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)>;
|
||||
|
||||
/// Provide pending transactions.
|
||||
fn ready_transactions(&self) -> Vec<PendingTransaction>;
|
||||
@ -204,11 +203,11 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
||||
Some(self.pruning_info().earliest_state)
|
||||
}
|
||||
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
||||
BlockChainClient::block_header(self, id)
|
||||
}
|
||||
|
||||
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
|
||||
BlockChainClient::block_body(self, id)
|
||||
}
|
||||
|
||||
@ -227,7 +226,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
||||
self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash))
|
||||
}
|
||||
|
||||
fn header_proof(&self, _req: request::HeaderProof) -> Option<(Bytes, Vec<Bytes>)> {
|
||||
fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
|
||||
None
|
||||
}
|
||||
|
||||
|
30
ethcore/res/constructor.json
Normal file
30
ethcore/res/constructor.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "GenesisConstructor",
|
||||
"engine": {
|
||||
"null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"generic": "0x"
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"0000000000000000000000000000000000000005": { "balance": "1", "constructor": "60606040526000805460ff19166001179055346000575b6075806100246000396000f300606060405263ffffffff60e060020a60003504166394b91deb81146022575b6000565b34600057602c6040565b604080519115158252519081900360200190f35b60005460ff16815600a165627a7a723058207882eb60ebce23178b3fa06d4cd8e5adc17711937ccddacb18a04abca2a2c9ee0029" }
|
||||
}
|
||||
}
|
@ -127,32 +127,32 @@ impl AccountProvider {
|
||||
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||
let public = acc.public().clone();
|
||||
let secret = acc.secret().clone();
|
||||
let address = try!(self.sstore.insert_account(secret, password));
|
||||
let address = self.sstore.insert_account(secret, password)?;
|
||||
Ok((address, public))
|
||||
}
|
||||
|
||||
/// Inserts new account into underlying store.
|
||||
/// Does not unlock account!
|
||||
pub fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> {
|
||||
let address = try!(self.sstore.insert_account(secret, password));
|
||||
let address = self.sstore.insert_account(secret, password)?;
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_presale(&self, presale_json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let address = try!(self.sstore.import_presale(presale_json, password));
|
||||
let address = self.sstore.import_presale(presale_json, password)?;
|
||||
Ok(Address::from(address).into())
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let address = try!(self.sstore.import_wallet(json, password));
|
||||
let address = self.sstore.import_wallet(json, password)?;
|
||||
Ok(Address::from(address).into())
|
||||
}
|
||||
|
||||
/// Returns addresses of all accounts.
|
||||
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
let accounts = try!(self.sstore.accounts());
|
||||
let accounts = self.sstore.accounts()?;
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ impl AccountProvider {
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
||||
let r: HashMap<Address, AccountMeta> = try!(self.sstore.accounts())
|
||||
let r: HashMap<Address, AccountMeta> = self.sstore.accounts()?
|
||||
.into_iter()
|
||||
.map(|a| (a.clone(), self.account_meta(a).ok().unwrap_or_default()))
|
||||
.collect();
|
||||
@ -239,21 +239,21 @@ impl AccountProvider {
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn account_meta(&self, account: Address) -> Result<AccountMeta, Error> {
|
||||
Ok(AccountMeta {
|
||||
name: try!(self.sstore.name(&account)),
|
||||
meta: try!(self.sstore.meta(&account)),
|
||||
name: self.sstore.name(&account)?,
|
||||
meta: self.sstore.meta(&account)?,
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_name(&self, account: Address, name: String) -> Result<(), Error> {
|
||||
try!(self.sstore.set_name(&account, name));
|
||||
self.sstore.set_name(&account, name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_meta(&self, account: Address, meta: String) -> Result<(), Error> {
|
||||
try!(self.sstore.set_meta(&account, meta));
|
||||
self.sstore.set_meta(&account, meta)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ impl AccountProvider {
|
||||
|
||||
/// Permanently removes an account.
|
||||
pub fn kill_account(&self, account: &Address, password: &str) -> Result<(), Error> {
|
||||
try!(self.sstore.remove_account(account, &password));
|
||||
self.sstore.remove_account(account, &password)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ impl AccountProvider {
|
||||
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
||||
// verify password by signing dump message
|
||||
// result may be discarded
|
||||
let _ = try!(self.sstore.sign(&account, &password, &Default::default()));
|
||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||
|
||||
// check if account is already unlocked pernamently, if it is, do nothing
|
||||
let mut unlocked = self.unlocked.write();
|
||||
@ -299,7 +299,7 @@ impl AccountProvider {
|
||||
|
||||
fn password(&self, account: &Address) -> Result<String, Error> {
|
||||
let mut unlocked = self.unlocked.write();
|
||||
let data = try!(unlocked.get(account).ok_or(Error::NotUnlocked)).clone();
|
||||
let data = unlocked.get(account).ok_or(Error::NotUnlocked)?.clone();
|
||||
if let Unlock::Temp = data.unlock {
|
||||
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
@ -335,25 +335,25 @@ impl AccountProvider {
|
||||
|
||||
/// Signs the message. If password is not provided the account must be unlocked.
|
||||
pub fn sign(&self, account: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||
Ok(try!(self.sstore.sign(&account, &password, &message)))
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.sign(&account, &password, &message)?)
|
||||
}
|
||||
|
||||
/// Signs given message with supplied token. Returns a token to use in next signing within this session.
|
||||
pub fn sign_with_token(&self, account: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), Error> {
|
||||
let is_std_password = try!(self.sstore.test_password(&account, &token));
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = random_string(16);
|
||||
let signature = if is_std_password {
|
||||
// Insert to transient store
|
||||
try!(self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token));
|
||||
self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token)?;
|
||||
// sign
|
||||
try!(self.sstore.sign(&account, &token, &message))
|
||||
self.sstore.sign(&account, &token, &message)?
|
||||
} else {
|
||||
// check transient store
|
||||
try!(self.transient_sstore.change_password(&account, &token, &new_token));
|
||||
self.transient_sstore.change_password(&account, &token, &new_token)?;
|
||||
// and sign
|
||||
try!(self.transient_sstore.sign(&account, &new_token, &message))
|
||||
self.transient_sstore.sign(&account, &new_token, &message)?
|
||||
};
|
||||
|
||||
Ok((signature, new_token))
|
||||
@ -363,19 +363,19 @@ impl AccountProvider {
|
||||
pub fn decrypt_with_token(&self, account: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
|
||||
-> Result<(Vec<u8>, AccountToken), Error>
|
||||
{
|
||||
let is_std_password = try!(self.sstore.test_password(&account, &token));
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = random_string(16);
|
||||
let message = if is_std_password {
|
||||
// Insert to transient store
|
||||
try!(self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token));
|
||||
self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token)?;
|
||||
// decrypt
|
||||
try!(self.sstore.decrypt(&account, &token, shared_mac, message))
|
||||
self.sstore.decrypt(&account, &token, shared_mac, message)?
|
||||
} else {
|
||||
// check transient store
|
||||
try!(self.transient_sstore.change_password(&account, &token, &new_token));
|
||||
self.transient_sstore.change_password(&account, &token, &new_token)?;
|
||||
// and decrypt
|
||||
try!(self.transient_sstore.decrypt(&account, &token, shared_mac, message))
|
||||
self.transient_sstore.decrypt(&account, &token, shared_mac, message)?
|
||||
};
|
||||
|
||||
Ok((message, new_token))
|
||||
@ -383,8 +383,8 @@ impl AccountProvider {
|
||||
|
||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||
pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||
Ok(try!(self.sstore.decrypt(&account, &password, shared_mac, message)))
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
|
||||
}
|
||||
|
||||
/// Returns the underlying `SecretStore` reference if one exists.
|
||||
|
@ -67,7 +67,7 @@ impl Block {
|
||||
|
||||
impl Decodable for Block {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
if decoder.as_raw().len() != try!(decoder.as_rlp().payload_info()).total() {
|
||||
if decoder.as_raw().len() != decoder.as_rlp().payload_info()?.total() {
|
||||
return Err(DecoderError::RlpIsTooBig);
|
||||
}
|
||||
let d = decoder.as_rlp();
|
||||
@ -75,9 +75,9 @@ impl Decodable for Block {
|
||||
return Err(DecoderError::RlpIncorrectListLen);
|
||||
}
|
||||
Ok(Block {
|
||||
header: try!(d.val_at(0)),
|
||||
transactions: try!(d.val_at(1)),
|
||||
uncles: try!(d.val_at(2)),
|
||||
header: d.val_at(0)?,
|
||||
transactions: d.val_at(1)?,
|
||||
uncles: d.val_at(2)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -252,7 +252,7 @@ impl<'x> OpenBlock<'x> {
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
) -> Result<Self, Error> {
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), factories));
|
||||
let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), factories)?;
|
||||
let mut r = OpenBlock {
|
||||
block: ExecutedBlock::new(state, tracing),
|
||||
engine: engine,
|
||||
@ -521,12 +521,12 @@ pub fn enact(
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), factories.clone()));
|
||||
let s = State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), factories.clone())?;
|
||||
trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", header.number(), s.root(), header.author(), s.balance(&header.author()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = try!(OpenBlock::new(engine, factories, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![]));
|
||||
let mut b = OpenBlock::new(engine, factories, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![])?;
|
||||
b.set_difficulty(*header.difficulty());
|
||||
b.set_gas_limit(*header.gas_limit());
|
||||
b.set_timestamp(header.timestamp());
|
||||
@ -536,9 +536,9 @@ pub fn enact(
|
||||
b.set_transactions_root(header.transactions_root().clone());
|
||||
b.set_receipts_root(header.receipts_root().clone());
|
||||
|
||||
try!(push_transactions(&mut b, transactions));
|
||||
push_transactions(&mut b, transactions)?;
|
||||
for u in uncles {
|
||||
try!(b.push_uncle(u.clone()));
|
||||
b.push_uncle(u.clone())?;
|
||||
}
|
||||
Ok(b.close_and_lock())
|
||||
}
|
||||
@ -547,7 +547,7 @@ pub fn enact(
|
||||
#[cfg(not(feature = "slow-blocks"))]
|
||||
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
|
||||
for t in transactions {
|
||||
try!(block.push_transaction(t.clone(), None));
|
||||
block.push_transaction(t.clone(), None)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -560,7 +560,7 @@ fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction])
|
||||
for t in transactions {
|
||||
let hash = t.hash();
|
||||
let start = time::Instant::now();
|
||||
try!(block.push_transaction(t.clone(), None));
|
||||
block.push_transaction(t.clone(), None)?;
|
||||
let took = start.elapsed();
|
||||
if took > time::Duration::from_millis(slow_tx) {
|
||||
warn!("Heavy transaction in block {:?}: {:?}", block.header().number(), hash);
|
||||
@ -595,9 +595,8 @@ mod tests {
|
||||
use factory::Factories;
|
||||
use state_db::StateDB;
|
||||
use views::BlockView;
|
||||
use util::{Address, TrieFactory};
|
||||
use util::Address;
|
||||
use util::hash::FixedHash;
|
||||
use util::trie::TrieSpec;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||
@ -628,7 +627,7 @@ mod tests {
|
||||
factories: Factories,
|
||||
) -> Result<SealedBlock, Error> {
|
||||
let header = BlockView::new(block_bytes).header_view();
|
||||
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal())))
|
||||
Ok(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)?.seal(engine, header.seal())?)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -637,8 +636,7 @@ mod tests {
|
||||
let spec = Spec::new_test();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
@ -653,8 +651,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||
.close_and_lock().seal(engine, vec![]).unwrap();
|
||||
@ -662,8 +659,7 @@ mod tests {
|
||||
let orig_db = b.drain();
|
||||
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap();
|
||||
|
||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||
@ -681,8 +677,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut uncle1_header = Header::new();
|
||||
@ -697,8 +692,7 @@ mod tests {
|
||||
let orig_db = b.drain();
|
||||
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap();
|
||||
|
||||
let bytes = e.rlp_bytes();
|
||||
|
@ -35,6 +35,7 @@ use blockchain::{CacheSize, ImportRoute, Config};
|
||||
use db::{self, Writable, Readable, CacheUpdatePolicy};
|
||||
use cache_manager::CacheManager;
|
||||
use engines::Engine;
|
||||
use encoded;
|
||||
|
||||
const LOG_BLOOMS_LEVELS: usize = 3;
|
||||
const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16;
|
||||
@ -64,7 +65,7 @@ pub trait BlockProvider {
|
||||
self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||
}
|
||||
/// Get raw block data
|
||||
fn block(&self, hash: &H256) -> Option<Bytes>;
|
||||
fn block(&self, hash: &H256) -> Option<encoded::Block>;
|
||||
|
||||
/// Get the familial details concerning a block.
|
||||
fn block_details(&self, hash: &H256) -> Option<BlockDetails>;
|
||||
@ -80,25 +81,25 @@ pub trait BlockProvider {
|
||||
|
||||
/// Get the partial-header of a block.
|
||||
fn block_header(&self, hash: &H256) -> Option<Header> {
|
||||
self.block_header_data(hash).map(|header| decode(&header))
|
||||
self.block_header_data(hash).map(|header| header.decode())
|
||||
}
|
||||
|
||||
/// Get the header RLP of a block.
|
||||
fn block_header_data(&self, hash: &H256) -> Option<Bytes>;
|
||||
fn block_header_data(&self, hash: &H256) -> Option<encoded::Header>;
|
||||
|
||||
/// Get the block body (uncles and transactions).
|
||||
fn block_body(&self, hash: &H256) -> Option<Bytes>;
|
||||
fn block_body(&self, hash: &H256) -> Option<encoded::Body>;
|
||||
|
||||
/// Get a list of uncles for a given block.
|
||||
/// Returns None if block does not exist.
|
||||
fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
|
||||
self.block_body(hash).map(|bytes| BodyView::new(&bytes).uncles())
|
||||
self.block_body(hash).map(|body| body.uncles())
|
||||
}
|
||||
|
||||
/// Get a list of uncle hashes for a given block.
|
||||
/// Returns None if block does not exist.
|
||||
fn uncle_hashes(&self, hash: &H256) -> Option<Vec<H256>> {
|
||||
self.block_body(hash).map(|bytes| BodyView::new(&bytes).uncle_hashes())
|
||||
self.block_body(hash).map(|body| body.uncle_hashes())
|
||||
}
|
||||
|
||||
/// Get the number of given block's hash.
|
||||
@ -109,8 +110,8 @@ pub trait BlockProvider {
|
||||
/// Get transaction with given transaction hash.
|
||||
fn transaction(&self, address: &TransactionAddress) -> Option<LocalizedTransaction> {
|
||||
self.block_body(&address.block_hash)
|
||||
.and_then(|bytes| self.block_number(&address.block_hash)
|
||||
.and_then(|n| BodyView::new(&bytes).localized_transaction_at(&address.block_hash, n, address.index)))
|
||||
.and_then(|body| self.block_number(&address.block_hash)
|
||||
.and_then(|n| body.view().localized_transaction_at(&address.block_hash, n, address.index)))
|
||||
}
|
||||
|
||||
/// Get transaction receipt.
|
||||
@ -122,8 +123,8 @@ pub trait BlockProvider {
|
||||
/// Returns None if block does not exist.
|
||||
fn transactions(&self, hash: &H256) -> Option<Vec<LocalizedTransaction>> {
|
||||
self.block_body(hash)
|
||||
.and_then(|bytes| self.block_number(hash)
|
||||
.map(|n| BodyView::new(&bytes).localized_transactions(hash, n)))
|
||||
.and_then(|body| self.block_number(hash)
|
||||
.map(|n| body.view().localized_transactions(hash, n)))
|
||||
}
|
||||
|
||||
/// Returns reference to genesis hash.
|
||||
@ -224,27 +225,27 @@ impl BlockProvider for BlockChain {
|
||||
}
|
||||
|
||||
/// Get raw block data
|
||||
fn block(&self, hash: &H256) -> Option<Bytes> {
|
||||
fn block(&self, hash: &H256) -> Option<encoded::Block> {
|
||||
match (self.block_header_data(hash), self.block_body(hash)) {
|
||||
(Some(header), Some(body)) => {
|
||||
let mut block = RlpStream::new_list(3);
|
||||
let body_rlp = Rlp::new(&body);
|
||||
block.append_raw(&header, 1);
|
||||
let body_rlp = body.rlp();
|
||||
block.append_raw(header.rlp().as_raw(), 1);
|
||||
block.append_raw(body_rlp.at(0).as_raw(), 1);
|
||||
block.append_raw(body_rlp.at(1).as_raw(), 1);
|
||||
Some(block.out())
|
||||
Some(encoded::Block::new(block.out()))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get block header data
|
||||
fn block_header_data(&self, hash: &H256) -> Option<Bytes> {
|
||||
fn block_header_data(&self, hash: &H256) -> Option<encoded::Header> {
|
||||
// Check cache first
|
||||
{
|
||||
let read = self.block_headers.read();
|
||||
if let Some(v) = read.get(hash) {
|
||||
return Some(v.clone());
|
||||
return Some(encoded::Header::new(v.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,7 +253,9 @@ impl BlockProvider for BlockChain {
|
||||
{
|
||||
let best_block = self.best_block.read();
|
||||
if &best_block.hash == hash {
|
||||
return Some(Rlp::new(&best_block.block).at(0).as_raw().to_vec());
|
||||
return Some(encoded::Header::new(
|
||||
Rlp::new(&best_block.block).at(0).as_raw().to_vec()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,7 +268,7 @@ impl BlockProvider for BlockChain {
|
||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
||||
let mut write = self.block_headers.write();
|
||||
write.insert(hash.clone(), bytes.clone());
|
||||
Some(bytes)
|
||||
Some(encoded::Header::new(bytes))
|
||||
},
|
||||
None => None
|
||||
};
|
||||
@ -275,12 +278,12 @@ impl BlockProvider for BlockChain {
|
||||
}
|
||||
|
||||
/// Get block body data
|
||||
fn block_body(&self, hash: &H256) -> Option<Bytes> {
|
||||
fn block_body(&self, hash: &H256) -> Option<encoded::Body> {
|
||||
// Check cache first
|
||||
{
|
||||
let read = self.block_bodies.read();
|
||||
if let Some(v) = read.get(hash) {
|
||||
return Some(v.clone());
|
||||
return Some(encoded::Body::new(v.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +291,7 @@ impl BlockProvider for BlockChain {
|
||||
{
|
||||
let best_block = self.best_block.read();
|
||||
if &best_block.hash == hash {
|
||||
return Some(Self::block_to_body(&best_block.block));
|
||||
return Some(encoded::Body::new(Self::block_to_body(&best_block.block)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,7 +304,7 @@ impl BlockProvider for BlockChain {
|
||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
||||
let mut write = self.block_bodies.write();
|
||||
write.insert(hash.clone(), bytes.clone());
|
||||
Some(bytes)
|
||||
Some(encoded::Body::new(bytes))
|
||||
},
|
||||
None => None
|
||||
};
|
||||
@ -358,7 +361,7 @@ impl BlockProvider for BlockChain {
|
||||
let mut logs = blocks.into_iter()
|
||||
.filter_map(|number| self.block_hash(number).map(|hash| (number, hash)))
|
||||
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
|
||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes())))
|
||||
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
||||
if receipts.len() != hashes.len() {
|
||||
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
|
||||
@ -484,7 +487,7 @@ impl BlockChain {
|
||||
// Fetch best block details
|
||||
let best_block_number = bc.block_number(&best_block_hash).unwrap();
|
||||
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
|
||||
let best_block_rlp = bc.block(&best_block_hash).unwrap();
|
||||
let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner();
|
||||
|
||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec());
|
||||
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
|
||||
@ -578,7 +581,7 @@ impl BlockChain {
|
||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||
|
||||
let best_block_total_difficulty = self.block_details(&hash).unwrap().total_difficulty;
|
||||
let best_block_rlp = self.block(&hash).unwrap();
|
||||
let best_block_rlp = self.block(&hash).unwrap().into_inner();
|
||||
|
||||
let mut best_block = self.best_block.write();
|
||||
*best_block = BestBlock {
|
||||
@ -862,7 +865,7 @@ impl BlockChain {
|
||||
let number = header.number();
|
||||
let parent_hash = header.parent_hash();
|
||||
let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
let is_new_best = self.engine.is_new_best_block(self.best_block_total_difficulty(), HeaderView::new(&self.best_block_header()), &parent_details, header);
|
||||
let is_new_best = self.engine.is_new_best_block(self.best_block_total_difficulty(), self.best_block_header().view(), &parent_details, header);
|
||||
|
||||
BlockInfo {
|
||||
hash: hash,
|
||||
@ -1108,8 +1111,8 @@ impl BlockChain {
|
||||
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
||||
let addresses = data.enacted.iter()
|
||||
.flat_map(|hash| {
|
||||
let bytes = self.block_body(hash).expect("Enacted block must be in database.");
|
||||
let hashes = BodyView::new(&bytes).transaction_hashes();
|
||||
let body = self.block_body(hash).expect("Enacted block must be in database.");
|
||||
let hashes = body.transaction_hashes();
|
||||
hashes.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, tx_hash)| (tx_hash, Some(TransactionAddress {
|
||||
@ -1129,8 +1132,8 @@ impl BlockChain {
|
||||
});
|
||||
|
||||
let retracted = data.retracted.iter().flat_map(|hash| {
|
||||
let bytes = self.block_body(hash).expect("Retracted block must be in database.");
|
||||
let hashes = BodyView::new(&bytes).transaction_hashes();
|
||||
let body = self.block_body(hash).expect("Retracted block must be in database.");
|
||||
let hashes = body.transaction_hashes();
|
||||
hashes.into_iter().map(|hash| (hash, None)).collect::<HashMap<H256, Option<TransactionAddress>>>()
|
||||
});
|
||||
|
||||
@ -1179,7 +1182,7 @@ impl BlockChain {
|
||||
|
||||
let mut blooms: Vec<bc::Bloom> = data.enacted.iter()
|
||||
.map(|hash| self.block_header_data(hash).unwrap())
|
||||
.map(|bytes| HeaderView::new(&bytes).log_bloom())
|
||||
.map(|h| h.log_bloom())
|
||||
.map(Bloom::from)
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
@ -1212,9 +1215,10 @@ impl BlockChain {
|
||||
}
|
||||
|
||||
/// Get best block header
|
||||
pub fn best_block_header(&self) -> Bytes {
|
||||
pub fn best_block_header(&self) -> encoded::Header {
|
||||
let block = self.best_block.read();
|
||||
BlockView::new(&block.block).header_view().rlp().as_raw().to_vec()
|
||||
let raw = BlockView::new(&block.block).header_view().rlp().as_raw().to_vec();
|
||||
encoded::Header::new(raw)
|
||||
}
|
||||
|
||||
/// Get current cache size.
|
||||
@ -1329,7 +1333,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn new_chain(genesis: &[u8], db: Arc<Database>) -> BlockChain {
|
||||
BlockChain::new(Config::default(), genesis, db, Spec::new_null().engine)
|
||||
BlockChain::new(Config::default(), genesis, db, Spec::new_null().engine)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -157,10 +157,10 @@ impl Decodable for BlockDetails {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let details = BlockDetails {
|
||||
number: try!(d.val_at(0)),
|
||||
total_difficulty: try!(d.val_at(1)),
|
||||
parent: try!(d.val_at(2)),
|
||||
children: try!(d.val_at(3)),
|
||||
number: d.val_at(0)?,
|
||||
total_difficulty: d.val_at(1)?,
|
||||
parent: d.val_at(2)?,
|
||||
children: d.val_at(3)?,
|
||||
};
|
||||
Ok(details)
|
||||
}
|
||||
@ -193,8 +193,8 @@ impl Decodable for TransactionAddress {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let tx_address = TransactionAddress {
|
||||
block_hash: try!(d.val_at(0)),
|
||||
index: try!(d.val_at(1)),
|
||||
block_hash: d.val_at(0)?,
|
||||
index: d.val_at(1)?,
|
||||
};
|
||||
|
||||
Ok(tx_address)
|
||||
@ -226,7 +226,7 @@ impl BlockReceipts {
|
||||
impl Decodable for BlockReceipts {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
Ok(BlockReceipts {
|
||||
receipts: try!(Decodable::decode(decoder))
|
||||
receipts: Decodable::decode(decoder)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl Into<bc::BloomGroup> for BloomGroup {
|
||||
|
||||
impl Decodable for BloomGroup {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let blooms = try!(Decodable::decode(decoder));
|
||||
let blooms = Decodable::decode(decoder)?;
|
||||
let group = BloomGroup {
|
||||
blooms: blooms
|
||||
};
|
||||
|
@ -32,7 +32,7 @@ use util::kvdb::*;
|
||||
|
||||
// other
|
||||
use io::*;
|
||||
use views::{HeaderView, BodyView, BlockView};
|
||||
use views::BlockView;
|
||||
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
|
||||
use header::BlockNumber;
|
||||
use state::{State, CleanupMode};
|
||||
@ -67,10 +67,11 @@ use evm::{Factory as EvmFactory, Schedule};
|
||||
use miner::{Miner, MinerService};
|
||||
use snapshot::{self, io as snapshot_io};
|
||||
use factory::Factories;
|
||||
use rlp::{decode, View, UntrustedRlp};
|
||||
use rlp::{View, UntrustedRlp};
|
||||
use state_db::StateDB;
|
||||
use rand::OsRng;
|
||||
use client::registry::Registry;
|
||||
use encoded;
|
||||
|
||||
// re-export
|
||||
pub use types::blockchain_info::BlockChainInfo;
|
||||
@ -167,26 +168,33 @@ impl Client {
|
||||
) -> Result<Arc<Client>, ClientError> {
|
||||
|
||||
let path = path.to_path_buf();
|
||||
let gb = spec.genesis_block();
|
||||
|
||||
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().expect("DB path could not be converted to string.")).map_err(ClientError::Database)));
|
||||
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone(), spec.engine.clone()));
|
||||
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
|
||||
|
||||
let db = Arc::new(Database::open(&db_config, &path.to_str().expect("DB path could not be converted to string.")).map_err(ClientError::Database)?);
|
||||
let trie_spec = match config.fat_db {
|
||||
true => TrieSpec::Fat,
|
||||
false => TrieSpec::Secure,
|
||||
};
|
||||
|
||||
let trie_factory = TrieFactory::new(trie_spec);
|
||||
let factories = Factories {
|
||||
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||
trie: trie_factory,
|
||||
accountdb: Default::default(),
|
||||
};
|
||||
|
||||
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
||||
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
|
||||
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db, &trie_factory)) {
|
||||
if state_db.journal_db().is_empty() {
|
||||
// Sets the correct state root.
|
||||
state_db = spec.ensure_db_good(state_db, &factories)?;
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash())?;
|
||||
db.write(batch).map_err(ClientError::Database)?;
|
||||
}
|
||||
|
||||
let gb = spec.genesis_block();
|
||||
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone(), spec.engine.clone()));
|
||||
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
|
||||
|
||||
trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());
|
||||
|
||||
let history = if config.history < MIN_HISTORY_SIZE {
|
||||
@ -203,8 +211,8 @@ impl Client {
|
||||
for era in earliest..(latest - history + 1) {
|
||||
trace!("Removing era {}", era);
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
try!(state_db.mark_canonical(&mut batch, era, &chain.block_hash(era).expect("Old block not found in the database")));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
state_db.mark_canonical(&mut batch, era, &chain.block_hash(era).expect("Old block not found in the database"))?;
|
||||
db.write(batch).map_err(ClientError::Database)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,12 +229,6 @@ impl Client {
|
||||
|
||||
let awake = match config.mode { Mode::Dark(..) | Mode::Off => false, _ => true };
|
||||
|
||||
let factories = Factories {
|
||||
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||
trie: trie_factory,
|
||||
accountdb: Default::default(),
|
||||
};
|
||||
|
||||
let client = Arc::new(Client {
|
||||
enabled: AtomicBool::new(true),
|
||||
sleep_state: Mutex::new(SleepState::new(awake)),
|
||||
@ -251,7 +253,7 @@ impl Client {
|
||||
last_hashes: RwLock::new(VecDeque::new()),
|
||||
factories: factories,
|
||||
history: history,
|
||||
rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))),
|
||||
rng: Mutex::new(OsRng::new().map_err(::util::UtilError::StdIo)?),
|
||||
on_mode_change: Mutex::new(None),
|
||||
registrar: Mutex::new(None),
|
||||
});
|
||||
@ -302,17 +304,16 @@ impl Client {
|
||||
|
||||
/// The env info as of the best block.
|
||||
fn latest_env_info(&self) -> EnvInfo {
|
||||
let header_data = self.best_block_header();
|
||||
let view = HeaderView::new(&header_data);
|
||||
let header = self.best_block_header();
|
||||
|
||||
EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
timestamp: view.timestamp(),
|
||||
difficulty: view.difficulty(),
|
||||
last_hashes: self.build_last_hashes(view.hash()),
|
||||
number: header.number(),
|
||||
author: header.author(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: header.difficulty(),
|
||||
last_hashes: self.build_last_hashes(header.hash()),
|
||||
gas_used: U256::default(),
|
||||
gas_limit: view.gas_limit(),
|
||||
gas_limit: header.gas_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,9 +370,9 @@ impl Client {
|
||||
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||
let locked_block = try!(enact_result.map_err(|e| {
|
||||
let locked_block = enact_result.map_err(|e| {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
}));
|
||||
})?;
|
||||
|
||||
// Final Verification
|
||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
||||
@ -510,14 +511,14 @@ impl Client {
|
||||
let chain = self.chain.read();
|
||||
|
||||
// verify block.
|
||||
try!(::snapshot::verify_old_block(
|
||||
::snapshot::verify_old_block(
|
||||
&mut *rng,
|
||||
&header,
|
||||
&*self.engine,
|
||||
&*chain,
|
||||
Some(&block_bytes),
|
||||
false,
|
||||
));
|
||||
)?;
|
||||
|
||||
// Commit results
|
||||
let receipts = ::rlp::decode(&receipts_bytes);
|
||||
@ -618,7 +619,8 @@ impl Client {
|
||||
/// Attempt to get a copy of a specific block's final state.
|
||||
///
|
||||
/// This will not fail if given BlockId::Latest.
|
||||
/// Otherwise, this can fail (but may not) if the DB prunes state.
|
||||
/// Otherwise, this can fail (but may not) if the DB prunes state or the block
|
||||
/// is unknown.
|
||||
pub fn state_at(&self, id: BlockId) -> Option<State> {
|
||||
// fast path for latest state.
|
||||
match id.clone() {
|
||||
@ -640,8 +642,7 @@ impl Client {
|
||||
return None;
|
||||
}
|
||||
|
||||
let root = HeaderView::new(&header).state_root();
|
||||
|
||||
let root = header.state_root();
|
||||
State::from_existing(db, root, self.engine.account_start_nonce(), self.factories.clone()).ok()
|
||||
})
|
||||
}
|
||||
@ -664,7 +665,6 @@ impl Client {
|
||||
/// Get a copy of the best block's state.
|
||||
pub fn state(&self) -> State {
|
||||
let header = self.best_block_header();
|
||||
let header = HeaderView::new(&header);
|
||||
State::from_existing(
|
||||
self.state_db.lock().boxed_clone_canon(&header.hash()),
|
||||
header.state_root(),
|
||||
@ -737,7 +737,7 @@ impl Client {
|
||||
pub fn take_snapshot<W: snapshot_io::SnapshotWriter + Send>(&self, writer: W, at: BlockId, p: &snapshot::Progress) -> Result<(), EthcoreError> {
|
||||
let db = self.state_db.lock().journal_db().boxed_clone();
|
||||
let best_block_number = self.chain_info().best_block_number;
|
||||
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
||||
let block_number = self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at))?;
|
||||
|
||||
if best_block_number > self.history + block_number && db.is_pruned() {
|
||||
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
||||
@ -763,7 +763,7 @@ impl Client {
|
||||
},
|
||||
};
|
||||
|
||||
try!(snapshot::take_snapshot(&self.chain.read(), start_hash, db.as_hashdb(), writer, p));
|
||||
snapshot::take_snapshot(&self.chain.read(), start_hash, db.as_hashdb(), writer, p)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -827,7 +827,7 @@ impl snapshot::DatabaseRestore for Client {
|
||||
let mut tracedb = self.tracedb.write();
|
||||
self.miner.clear();
|
||||
let db = self.db.write();
|
||||
try!(db.restore(new_db));
|
||||
db.restore(new_db)?;
|
||||
|
||||
let cache_size = state_db.cache_size();
|
||||
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
|
||||
@ -840,26 +840,25 @@ impl snapshot::DatabaseRestore for Client {
|
||||
|
||||
impl BlockChainClient for Client {
|
||||
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
|
||||
let view = HeaderView::new(&header);
|
||||
let last_hashes = self.build_last_hashes(view.parent_hash());
|
||||
let header = self.block_header(block).ok_or(CallError::StatePruned)?;
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash());
|
||||
let env_info = EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
timestamp: view.timestamp(),
|
||||
difficulty: view.difficulty(),
|
||||
number: header.number(),
|
||||
author: header.author(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: header.difficulty(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: U256::max_value(),
|
||||
};
|
||||
// that's just a copy of the state.
|
||||
let mut state = try!(self.state_at(block).ok_or(CallError::StatePruned));
|
||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
|
||||
let sender = try!(t.sender().map_err(|e| {
|
||||
let sender = t.sender().map_err(|e| {
|
||||
let message = format!("Transaction malformed: {:?}", e);
|
||||
ExecutionError::TransactionMalformed(message)
|
||||
}));
|
||||
})?;
|
||||
let balance = state.balance(&sender);
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
@ -867,7 +866,7 @@ impl BlockChainClient for Client {
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options));
|
||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)?;
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
ret.state_diff = original_state.map(|original| state.diff_from(original));
|
||||
@ -876,27 +875,26 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let address = try!(self.transaction_address(id).ok_or(CallError::TransactionNotFound));
|
||||
let header_data = try!(self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned));
|
||||
let body_data = try!(self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned));
|
||||
let mut state = try!(self.state_at_beginning(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned));
|
||||
let txs = BodyView::new(&body_data).transactions();
|
||||
let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?;
|
||||
let header = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
let body = self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
let mut state = self.state_at_beginning(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
let txs = body.transactions();
|
||||
|
||||
if address.index >= txs.len() {
|
||||
return Err(CallError::TransactionNotFound);
|
||||
}
|
||||
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let view = HeaderView::new(&header_data);
|
||||
let last_hashes = self.build_last_hashes(view.hash());
|
||||
let last_hashes = self.build_last_hashes(header.hash());
|
||||
let mut env_info = EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
timestamp: view.timestamp(),
|
||||
difficulty: view.difficulty(),
|
||||
number: header.number(),
|
||||
author: header.author(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: header.difficulty(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::default(),
|
||||
gas_limit: view.gas_limit(),
|
||||
gas_limit: header.gas_limit(),
|
||||
};
|
||||
for t in txs.iter().take(address.index) {
|
||||
match Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, Default::default()) {
|
||||
@ -907,7 +905,7 @@ impl BlockChainClient for Client {
|
||||
let t = &txs[address.index];
|
||||
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options));
|
||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)?;
|
||||
ret.state_diff = original_state.map(|original| state.diff_from(original));
|
||||
|
||||
Ok(ret)
|
||||
@ -957,11 +955,11 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
fn best_block_header(&self) -> Bytes {
|
||||
fn best_block_header(&self) -> encoded::Header {
|
||||
self.chain.read().best_block_header()
|
||||
}
|
||||
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_header(&self, id: BlockId) -> Option<::encoded::Header> {
|
||||
let chain = self.chain.read();
|
||||
Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash))
|
||||
}
|
||||
@ -975,15 +973,15 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
|
||||
let chain = self.chain.read();
|
||||
Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash))
|
||||
}
|
||||
|
||||
fn block(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block(&self, id: BlockId) -> Option<encoded::Block> {
|
||||
if let BlockId::Pending = id {
|
||||
if let Some(block) = self.miner.pending_block() {
|
||||
return Some(block.rlp_bytes(Seal::Without));
|
||||
return Some(encoded::Block::new(block.rlp_bytes(Seal::Without)));
|
||||
}
|
||||
}
|
||||
let chain = self.chain.read();
|
||||
@ -1126,9 +1124,10 @@ impl BlockChainClient for Client {
|
||||
self.transaction_address(id).map(|addr| addr.block_hash)
|
||||
}
|
||||
|
||||
fn uncle(&self, id: UncleId) -> Option<Bytes> {
|
||||
fn uncle(&self, id: UncleId) -> Option<encoded::Header> {
|
||||
let index = id.position;
|
||||
self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index))
|
||||
self.block_body(id.block).and_then(|body| body.view().uncle_rlp_at(index))
|
||||
.map(encoded::Header::new)
|
||||
}
|
||||
|
||||
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
|
||||
@ -1136,8 +1135,8 @@ impl BlockChainClient for Client {
|
||||
self.transaction_address(id)
|
||||
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
|
||||
let t = chain.block_body(&address.block_hash)
|
||||
.and_then(|block| {
|
||||
BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
|
||||
.and_then(|body| {
|
||||
body.view().localized_transaction_at(&address.block_hash, block_number, address.index)
|
||||
});
|
||||
|
||||
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
|
||||
@ -1205,7 +1204,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
use verification::queue::kind::HasHash;
|
||||
use verification::queue::kind::BlockLike;
|
||||
use verification::queue::kind::blocks::Unverified;
|
||||
|
||||
// create unverified block here so the `sha3` calculation can be cached.
|
||||
@ -1219,7 +1218,7 @@ impl BlockChainClient for Client {
|
||||
return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash())));
|
||||
}
|
||||
}
|
||||
Ok(try!(self.block_queue.import(unverified)))
|
||||
Ok(self.block_queue.import(unverified)?)
|
||||
}
|
||||
|
||||
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
@ -1245,7 +1244,9 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo {
|
||||
self.chain.read().chain_info()
|
||||
let mut chain_info = self.chain.read().chain_info();
|
||||
chain_info.pending_total_difficulty = chain_info.total_difficulty + self.block_queue.total_difficulty();
|
||||
chain_info
|
||||
}
|
||||
|
||||
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||
@ -1356,19 +1357,19 @@ impl BlockChainClient for Client {
|
||||
|
||||
fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
|
||||
self.block_header(id)
|
||||
.map(|block| decode(&block))
|
||||
.map(|header| self.engine.extra_info(&header))
|
||||
.map(|header| self.engine.extra_info(&header.decode()))
|
||||
}
|
||||
|
||||
fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> {
|
||||
self.uncle(id)
|
||||
.map(|header| self.engine.extra_info(&decode(&header)))
|
||||
.map(|header| self.engine.extra_info(&header.decode()))
|
||||
}
|
||||
|
||||
fn pruning_info(&self) -> PruningInfo {
|
||||
PruningInfo {
|
||||
earliest_chain: self.chain.read().first_block_number().unwrap_or(1),
|
||||
earliest_state: self.state_db.lock().journal_db().earliest_era().unwrap_or(0),
|
||||
state_history_size: Some(self.history),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}`
|
||||
@ -55,7 +55,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"set","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}`
|
||||
@ -67,7 +67,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}`
|
||||
@ -79,7 +79,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
|
||||
@ -91,7 +91,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_address().ok_or("Invalid type returned")); util::Address::from(r) }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"}`
|
||||
@ -115,7 +115,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
|
||||
@ -127,7 +127,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_address().ok_or("Invalid type returned")); util::Address::from(r) }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"payable":false,"type":"function"}`
|
||||
@ -139,7 +139,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[],"name":"drain","outputs":[],"payable":false,"type":"function"}`
|
||||
@ -163,7 +163,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}`
|
||||
@ -175,7 +175,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_uint().ok_or("Invalid type returned")); util::U256::from(r.as_ref()) }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_uint().ok_or("Invalid type returned")?; util::U256::from(r.as_ref()) }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}`
|
||||
@ -187,7 +187,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_fixed_bytes().ok_or("Invalid type returned")); util::H256::from_slice(r.as_ref()) }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_fixed_bytes().ok_or("Invalid type returned")?; util::H256::from_slice(r.as_ref()) }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}`
|
||||
@ -199,7 +199,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_uint().ok_or("Invalid type returned")); util::U256::from(r.as_ref()) }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_uint().ok_or("Invalid type returned")?; util::U256::from(r.as_ref()) }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
|
||||
@ -211,7 +211,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_address().ok_or("Invalid type returned")); util::Address::from(r) }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}`
|
||||
@ -223,7 +223,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_string().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_string().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}`
|
||||
@ -235,7 +235,7 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
|
||||
/// Auto-generated from: `{"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"payable":false,"type":"function"}`
|
||||
@ -259,6 +259,6 @@ impl Registry {
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = try!(r.to_bool().ok_or("Invalid type returned")); r }))
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||
}
|
||||
}
|
@ -39,7 +39,6 @@ use miner::{Miner, MinerService, TransactionImportResult};
|
||||
use spec::Spec;
|
||||
use types::mode::Mode;
|
||||
use types::pruning_info::PruningInfo;
|
||||
use views::BlockView;
|
||||
|
||||
use verification::queue::QueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
@ -47,6 +46,7 @@ use executive::Executed;
|
||||
use error::CallError;
|
||||
use trace::LocalizedTrace;
|
||||
use state_db::StateDB;
|
||||
use encoded;
|
||||
|
||||
/// Test client.
|
||||
pub struct TestBlockChainClient {
|
||||
@ -92,6 +92,8 @@ pub struct TestBlockChainClient {
|
||||
pub first_block: RwLock<Option<(H256, u64)>>,
|
||||
/// Traces to return
|
||||
pub traces: RwLock<Option<Vec<LocalizedTrace>>>,
|
||||
/// Pruning history size to report.
|
||||
pub history: RwLock<Option<u64>>,
|
||||
}
|
||||
|
||||
/// Used for generating test client blocks.
|
||||
@ -154,6 +156,7 @@ impl TestBlockChainClient {
|
||||
ancient_block: RwLock::new(None),
|
||||
first_block: RwLock::new(None),
|
||||
traces: RwLock::new(None),
|
||||
history: RwLock::new(None),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
@ -260,7 +263,7 @@ impl TestBlockChainClient {
|
||||
/// Make a bad block by setting invalid extra data.
|
||||
pub fn corrupt_block(&self, n: BlockNumber) {
|
||||
let hash = self.block_hash(BlockId::Number(n)).unwrap();
|
||||
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
|
||||
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode();
|
||||
header.set_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);
|
||||
@ -272,7 +275,7 @@ impl TestBlockChainClient {
|
||||
/// Make a bad block by setting invalid parent hash.
|
||||
pub fn corrupt_block_parent(&self, n: BlockNumber) {
|
||||
let hash = self.block_hash(BlockId::Number(n)).unwrap();
|
||||
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
|
||||
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode();
|
||||
header.set_parent_hash(H256::from(42));
|
||||
let mut rlp = RlpStream::new_list(3);
|
||||
rlp.append(&header);
|
||||
@ -314,6 +317,11 @@ impl TestBlockChainClient {
|
||||
let res = res.into_iter().next().unwrap().expect("Successful import");
|
||||
assert_eq!(res, TransactionImportResult::Current);
|
||||
}
|
||||
|
||||
/// Set reported history size.
|
||||
pub fn set_history(&self, h: Option<u64>) {
|
||||
*self.history.write() = h;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||
@ -336,8 +344,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
let engine = &*self.spec.engine;
|
||||
let genesis_header = self.spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
self.spec.ensure_db_good(&mut db, &TrieFactory::default()).unwrap();
|
||||
let db = self.spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let mut open_block = OpenBlock::new(
|
||||
@ -451,7 +458,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
None // Simple default.
|
||||
}
|
||||
|
||||
fn uncle(&self, _id: UncleId) -> Option<Bytes> {
|
||||
fn uncle(&self, _id: UncleId) -> Option<encoded::Header> {
|
||||
None // Simple default.
|
||||
}
|
||||
|
||||
@ -480,34 +487,39 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn best_block_header(&self) -> Bytes {
|
||||
self.block_header(BlockId::Hash(self.chain_info().best_block_hash)).expect("Best block always have header.")
|
||||
fn best_block_header(&self) -> encoded::Header {
|
||||
self.block_header(BlockId::Hash(self.chain_info().best_block_hash))
|
||||
.expect("Best block always has header.")
|
||||
}
|
||||
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
||||
self.block_hash(id)
|
||||
.and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
||||
.map(encoded::Header::new)
|
||||
}
|
||||
|
||||
fn block_number(&self, _id: BlockId) -> Option<BlockNumber> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
||||
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append_raw(Rlp::new(r).at(1).as_raw(), 1);
|
||||
stream.append_raw(Rlp::new(r).at(2).as_raw(), 1);
|
||||
stream.out()
|
||||
encoded::Body::new(stream.out())
|
||||
}))
|
||||
}
|
||||
|
||||
fn block(&self, id: BlockId) -> Option<Bytes> {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned())
|
||||
fn block(&self, id: BlockId) -> Option<encoded::Block> {
|
||||
self.block_hash(id)
|
||||
.and_then(|hash| self.blocks.read().get(&hash).cloned())
|
||||
.map(encoded::Block::new)
|
||||
}
|
||||
|
||||
fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
|
||||
self.block(id)
|
||||
.map(|block| BlockView::new(&block).header())
|
||||
.map(|block| block.view().header())
|
||||
.map(|header| self.spec.engine.extra_info(&header))
|
||||
}
|
||||
|
||||
@ -516,7 +528,8 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
match id {
|
||||
BlockId::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain,
|
||||
BlockId::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain,
|
||||
_ => BlockStatus::Unknown
|
||||
BlockId::Latest | BlockId::Pending | BlockId::Earliest => BlockStatus::InChain,
|
||||
_ => BlockStatus::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@ -704,6 +717,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
PruningInfo {
|
||||
earliest_chain: 1,
|
||||
earliest_state: 1,
|
||||
state_history_size: *self.history.read(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ use header::{BlockNumber};
|
||||
use transaction::{LocalizedTransaction, SignedTransaction, PendingTransaction};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use filter::Filter;
|
||||
use views::{BlockView};
|
||||
use error::{ImportResult, CallError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
@ -40,6 +39,7 @@ use types::blockchain_info::BlockChainInfo;
|
||||
use types::block_status::BlockStatus;
|
||||
use types::mode::Mode;
|
||||
use types::pruning_info::PruningInfo;
|
||||
use encoded;
|
||||
|
||||
#[ipc(client_ident="RemoteClient")]
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
@ -50,17 +50,17 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn keep_alive(&self) {}
|
||||
|
||||
/// Get raw block header data by block id.
|
||||
fn block_header(&self, id: BlockId) -> Option<Bytes>;
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
|
||||
|
||||
/// Look up the block number for the given block ID.
|
||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber>;
|
||||
|
||||
/// 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<encoded::Body>;
|
||||
|
||||
/// Get raw block data by block header hash.
|
||||
fn block(&self, id: BlockId) -> Option<Bytes>;
|
||||
fn block(&self, id: BlockId) -> Option<encoded::Block>;
|
||||
|
||||
/// Get block status by block header hash.
|
||||
fn block_status(&self, id: BlockId) -> BlockStatus;
|
||||
@ -136,7 +136,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn transaction_block(&self, id: TransactionId) -> Option<H256>;
|
||||
|
||||
/// Get uncle with given id.
|
||||
fn uncle(&self, id: UncleId) -> Option<Bytes>;
|
||||
fn uncle(&self, id: UncleId) -> Option<encoded::Header>;
|
||||
|
||||
/// Get transaction receipt with given hash.
|
||||
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;
|
||||
@ -173,7 +173,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn additional_params(&self) -> BTreeMap<String, String>;
|
||||
|
||||
/// Get the best block header.
|
||||
fn best_block_header(&self) -> Bytes;
|
||||
fn best_block_header(&self) -> encoded::Header;
|
||||
|
||||
/// Returns numbers of blocks containing given bloom.
|
||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>;
|
||||
@ -220,8 +220,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
let mut corpus = Vec::new();
|
||||
while corpus.is_empty() {
|
||||
for _ in 0..sample_size {
|
||||
let block_bytes = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let block = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
return corpus;
|
||||
@ -277,7 +276,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Get the address of the registry itself.
|
||||
fn registrar_address(&self) -> Option<Address>;
|
||||
|
||||
/// Get the address of a particular blockchain service, if available.
|
||||
/// Get the address of a particular blockchain service, if available.
|
||||
fn registry_address(&self, name: String) -> Option<Address>;
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ impl AuthorityRound {
|
||||
params: params,
|
||||
our_params: our_params,
|
||||
builtins: builtins,
|
||||
transition_service: try!(IoService::<()>::start()),
|
||||
transition_service: IoService::<()>::start()?,
|
||||
message_channel: Mutex::new(None),
|
||||
step: AtomicUsize::new(initial_step),
|
||||
proposed: AtomicBool::new(false),
|
||||
@ -117,7 +117,7 @@ impl AuthorityRound {
|
||||
// Do not initialize timeouts for tests.
|
||||
if should_timeout {
|
||||
let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
|
||||
try!(engine.transition_service.register_handler(Arc::new(handler)));
|
||||
engine.transition_service.register_handler(Arc::new(handler))?;
|
||||
}
|
||||
Ok(engine)
|
||||
}
|
||||
@ -263,20 +263,20 @@ impl Engine for AuthorityRound {
|
||||
|
||||
/// Check if the signature belongs to the correct proposer.
|
||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
let header_step = try!(header_step(header));
|
||||
let header_step = header_step(header)?;
|
||||
// Give one step slack if step is lagging, double vote is still not possible.
|
||||
if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
|
||||
let proposer_signature = try!(header_signature(header));
|
||||
let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash()));
|
||||
let proposer_signature = header_signature(header)?;
|
||||
let ok_sig = verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash())?;
|
||||
if ok_sig {
|
||||
Ok(())
|
||||
} else {
|
||||
trace!(target: "poa", "verify_block_unordered: invalid seal signature");
|
||||
try!(Err(BlockError::InvalidSeal))
|
||||
Err(BlockError::InvalidSeal)?
|
||||
}
|
||||
} else {
|
||||
trace!(target: "poa", "verify_block_unordered: block from the future");
|
||||
try!(Err(BlockError::InvalidSeal))
|
||||
Err(BlockError::InvalidSeal)?
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,11 +285,11 @@ impl Engine for AuthorityRound {
|
||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||
}
|
||||
|
||||
let step = try!(header_step(header));
|
||||
let step = header_step(header)?;
|
||||
// Check if parent is from a previous step.
|
||||
if step == try!(header_step(parent)) {
|
||||
if step == header_step(parent)? {
|
||||
trace!(target: "poa", "Multiple blocks proposed for step {}.", step);
|
||||
try!(Err(EngineError::DoubleVote(header.author().clone())));
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
@ -302,7 +302,7 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
try!(t.check_low_s());
|
||||
t.check_low_s()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -339,7 +339,6 @@ impl Engine for AuthorityRound {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use env_info::EnvInfo;
|
||||
use header::Header;
|
||||
use error::{Error, BlockError};
|
||||
@ -407,10 +406,8 @@ mod tests {
|
||||
let engine = &*spec.engine;
|
||||
engine.register_account_provider(Arc::new(tap));
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db1 = get_temp_state_db().take();
|
||||
spec.ensure_db_good(&mut db1, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let mut db2 = get_temp_state_db().take();
|
||||
spec.ensure_db_good(&mut db2, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db1 = spec.ensure_db_good(get_temp_state_db().take(), &Default::default()).unwrap();
|
||||
let db2 = spec.ensure_db_good(get_temp_state_db().take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
|
@ -143,10 +143,10 @@ impl Engine for BasicAuthority {
|
||||
use rlp::{UntrustedRlp, View};
|
||||
|
||||
// check the signature is legit.
|
||||
let sig = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H520>());
|
||||
let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash())));
|
||||
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
|
||||
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
|
||||
if !self.our_params.authorities.contains(&signer) {
|
||||
return try!(Err(BlockError::InvalidSeal));
|
||||
return Err(BlockError::InvalidSeal)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -171,7 +171,7 @@ impl Engine for BasicAuthority {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> result::Result<(), Error> {
|
||||
try!(t.check_low_s());
|
||||
t.check_low_s()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -191,7 +191,6 @@ impl Engine for BasicAuthority {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use block::*;
|
||||
use env_info::EnvInfo;
|
||||
use error::{BlockError, Error};
|
||||
@ -265,8 +264,7 @@ mod tests {
|
||||
engine.register_account_provider(Arc::new(tap));
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
|
@ -66,7 +66,6 @@ impl Engine for InstantSeal {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use tests::helpers::*;
|
||||
use spec::Spec;
|
||||
use header::Header;
|
||||
@ -79,8 +78,7 @@ mod tests {
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
|
@ -51,9 +51,9 @@ impl ConsensusMessage {
|
||||
|
||||
pub fn new_proposal(header: &Header) -> Result<Self, ::rlp::DecoderError> {
|
||||
Ok(ConsensusMessage {
|
||||
signature: try!(UntrustedRlp::new(header.seal().get(1).expect("seal passed basic verification; seal has 3 fields; qed").as_slice()).as_val()),
|
||||
signature: UntrustedRlp::new(header.seal().get(1).expect("seal passed basic verification; seal has 3 fields; qed").as_slice()).as_val()?,
|
||||
height: header.number() as Height,
|
||||
round: try!(consensus_round(header)),
|
||||
round: consensus_round(header)?,
|
||||
step: Step::Propose,
|
||||
block_hash: Some(header.bare_hash()),
|
||||
})
|
||||
@ -92,7 +92,7 @@ impl ConsensusMessage {
|
||||
pub fn verify(&self) -> Result<Address, Error> {
|
||||
let full_rlp = ::rlp::encode(self);
|
||||
let block_info = Rlp::new(&full_rlp).at(1);
|
||||
let public_key = try!(recover(&self.signature.into(), &block_info.as_raw().sha3()));
|
||||
let public_key = recover(&self.signature.into(), &block_info.as_raw().sha3())?;
|
||||
Ok(public_to_address(&public_key))
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ impl Ord for ConsensusMessage {
|
||||
|
||||
impl Decodable for Step {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
match try!(decoder.as_rlp().as_val()) {
|
||||
match decoder.as_rlp().as_val()? {
|
||||
0u8 => Ok(Step::Propose),
|
||||
1 => Ok(Step::Prevote),
|
||||
2 => Ok(Step::Precommit),
|
||||
@ -153,13 +153,13 @@ impl Encodable for Step {
|
||||
impl Decodable for ConsensusMessage {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let rlp = decoder.as_rlp();
|
||||
let m = try!(rlp.at(1));
|
||||
let block_message: H256 = try!(m.val_at(3));
|
||||
let m = rlp.at(1)?;
|
||||
let block_message: H256 = m.val_at(3)?;
|
||||
Ok(ConsensusMessage {
|
||||
signature: try!(rlp.val_at(0)),
|
||||
height: try!(m.val_at(0)),
|
||||
round: try!(m.val_at(1)),
|
||||
step: try!(m.val_at(2)),
|
||||
signature: rlp.val_at(0)?,
|
||||
height: m.val_at(0)?,
|
||||
round: m.val_at(1)?,
|
||||
step: m.val_at(2)?,
|
||||
block_hash: match block_message.is_zero() {
|
||||
true => None,
|
||||
false => Some(block_message),
|
||||
|
@ -109,7 +109,7 @@ impl Tendermint {
|
||||
params: params,
|
||||
our_params: our_params,
|
||||
builtins: builtins,
|
||||
step_service: try!(IoService::<Step>::start()),
|
||||
step_service: IoService::<Step>::start()?,
|
||||
authority: RwLock::new(Address::default()),
|
||||
password: RwLock::new(None),
|
||||
height: AtomicUsize::new(1),
|
||||
@ -123,7 +123,7 @@ impl Tendermint {
|
||||
proposal: RwLock::new(None),
|
||||
});
|
||||
let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
|
||||
try!(engine.step_service.register_handler(Arc::new(handler)));
|
||||
engine.step_service.register_handler(Arc::new(handler))?;
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
@ -455,11 +455,11 @@ impl Engine for Tendermint {
|
||||
|
||||
fn handle_message(&self, rlp: &[u8]) -> Result<(), Error> {
|
||||
let rlp = UntrustedRlp::new(rlp);
|
||||
let message: ConsensusMessage = try!(rlp.as_val());
|
||||
let message: ConsensusMessage = rlp.as_val()?;
|
||||
if !self.votes.is_old_or_known(&message) {
|
||||
let sender = public_to_address(&try!(recover(&message.signature.into(), &try!(rlp.at(1)).as_raw().sha3())));
|
||||
let sender = public_to_address(&recover(&message.signature.into(), &rlp.at(1)?.as_raw().sha3())?);
|
||||
if !self.is_authority(&sender) {
|
||||
try!(Err(EngineError::NotAuthorized(sender)));
|
||||
Err(EngineError::NotAuthorized(sender))?;
|
||||
}
|
||||
self.broadcast_message(rlp.as_raw().to_vec());
|
||||
trace!(target: "poa", "Handling a valid {:?} from {}.", message, sender);
|
||||
@ -491,10 +491,10 @@ impl Engine for Tendermint {
|
||||
}
|
||||
|
||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
let proposal = try!(ConsensusMessage::new_proposal(header));
|
||||
let proposer = try!(proposal.verify());
|
||||
let proposal = ConsensusMessage::new_proposal(header)?;
|
||||
let proposer = proposal.verify()?;
|
||||
if !self.is_authority(&proposer) {
|
||||
try!(Err(EngineError::NotAuthorized(proposer)))
|
||||
Err(EngineError::NotAuthorized(proposer))?
|
||||
}
|
||||
|
||||
let precommit_hash = proposal.precommit_hash();
|
||||
@ -502,20 +502,20 @@ impl Engine for Tendermint {
|
||||
let mut signature_count = 0;
|
||||
let mut origins = HashSet::new();
|
||||
for rlp in UntrustedRlp::new(signatures_field).iter() {
|
||||
let precommit: ConsensusMessage = ConsensusMessage::new_commit(&proposal, try!(rlp.as_val()));
|
||||
let precommit: ConsensusMessage = ConsensusMessage::new_commit(&proposal, rlp.as_val()?);
|
||||
let address = match self.votes.get(&precommit) {
|
||||
Some(a) => a,
|
||||
None => public_to_address(&try!(recover(&precommit.signature.into(), &precommit_hash))),
|
||||
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
|
||||
};
|
||||
if !self.our_params.authorities.contains(&address) {
|
||||
try!(Err(EngineError::NotAuthorized(address.to_owned())))
|
||||
Err(EngineError::NotAuthorized(address.to_owned()))?
|
||||
}
|
||||
|
||||
if origins.insert(address) {
|
||||
signature_count += 1;
|
||||
} else {
|
||||
warn!(target: "poa", "verify_block_unordered: Duplicate signature from {} on the seal.", address);
|
||||
try!(Err(BlockError::InvalidSeal));
|
||||
Err(BlockError::InvalidSeal)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,34 +524,34 @@ impl Engine for Tendermint {
|
||||
let signatures_len = signatures_field.len();
|
||||
// Proposal has to have an empty signature list.
|
||||
if signatures_len != 1 {
|
||||
try!(Err(EngineError::BadSealFieldSize(OutOfBounds {
|
||||
Err(EngineError::BadSealFieldSize(OutOfBounds {
|
||||
min: Some(1),
|
||||
max: Some(1),
|
||||
found: signatures_len
|
||||
})));
|
||||
}))?;
|
||||
}
|
||||
try!(self.is_round_proposer(proposal.height, proposal.round, &proposer));
|
||||
self.is_round_proposer(proposal.height, proposal.round, &proposer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.number() == 0 {
|
||||
try!(Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
try!(Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
|
||||
Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
try!(t.check_low_s());
|
||||
t.check_low_s()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -656,7 +656,6 @@ impl Engine for Tendermint {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use io::{IoContext, IoHandler};
|
||||
use block::*;
|
||||
use error::{Error, BlockError};
|
||||
@ -681,8 +680,7 @@ mod tests {
|
||||
|
||||
fn propose_default(spec: &Spec, proposer: Address) -> (LockedBlock, Vec<Bytes>) {
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
@ -889,9 +887,6 @@ mod tests {
|
||||
fn relays_messages() {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine.clone();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
|
||||
let v0 = insert_and_register(&tap, &engine, "0");
|
||||
let v1 = insert_and_register(&tap, &engine, "1");
|
||||
@ -925,9 +920,6 @@ mod tests {
|
||||
fn seal_submission() {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine.clone();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
|
||||
let v0 = insert_and_register(&tap, &engine, "0");
|
||||
let v1 = insert_and_register(&tap, &engine, "1");
|
||||
|
@ -240,8 +240,8 @@ impl Engine for Ethash {
|
||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||
)));
|
||||
}
|
||||
try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H256>());
|
||||
try!(UntrustedRlp::new(&header.seal()[1]).as_val::<H64>());
|
||||
UntrustedRlp::new(&header.seal()[0]).as_val::<H256>()?;
|
||||
UntrustedRlp::new(&header.seal()[1]).as_val::<H64>()?;
|
||||
|
||||
// TODO: consider removing these lines.
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
@ -312,7 +312,7 @@ impl Engine for Ethash {
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
if header.number() >= self.ethash_params.homestead_transition {
|
||||
try!(t.check_low_s());
|
||||
t.check_low_s()?;
|
||||
}
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
@ -433,7 +433,6 @@ impl Header {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use block::*;
|
||||
use tests::helpers::*;
|
||||
use env_info::EnvInfo;
|
||||
@ -449,8 +448,7 @@ mod tests {
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close();
|
||||
@ -463,8 +461,7 @@ mod tests {
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut uncle = Header::new();
|
||||
|
@ -78,7 +78,6 @@ pub fn new_morden() -> Spec { load(include_bytes!("../../res/ethereum/morden.jso
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use state::*;
|
||||
use super::*;
|
||||
use tests::helpers::*;
|
||||
@ -90,8 +89,7 @@ mod tests {
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(), Default::default()).unwrap();
|
||||
assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()), 1u64.into());
|
||||
assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()), 1u64.into());
|
||||
|
@ -159,39 +159,39 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
Request::Gas(gas)
|
||||
},
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
Request::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
|
||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?)
|
||||
},
|
||||
instructions::MSTORE8 => {
|
||||
Request::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
|
||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
|
||||
},
|
||||
instructions::RETURN => {
|
||||
Request::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
},
|
||||
instructions::SHA3 => {
|
||||
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
|
||||
let w = overflowing!(add_gas_usize(Gas::from_u256(*stack.peek(1))?, 31));
|
||||
let words = w >> 5;
|
||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
||||
Request::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
},
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
||||
Request::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
||||
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
|
||||
},
|
||||
instructions::EXTCODECOPY => {
|
||||
Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, Gas::from_u256(*stack.peek(3))?)
|
||||
},
|
||||
instructions::LOG0...instructions::LOG4 => {
|
||||
let no_of_topics = instructions::get_log_topics(instruction);
|
||||
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
||||
|
||||
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
|
||||
Request::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(5), stack.peek(6))),
|
||||
try!(mem_needed(stack.peek(3), stack.peek(4)))
|
||||
mem_needed(stack.peek(5), stack.peek(6))?,
|
||||
mem_needed(stack.peek(3), stack.peek(4))?
|
||||
);
|
||||
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
@ -216,8 +216,8 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
instructions::DELEGATECALL => {
|
||||
let gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(4), stack.peek(5))),
|
||||
try!(mem_needed(stack.peek(2), stack.peek(3)))
|
||||
mem_needed(stack.peek(4), stack.peek(5))?,
|
||||
mem_needed(stack.peek(2), stack.peek(3))?
|
||||
);
|
||||
let requested = *stack.peek(0);
|
||||
|
||||
@ -225,7 +225,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
},
|
||||
instructions::CREATE => {
|
||||
let gas = Gas::from(schedule.create_gas);
|
||||
let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
|
||||
let mem = mem_needed(stack.peek(1), stack.peek(2))?;
|
||||
|
||||
Request::GasMemProvide(gas, mem, None)
|
||||
},
|
||||
@ -248,7 +248,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
}
|
||||
},
|
||||
Request::GasMem(gas, mem_size) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
@ -258,9 +258,9 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
}
|
||||
},
|
||||
Request::GasMemProvide(gas, mem_size, requested) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
let provided = try!(self.gas_provided(schedule, gas, requested));
|
||||
let provided = self.gas_provided(schedule, gas, requested)?;
|
||||
let total_gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionRequirements {
|
||||
@ -271,7 +271,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
}
|
||||
},
|
||||
Request::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
|
||||
let copy = overflowing!(add_gas_usize(copy, 31)) >> 5;
|
||||
let copy_gas = Gas::from(schedule.copy_gas) * copy;
|
||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||
@ -303,7 +303,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
|
||||
|
||||
let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size {
|
||||
let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded));
|
||||
let new_mem_gas = gas_for_mem(req_mem_size_rounded)?;
|
||||
(new_mem_gas - self.current_mem_gas, new_mem_gas)
|
||||
} else {
|
||||
(Gas::from(0), self.current_mem_gas)
|
||||
|
@ -107,7 +107,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
let code = ¶ms.code.as_ref().expect("exec always called with code; qed");
|
||||
let valid_jump_destinations = self.cache.jump_destinations(¶ms.code_hash, code);
|
||||
|
||||
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
|
||||
let mut gasometer = Gasometer::<Cost>::new(Cost::from_u256(params.gas)?);
|
||||
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
|
||||
let mut reader = CodeReader::new(code);
|
||||
let infos = &*instructions::INSTRUCTIONS;
|
||||
@ -117,14 +117,14 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
reader.position += 1;
|
||||
|
||||
let info = &infos[instruction as usize];
|
||||
try!(self.verify_instruction(ext, instruction, info, &stack));
|
||||
self.verify_instruction(ext, instruction, info, &stack)?;
|
||||
|
||||
// Calculate gas cost
|
||||
let requirements = try!(gasometer.requirements(ext, instruction, info, &stack, self.mem.size()));
|
||||
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
|
||||
// TODO: make compile-time removable if too much of a performance hit.
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &requirements.gas_cost.as_u256());
|
||||
|
||||
try!(gasometer.verify_gas(&requirements.gas_cost));
|
||||
gasometer.verify_gas(&requirements.gas_cost)?;
|
||||
self.mem.expand(requirements.memory_required_size);
|
||||
gasometer.current_mem_gas = requirements.memory_total_gas;
|
||||
gasometer.current_gas = gasometer.current_gas - requirements.gas_cost;
|
||||
@ -137,9 +137,9 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
};
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
let result = self.exec_instruction(
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack, requirements.provide_gas
|
||||
));
|
||||
)?;
|
||||
|
||||
evm_debug!({ informant.after_instruction(instruction) });
|
||||
|
||||
@ -154,7 +154,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
// Advance
|
||||
match result {
|
||||
InstructionResult::JumpToPosition(position) => {
|
||||
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
||||
let pos = self.verify_jump(position, &valid_jump_destinations)?;
|
||||
reader.position = pos;
|
||||
},
|
||||
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
|
||||
@ -513,7 +513,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
stack.push(ext.env_info().gas_limit.clone());
|
||||
},
|
||||
_ => {
|
||||
try!(self.exec_stack_instruction(instruction, stack));
|
||||
self.exec_stack_instruction(instruction, stack)?;
|
||||
}
|
||||
};
|
||||
Ok(InstructionResult::Ok)
|
||||
|
@ -122,10 +122,10 @@ impl<'a> Executive<'a> {
|
||||
mut tracer: T,
|
||||
mut vm_tracer: V
|
||||
) -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer {
|
||||
let sender = try!(t.sender().map_err(|e| {
|
||||
let sender = t.sender().map_err(|e| {
|
||||
let message = format!("Transaction malformed: {:?}", e);
|
||||
ExecutionError::TransactionMalformed(message)
|
||||
}));
|
||||
})?;
|
||||
let nonce = self.state.nonce(&sender);
|
||||
|
||||
let schedule = self.engine.schedule(self.info);
|
||||
@ -206,7 +206,7 @@ impl<'a> Executive<'a> {
|
||||
};
|
||||
|
||||
// finalize here!
|
||||
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())))
|
||||
Ok(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())?)
|
||||
}
|
||||
|
||||
fn exec_vm<T, V>(
|
||||
|
@ -265,26 +265,26 @@ impl Decodable for Header {
|
||||
let r = decoder.as_rlp();
|
||||
|
||||
let mut blockheader = Header {
|
||||
parent_hash: try!(r.val_at(0)),
|
||||
uncles_hash: try!(r.val_at(1)),
|
||||
author: try!(r.val_at(2)),
|
||||
state_root: try!(r.val_at(3)),
|
||||
transactions_root: try!(r.val_at(4)),
|
||||
receipts_root: try!(r.val_at(5)),
|
||||
log_bloom: try!(r.val_at(6)),
|
||||
difficulty: try!(r.val_at(7)),
|
||||
number: try!(r.val_at(8)),
|
||||
gas_limit: try!(r.val_at(9)),
|
||||
gas_used: try!(r.val_at(10)),
|
||||
timestamp: try!(r.val_at(11)),
|
||||
extra_data: try!(r.val_at(12)),
|
||||
parent_hash: r.val_at(0)?,
|
||||
uncles_hash: r.val_at(1)?,
|
||||
author: r.val_at(2)?,
|
||||
state_root: r.val_at(3)?,
|
||||
transactions_root: r.val_at(4)?,
|
||||
receipts_root: r.val_at(5)?,
|
||||
log_bloom: r.val_at(6)?,
|
||||
difficulty: r.val_at(7)?,
|
||||
number: r.val_at(8)?,
|
||||
gas_limit: r.val_at(9)?,
|
||||
gas_used: r.val_at(10)?,
|
||||
timestamp: r.val_at(11)?,
|
||||
extra_data: r.val_at(12)?,
|
||||
seal: vec![],
|
||||
hash: RefCell::new(Some(r.as_raw().sha3())),
|
||||
bare_hash: RefCell::new(None),
|
||||
};
|
||||
|
||||
for i in 13..r.item_count() {
|
||||
blockheader.seal.push(try!(r.at(i)).as_raw().to_vec())
|
||||
blockheader.seal.push(r.at(i)?.as_raw().to_vec())
|
||||
}
|
||||
|
||||
Ok(blockheader)
|
||||
|
@ -109,7 +109,7 @@ impl OverlayRecentV7 {
|
||||
// walk all journal entries in the database backwards.
|
||||
// find migrations for any possible inserted keys.
|
||||
fn walk_journal(&mut self, source: Arc<Database>) -> Result<(), Error> {
|
||||
if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
|
||||
if let Some(val) = source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)? {
|
||||
let mut era = decode::<u64>(&val);
|
||||
loop {
|
||||
let mut index: usize = 0;
|
||||
@ -120,7 +120,7 @@ impl OverlayRecentV7 {
|
||||
r.out()
|
||||
};
|
||||
|
||||
if let Some(journal_raw) = try!(source.get(None, &entry_key).map_err(Error::Custom)) {
|
||||
if let Some(journal_raw) = source.get(None, &entry_key).map_err(Error::Custom)? {
|
||||
let rlp = Rlp::new(&journal_raw);
|
||||
|
||||
// migrate all inserted keys.
|
||||
@ -153,8 +153,8 @@ impl OverlayRecentV7 {
|
||||
// replace all possible inserted/deleted keys with their migrated counterparts
|
||||
// and commit the altered entries.
|
||||
fn migrate_journal(&self, source: Arc<Database>, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
|
||||
if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
|
||||
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().to_vec(), dest));
|
||||
if let Some(val) = source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)? {
|
||||
batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().to_vec(), dest)?;
|
||||
|
||||
let mut era = decode::<u64>(&val);
|
||||
loop {
|
||||
@ -166,7 +166,7 @@ impl OverlayRecentV7 {
|
||||
r.out()
|
||||
};
|
||||
|
||||
if let Some(journal_raw) = try!(source.get(None, &entry_key).map_err(Error::Custom)) {
|
||||
if let Some(journal_raw) = source.get(None, &entry_key).map_err(Error::Custom)? {
|
||||
let rlp = Rlp::new(&journal_raw);
|
||||
let id: H256 = rlp.val_at(0);
|
||||
let mut inserted_keys: Vec<(H256, Bytes)> = Vec::new();
|
||||
@ -202,7 +202,7 @@ impl OverlayRecentV7 {
|
||||
stream.append(&deleted_keys);
|
||||
|
||||
// and insert it into the new database.
|
||||
try!(batch.insert(entry_key, stream.out(), dest));
|
||||
batch.insert(entry_key, stream.out(), dest)?;
|
||||
|
||||
index += 1;
|
||||
} else {
|
||||
@ -233,7 +233,7 @@ impl Migration for OverlayRecentV7 {
|
||||
let mut batch = Batch::new(config, col);
|
||||
|
||||
// check version metadata.
|
||||
match try!(source.get(None, V7_VERSION_KEY).map_err(Error::Custom)) {
|
||||
match source.get(None, V7_VERSION_KEY).map_err(Error::Custom)? {
|
||||
Some(ref version) if decode::<u32>(&*version) == DB_VERSION => {}
|
||||
_ => return Err(Error::MigrationImpossible), // missing or wrong version
|
||||
}
|
||||
@ -255,10 +255,10 @@ impl Migration for OverlayRecentV7 {
|
||||
}
|
||||
}
|
||||
|
||||
try!(batch.insert(key, value.into_vec(), dest));
|
||||
batch.insert(key, value.into_vec(), dest)?;
|
||||
}
|
||||
|
||||
try!(self.walk_journal(source.clone()));
|
||||
self.walk_journal(source.clone())?;
|
||||
self.migrate_journal(source, batch, dest)
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ use util::{Database, DBTransaction};
|
||||
/// Can be called on upgraded database with no issues (will do nothing).
|
||||
pub fn generate_bloom(source: Arc<Database>, dest: &mut Database) -> Result<(), Error> {
|
||||
trace!(target: "migration", "Account bloom upgrade started");
|
||||
let best_block_hash = match try!(source.get(COL_EXTRA, b"best")) {
|
||||
let best_block_hash = match source.get(COL_EXTRA, b"best")? {
|
||||
// no migration needed
|
||||
None => {
|
||||
trace!(target: "migration", "No best block hash, skipping");
|
||||
@ -40,7 +40,7 @@ pub fn generate_bloom(source: Arc<Database>, dest: &mut Database) -> Result<(),
|
||||
},
|
||||
Some(hash) => hash,
|
||||
};
|
||||
let best_block_header = match try!(source.get(COL_HEADERS, &best_block_hash)) {
|
||||
let best_block_header = match source.get(COL_HEADERS, &best_block_hash)? {
|
||||
// no best block, nothing to do
|
||||
None => {
|
||||
trace!(target: "migration", "No best block header, skipping");
|
||||
@ -58,9 +58,9 @@ pub fn generate_bloom(source: Arc<Database>, dest: &mut Database) -> Result<(),
|
||||
source.clone(),
|
||||
journaldb::Algorithm::OverlayRecent,
|
||||
COL_STATE);
|
||||
let account_trie = try!(TrieDB::new(state_db.as_hashdb(), &state_root).map_err(|e| Error::Custom(format!("Cannot open trie: {:?}", e))));
|
||||
for item in try!(account_trie.iter().map_err(|_| Error::MigrationImpossible)) {
|
||||
let (ref account_key, _) = try!(item.map_err(|_| Error::MigrationImpossible));
|
||||
let account_trie = TrieDB::new(state_db.as_hashdb(), &state_root).map_err(|e| Error::Custom(format!("Cannot open trie: {:?}", e)))?;
|
||||
for item in account_trie.iter().map_err(|_| Error::MigrationImpossible)? {
|
||||
let (ref account_key, _) = item.map_err(|_| Error::MigrationImpossible)?;
|
||||
let account_key_hash = H256::from_slice(account_key);
|
||||
bloom.set(&*account_key_hash);
|
||||
}
|
||||
@ -71,8 +71,8 @@ pub fn generate_bloom(source: Arc<Database>, dest: &mut Database) -> Result<(),
|
||||
trace!(target: "migration", "Generated {} bloom updates", bloom_journal.entries.len());
|
||||
|
||||
let mut batch = DBTransaction::new(dest);
|
||||
try!(StateDB::commit_bloom(&mut batch, bloom_journal).map_err(|_| Error::Custom("Failed to commit bloom".to_owned())));
|
||||
try!(dest.write(batch));
|
||||
StateDB::commit_bloom(&mut batch, bloom_journal).map_err(|_| Error::Custom("Failed to commit bloom".to_owned()))?;
|
||||
dest.write(batch)?;
|
||||
|
||||
trace!(target: "migration", "Finished bloom update");
|
||||
|
||||
@ -104,12 +104,12 @@ impl Migration for ToV10 {
|
||||
let mut batch = Batch::new(config, col);
|
||||
for (key, value) in source.iter(col) {
|
||||
self.progress.tick();
|
||||
try!(batch.insert(key.to_vec(), value.to_vec(), dest));
|
||||
batch.insert(key.to_vec(), value.to_vec(), dest)?;
|
||||
}
|
||||
try!(batch.commit(dest));
|
||||
batch.commit(dest)?;
|
||||
|
||||
if col == COL_STATE {
|
||||
try!(generate_bloom(source, dest));
|
||||
generate_bloom(source, dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -63,17 +63,17 @@ impl Migration for ToV9 {
|
||||
self.progress.tick();
|
||||
match self.extract {
|
||||
Extract::Header => {
|
||||
try!(batch.insert(key.to_vec(), Rlp::new(&value).at(0).as_raw().to_vec(), dest))
|
||||
batch.insert(key.to_vec(), Rlp::new(&value).at(0).as_raw().to_vec(), dest)?
|
||||
},
|
||||
Extract::Body => {
|
||||
let mut body = RlpStream::new_list(2);
|
||||
let block_rlp = Rlp::new(&value);
|
||||
body.append_raw(block_rlp.at(1).as_raw(), 1);
|
||||
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
||||
try!(batch.insert(key.to_vec(), body.out(), dest))
|
||||
batch.insert(key.to_vec(), body.out(), dest)?
|
||||
},
|
||||
Extract::All => {
|
||||
try!(batch.insert(key.to_vec(), value.to_vec(), dest))
|
||||
batch.insert(key.to_vec(), value.to_vec(), dest)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ use std::time::{Instant, Duration};
|
||||
use util::*;
|
||||
use util::using_queue::{UsingQueue, GetAction};
|
||||
use account_provider::{AccountProvider, Error as AccountError};
|
||||
use views::{BlockView, HeaderView};
|
||||
use header::Header;
|
||||
use state::{State, CleanupMode};
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics, TransactionId};
|
||||
use client::TransactionImportResult;
|
||||
@ -536,7 +534,7 @@ impl Miner {
|
||||
}
|
||||
|
||||
fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
|
||||
let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit();
|
||||
let gas_limit = chain.best_block_header().gas_limit();
|
||||
let mut queue = self.transaction_queue.lock();
|
||||
queue.set_gas_limit(gas_limit);
|
||||
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
|
||||
@ -598,7 +596,7 @@ impl Miner {
|
||||
|
||||
let schedule = chain.latest_schedule();
|
||||
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
||||
let best_block_header: Header = ::rlp::decode(&chain.best_block_header());
|
||||
let best_block_header = chain.best_block_header().decode();
|
||||
transactions.into_iter()
|
||||
.map(|tx| {
|
||||
if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() {
|
||||
@ -698,10 +696,10 @@ impl MinerService for Miner {
|
||||
let mut state = block.state().clone();
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
|
||||
let sender = try!(t.sender().map_err(|e| {
|
||||
let sender = t.sender().map_err(|e| {
|
||||
let message = format!("Transaction malformed: {:?}", e);
|
||||
ExecutionError::TransactionMalformed(message)
|
||||
}));
|
||||
})?;
|
||||
let balance = state.balance(&sender);
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
@ -709,7 +707,7 @@ impl MinerService for Miner {
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options));
|
||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options)?;
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
ret.state_diff = original_state.map(|original| state.diff_from(original));
|
||||
@ -765,7 +763,7 @@ impl MinerService for Miner {
|
||||
fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> {
|
||||
if self.seals_internally {
|
||||
if let Some(ref ap) = self.accounts {
|
||||
try!(ap.sign(address.clone(), Some(password.clone()), Default::default()));
|
||||
ap.sign(address.clone(), Some(password.clone()), Default::default())?;
|
||||
}
|
||||
let mut sealing_work = self.sealing_work.lock();
|
||||
sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
|
||||
@ -1104,7 +1102,7 @@ impl MinerService for Miner {
|
||||
result.and_then(|sealed| {
|
||||
let n = sealed.header().number();
|
||||
let h = sealed.header().hash();
|
||||
try!(chain.import_sealed_block(sealed));
|
||||
chain.import_sealed_block(sealed)?;
|
||||
info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex()));
|
||||
Ok(())
|
||||
})
|
||||
@ -1127,7 +1125,6 @@ impl MinerService for Miner {
|
||||
.map(|hash| {
|
||||
let block = chain.block(BlockId::Hash(*hash))
|
||||
.expect("Client is sending message after commit to db and inserting to chain; the block is available; qed");
|
||||
let block = BlockView::new(&block);
|
||||
let txs = block.transactions();
|
||||
// populate sender
|
||||
for tx in &txs {
|
||||
|
@ -65,7 +65,7 @@ impl<F: Fn(PriceInfo) + Sync + Send + 'static> Handler<HttpStream> for SetPriceH
|
||||
impl PriceInfo {
|
||||
pub fn get<F: Fn(PriceInfo) + Sync + Send + 'static>(set_price: F) -> Result<(), ()> {
|
||||
// TODO: Handle each error type properly
|
||||
let client = try!(Client::new().map_err(|_| ()));
|
||||
let client = Client::new().map_err(|_| ())?;
|
||||
thread::spawn(move || {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let url = FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice")
|
||||
|
@ -264,7 +264,7 @@ struct VerifiedTransaction {
|
||||
|
||||
impl VerifiedTransaction {
|
||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin, min_block: Option<BlockNumber>) -> Result<Self, Error> {
|
||||
try!(transaction.sender());
|
||||
transaction.sender()?;
|
||||
Ok(VerifiedTransaction {
|
||||
transaction: transaction,
|
||||
origin: origin,
|
||||
@ -732,9 +732,9 @@ impl TransactionQueue {
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
try!(tx.check_low_s());
|
||||
tx.check_low_s()?;
|
||||
|
||||
let vtx = try!(VerifiedTransaction::new(tx, origin, min_block));
|
||||
let vtx = VerifiedTransaction::new(tx, origin, min_block)?;
|
||||
let client_account = fetch_account(&vtx.sender());
|
||||
|
||||
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
|
||||
@ -1138,13 +1138,13 @@ impl TransactionQueue {
|
||||
if nonce > next_nonce {
|
||||
// We have a gap - put to future.
|
||||
// Insert transaction (or replace old one with lower gas price)
|
||||
try!(check_too_cheap(
|
||||
check_too_cheap(
|
||||
Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.future, &mut self.by_hash, &mut self.local_transactions)
|
||||
));
|
||||
)?;
|
||||
// Enforce limit in Future
|
||||
let removed = self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions);
|
||||
// Return an error if this transaction was not imported because of limit.
|
||||
try!(check_if_removed(&address, &nonce, removed));
|
||||
check_if_removed(&address, &nonce, removed)?;
|
||||
|
||||
debug!(target: "txqueue", "Importing transaction to future: {:?}", hash);
|
||||
debug!(target: "txqueue", "status: {:?}", self.status());
|
||||
@ -1156,9 +1156,9 @@ impl TransactionQueue {
|
||||
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
|
||||
|
||||
// Replace transaction if any
|
||||
try!(check_too_cheap(
|
||||
check_too_cheap(
|
||||
Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.current, &mut self.by_hash, &mut self.local_transactions)
|
||||
));
|
||||
)?;
|
||||
// Keep track of highest nonce stored in current
|
||||
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
||||
self.last_nonces.insert(address, new_max);
|
||||
@ -1168,7 +1168,7 @@ impl TransactionQueue {
|
||||
// If some transaction were removed because of limit we need to update last_nonces also.
|
||||
self.update_last_nonces(&removed);
|
||||
// Trigger error if the transaction we are importing was removed.
|
||||
try!(check_if_removed(&address, &nonce, removed));
|
||||
check_if_removed(&address, &nonce, removed)?;
|
||||
|
||||
debug!(target: "txqueue", "Imported transaction to current: {:?}", hash);
|
||||
debug!(target: "txqueue", "status: {:?}", self.status());
|
||||
|
@ -64,7 +64,7 @@ impl From<ethjson::spec::State> for PodState {
|
||||
impl fmt::Display for PodState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for (add, acc) in &self.0 {
|
||||
try!(writeln!(f, "{} => {}", add, acc));
|
||||
writeln!(f, "{} => {}", add, acc)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ impl ClientService {
|
||||
) -> Result<ClientService, Error>
|
||||
{
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
let io_service = try!(IoService::<ClientIoMessage>::start());
|
||||
let io_service = IoService::<ClientIoMessage>::start()?;
|
||||
panic_handler.forward_from(&io_service);
|
||||
|
||||
info!("Configured for {} using {} engine", Colour::White.bold().paint(spec.name.clone()), Colour::Yellow.bold().paint(spec.engine.name()));
|
||||
@ -94,7 +94,7 @@ impl ClientService {
|
||||
db_config.wal = config.db_wal;
|
||||
|
||||
let pruning = config.pruning;
|
||||
let client = try!(Client::new(config, &spec, client_path, miner, io_service.channel(), &db_config));
|
||||
let client = Client::new(config, &spec, client_path, miner, io_service.channel(), &db_config)?;
|
||||
|
||||
let snapshot_params = SnapServiceParams {
|
||||
engine: spec.engine.clone(),
|
||||
@ -105,14 +105,14 @@ impl ClientService {
|
||||
snapshot_root: snapshot_path.into(),
|
||||
db_restore: client.clone(),
|
||||
};
|
||||
let snapshot = Arc::new(try!(SnapshotService::new(snapshot_params)));
|
||||
let snapshot = Arc::new(SnapshotService::new(snapshot_params)?);
|
||||
|
||||
panic_handler.forward_from(&*client);
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
client: client.clone(),
|
||||
snapshot: snapshot.clone(),
|
||||
});
|
||||
try!(io_service.register_handler(client_io));
|
||||
io_service.register_handler(client_io)?;
|
||||
|
||||
spec.engine.register_message_channel(io_service.channel());
|
||||
|
||||
|
@ -100,12 +100,12 @@ impl Account {
|
||||
return Ok(::rlp::NULL_RLP.to_vec());
|
||||
}
|
||||
|
||||
let db = try!(TrieDB::new(acct_db, &self.storage_root));
|
||||
let db = TrieDB::new(acct_db, &self.storage_root)?;
|
||||
|
||||
let mut pairs = Vec::new();
|
||||
|
||||
for item in try!(db.iter()) {
|
||||
let (k, v) = try!(item);
|
||||
for item in db.iter()? {
|
||||
let (k, v) = item?;
|
||||
pairs.push((k, v));
|
||||
}
|
||||
|
||||
@ -158,24 +158,24 @@ impl Account {
|
||||
return Ok((ACC_EMPTY, None));
|
||||
}
|
||||
|
||||
let nonce = try!(rlp.val_at(0));
|
||||
let balance = try!(rlp.val_at(1));
|
||||
let nonce = rlp.val_at(0)?;
|
||||
let balance = rlp.val_at(1)?;
|
||||
let code_state: CodeState = {
|
||||
let raw: u8 = try!(rlp.val_at(2));
|
||||
try!(CodeState::from(raw))
|
||||
let raw: u8 = rlp.val_at(2)?;
|
||||
CodeState::from(raw)?
|
||||
};
|
||||
|
||||
// load the code if it exists.
|
||||
let (code_hash, new_code) = match code_state {
|
||||
CodeState::Empty => (SHA3_EMPTY, None),
|
||||
CodeState::Inline => {
|
||||
let code: Bytes = try!(rlp.val_at(3));
|
||||
let code: Bytes = rlp.val_at(3)?;
|
||||
let code_hash = acct_db.insert(&code);
|
||||
|
||||
(code_hash, Some(code))
|
||||
}
|
||||
CodeState::Hash => {
|
||||
let code_hash = try!(rlp.val_at(3));
|
||||
let code_hash = rlp.val_at(3)?;
|
||||
|
||||
(code_hash, None)
|
||||
}
|
||||
@ -185,12 +185,12 @@ impl Account {
|
||||
|
||||
{
|
||||
let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root);
|
||||
let pairs = try!(rlp.at(4));
|
||||
let pairs = rlp.at(4)?;
|
||||
for pair_rlp in pairs.iter() {
|
||||
let k: Bytes = try!(pair_rlp.val_at(0));
|
||||
let v: Bytes = try!(pair_rlp.val_at(1));
|
||||
let k: Bytes = pair_rlp.val_at(0)?;
|
||||
let v: Bytes = pair_rlp.val_at(1)?;
|
||||
|
||||
try!(storage_trie.insert(&k, &v));
|
||||
storage_trie.insert(&k, &v)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,21 +89,21 @@ impl AbridgedBlock {
|
||||
|
||||
let mut header: Header = Default::default();
|
||||
header.set_parent_hash(parent_hash);
|
||||
header.set_author(try!(rlp.val_at(0)));
|
||||
header.set_state_root(try!(rlp.val_at(1)));
|
||||
header.set_log_bloom(try!(rlp.val_at(2)));
|
||||
header.set_difficulty(try!(rlp.val_at(3)));
|
||||
header.set_author(rlp.val_at(0)?);
|
||||
header.set_state_root(rlp.val_at(1)?);
|
||||
header.set_log_bloom(rlp.val_at(2)?);
|
||||
header.set_difficulty(rlp.val_at(3)?);
|
||||
header.set_number(number);
|
||||
header.set_gas_limit(try!(rlp.val_at(4)));
|
||||
header.set_gas_used(try!(rlp.val_at(5)));
|
||||
header.set_timestamp(try!(rlp.val_at(6)));
|
||||
header.set_extra_data(try!(rlp.val_at(7)));
|
||||
header.set_gas_limit(rlp.val_at(4)?);
|
||||
header.set_gas_used(rlp.val_at(5)?);
|
||||
header.set_timestamp(rlp.val_at(6)?);
|
||||
header.set_extra_data(rlp.val_at(7)?);
|
||||
|
||||
let transactions = try!(rlp.val_at(8));
|
||||
let uncles: Vec<Header> = try!(rlp.val_at(9));
|
||||
let transactions = rlp.val_at(8)?;
|
||||
let uncles: Vec<Header> = rlp.val_at(9)?;
|
||||
|
||||
header.set_transactions_root(ordered_trie_root(
|
||||
try!(rlp.at(8)).iter().map(|r| r.as_raw().to_owned())
|
||||
rlp.at(8)?.iter().map(|r| r.as_raw().to_owned())
|
||||
));
|
||||
header.set_receipts_root(receipts_root);
|
||||
|
||||
@ -113,7 +113,7 @@ impl AbridgedBlock {
|
||||
|
||||
let mut seal_fields = Vec::new();
|
||||
for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count() {
|
||||
let seal_rlp = try!(rlp.at(i));
|
||||
let seal_rlp = rlp.at(i)?;
|
||||
seal_fields.push(seal_rlp.as_raw().to_owned());
|
||||
}
|
||||
|
||||
|
@ -60,9 +60,9 @@ impl rlp::Decodable for ChunkInfo {
|
||||
fn decode<D: rlp::Decoder>(decoder: &D) -> Result<Self, rlp::DecoderError> {
|
||||
let d = decoder.as_rlp();
|
||||
|
||||
let hash = try!(d.val_at(0));
|
||||
let len = try!(d.val_at(1));
|
||||
let off = try!(d.val_at(2));
|
||||
let hash = d.val_at(0)?;
|
||||
let len = d.val_at(1)?;
|
||||
let off = d.val_at(2)?;
|
||||
Ok(ChunkInfo(hash, len, off))
|
||||
}
|
||||
}
|
||||
@ -88,7 +88,7 @@ impl PackedWriter {
|
||||
/// Create a new "PackedWriter", to write into the file at the given path.
|
||||
pub fn new(path: &Path) -> io::Result<Self> {
|
||||
Ok(PackedWriter {
|
||||
file: try!(File::create(path)),
|
||||
file: File::create(path)?,
|
||||
state_hashes: Vec::new(),
|
||||
block_hashes: Vec::new(),
|
||||
cur_len: 0,
|
||||
@ -98,7 +98,7 @@ impl PackedWriter {
|
||||
|
||||
impl SnapshotWriter for PackedWriter {
|
||||
fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> {
|
||||
try!(self.file.write_all(chunk));
|
||||
self.file.write_all(chunk)?;
|
||||
|
||||
let len = chunk.len() as u64;
|
||||
self.state_hashes.push(ChunkInfo(hash, len, self.cur_len));
|
||||
@ -108,7 +108,7 @@ impl SnapshotWriter for PackedWriter {
|
||||
}
|
||||
|
||||
fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> {
|
||||
try!(self.file.write_all(chunk));
|
||||
self.file.write_all(chunk)?;
|
||||
|
||||
let len = chunk.len() as u64;
|
||||
self.block_hashes.push(ChunkInfo(hash, len, self.cur_len));
|
||||
@ -130,7 +130,7 @@ impl SnapshotWriter for PackedWriter {
|
||||
|
||||
let manifest_rlp = stream.out();
|
||||
|
||||
try!(self.file.write_all(&manifest_rlp));
|
||||
self.file.write_all(&manifest_rlp)?;
|
||||
let off = self.cur_len;
|
||||
trace!(target: "snapshot_io", "writing manifest of len {} to offset {}", manifest_rlp.len(), off);
|
||||
|
||||
@ -146,7 +146,7 @@ impl SnapshotWriter for PackedWriter {
|
||||
(off >> 56) as u8,
|
||||
];
|
||||
|
||||
try!(self.file.write_all(&off_bytes[..]));
|
||||
self.file.write_all(&off_bytes[..])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -161,7 +161,7 @@ impl LooseWriter {
|
||||
/// Create a new LooseWriter which will write into the given directory,
|
||||
/// creating it if it doesn't exist.
|
||||
pub fn new(path: PathBuf) -> io::Result<Self> {
|
||||
try!(fs::create_dir_all(&path));
|
||||
fs::create_dir_all(&path)?;
|
||||
|
||||
Ok(LooseWriter {
|
||||
dir: path,
|
||||
@ -173,8 +173,8 @@ impl LooseWriter {
|
||||
let mut file_path = self.dir.clone();
|
||||
file_path.push(hash.hex());
|
||||
|
||||
let mut file = try!(File::create(file_path));
|
||||
try!(file.write_all(chunk));
|
||||
let mut file = File::create(file_path)?;
|
||||
file.write_all(chunk)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -194,8 +194,8 @@ impl SnapshotWriter for LooseWriter {
|
||||
let mut path = self.dir.clone();
|
||||
path.push("MANIFEST");
|
||||
|
||||
let mut file = try!(File::create(path));
|
||||
try!(file.write_all(&rlp[..]));
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(&rlp[..])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -224,18 +224,18 @@ impl PackedReader {
|
||||
/// This will fail if any io errors are encountered or the file
|
||||
/// is not a valid packed snapshot.
|
||||
pub fn new(path: &Path) -> Result<Option<Self>, ::error::Error> {
|
||||
let mut file = try!(File::open(path));
|
||||
let file_len = try!(file.metadata()).len();
|
||||
let mut file = File::open(path)?;
|
||||
let file_len = file.metadata()?.len();
|
||||
if file_len < 8 {
|
||||
// ensure we don't seek before beginning.
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
||||
try!(file.seek(SeekFrom::End(-8)));
|
||||
file.seek(SeekFrom::End(-8))?;
|
||||
let mut off_bytes = [0u8; 8];
|
||||
|
||||
try!(file.read_exact(&mut off_bytes[..]));
|
||||
file.read_exact(&mut off_bytes[..])?;
|
||||
|
||||
let manifest_off: u64 =
|
||||
((off_bytes[7] as u64) << 56) +
|
||||
@ -252,20 +252,20 @@ impl PackedReader {
|
||||
|
||||
let mut manifest_buf = vec![0; manifest_len as usize];
|
||||
|
||||
try!(file.seek(SeekFrom::Start(manifest_off)));
|
||||
try!(file.read_exact(&mut manifest_buf));
|
||||
file.seek(SeekFrom::Start(manifest_off))?;
|
||||
file.read_exact(&mut manifest_buf)?;
|
||||
|
||||
let rlp = UntrustedRlp::new(&manifest_buf);
|
||||
|
||||
let state: Vec<ChunkInfo> = try!(rlp.val_at(0));
|
||||
let blocks: Vec<ChunkInfo> = try!(rlp.val_at(1));
|
||||
let state: Vec<ChunkInfo> = rlp.val_at(0)?;
|
||||
let blocks: Vec<ChunkInfo> = rlp.val_at(1)?;
|
||||
|
||||
let manifest = ManifestData {
|
||||
state_hashes: state.iter().map(|c| c.0).collect(),
|
||||
block_hashes: blocks.iter().map(|c| c.0).collect(),
|
||||
state_root: try!(rlp.val_at(2)),
|
||||
block_number: try!(rlp.val_at(3)),
|
||||
block_hash: try!(rlp.val_at(4)),
|
||||
state_root: rlp.val_at(2)?,
|
||||
block_number: rlp.val_at(3)?,
|
||||
block_hash: rlp.val_at(4)?,
|
||||
};
|
||||
|
||||
Ok(Some(PackedReader {
|
||||
@ -288,10 +288,10 @@ impl SnapshotReader for PackedReader {
|
||||
|
||||
let mut file = &self.file;
|
||||
|
||||
try!(file.seek(SeekFrom::Start(off)));
|
||||
file.seek(SeekFrom::Start(off))?;
|
||||
let mut buf = vec![0; len as usize];
|
||||
|
||||
try!(file.read_exact(&mut buf[..]));
|
||||
file.read_exact(&mut buf[..])?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
@ -310,10 +310,10 @@ impl LooseReader {
|
||||
let mut manifest_buf = Vec::new();
|
||||
|
||||
dir.push("MANIFEST");
|
||||
let mut manifest_file = try!(File::open(&dir));
|
||||
try!(manifest_file.read_to_end(&mut manifest_buf));
|
||||
let mut manifest_file = File::open(&dir)?;
|
||||
manifest_file.read_to_end(&mut manifest_buf)?;
|
||||
|
||||
let manifest = try!(ManifestData::from_rlp(&manifest_buf[..]));
|
||||
let manifest = ManifestData::from_rlp(&manifest_buf[..])?;
|
||||
|
||||
dir.pop();
|
||||
|
||||
@ -334,9 +334,9 @@ impl SnapshotReader for LooseReader {
|
||||
path.push(hash.hex());
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut file = try!(File::open(&path));
|
||||
let mut file = File::open(&path)?;
|
||||
|
||||
try!(file.read_to_end(&mut buf));
|
||||
file.read_to_end(&mut buf)?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
@ -128,22 +128,22 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
|
||||
writer: W,
|
||||
p: &Progress
|
||||
) -> Result<(), Error> {
|
||||
let start_header = try!(chain.block_header(&block_at)
|
||||
.ok_or(Error::InvalidStartingBlock(BlockId::Hash(block_at))));
|
||||
let start_header = chain.block_header(&block_at)
|
||||
.ok_or(Error::InvalidStartingBlock(BlockId::Hash(block_at)))?;
|
||||
let state_root = start_header.state_root();
|
||||
let number = start_header.number();
|
||||
|
||||
info!("Taking snapshot starting at block {}", number);
|
||||
|
||||
let writer = Mutex::new(writer);
|
||||
let (state_hashes, block_hashes) = try!(scope(|scope| {
|
||||
let (state_hashes, block_hashes) = scope(|scope| {
|
||||
let block_guard = scope.spawn(|| chunk_blocks(chain, block_at, &writer, p));
|
||||
let state_res = chunk_state(state_db, state_root, &writer, p);
|
||||
|
||||
state_res.and_then(|state_hashes| {
|
||||
block_guard.join().map(|block_hashes| (state_hashes, block_hashes))
|
||||
})
|
||||
}));
|
||||
})?;
|
||||
|
||||
info!("produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
|
||||
|
||||
@ -155,7 +155,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
|
||||
block_hash: block_at,
|
||||
};
|
||||
|
||||
try!(writer.into_inner().finish(manifest_data));
|
||||
writer.into_inner().finish(manifest_data)?;
|
||||
|
||||
p.done.store(true, Ordering::SeqCst);
|
||||
|
||||
@ -186,12 +186,11 @@ impl<'a> BlockChunker<'a> {
|
||||
for _ in 0..SNAPSHOT_BLOCKS {
|
||||
if self.current_hash == genesis_hash { break }
|
||||
|
||||
let (block, receipts) = try!(self.chain.block(&self.current_hash)
|
||||
let (block, receipts) = self.chain.block(&self.current_hash)
|
||||
.and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r)))
|
||||
.ok_or(Error::BlockNotFound(self.current_hash)));
|
||||
.ok_or(Error::BlockNotFound(self.current_hash))?;
|
||||
|
||||
let view = BlockView::new(&block);
|
||||
let abridged_rlp = AbridgedBlock::from_block_view(&view).into_inner();
|
||||
let abridged_rlp = AbridgedBlock::from_block_view(&block.view()).into_inner();
|
||||
|
||||
let pair = {
|
||||
let mut pair_stream = RlpStream::new_list(2);
|
||||
@ -204,7 +203,7 @@ impl<'a> BlockChunker<'a> {
|
||||
// cut off the chunk if too large.
|
||||
|
||||
if new_loaded_size > PREFERRED_CHUNK_SIZE && !self.rlps.is_empty() {
|
||||
try!(self.write_chunk(last));
|
||||
self.write_chunk(last)?;
|
||||
loaded_size = pair.len();
|
||||
} else {
|
||||
loaded_size = new_loaded_size;
|
||||
@ -213,11 +212,11 @@ impl<'a> BlockChunker<'a> {
|
||||
self.rlps.push_front(pair);
|
||||
|
||||
last = self.current_hash;
|
||||
self.current_hash = view.header_view().parent_hash();
|
||||
self.current_hash = block.header_view().parent_hash();
|
||||
}
|
||||
|
||||
if loaded_size != 0 {
|
||||
try!(self.write_chunk(last));
|
||||
self.write_chunk(last)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -230,9 +229,9 @@ impl<'a> BlockChunker<'a> {
|
||||
fn write_chunk(&mut self, last: H256) -> Result<(), Error> {
|
||||
trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len());
|
||||
|
||||
let (last_header, last_details) = try!(self.chain.block_header(&last)
|
||||
let (last_header, last_details) = self.chain.block_header(&last)
|
||||
.and_then(|n| self.chain.block_details(&last).map(|d| (n, d)))
|
||||
.ok_or(Error::BlockNotFound(last)));
|
||||
.ok_or(Error::BlockNotFound(last))?;
|
||||
|
||||
let parent_number = last_header.number() - 1;
|
||||
let parent_hash = last_header.parent_hash();
|
||||
@ -254,7 +253,7 @@ impl<'a> BlockChunker<'a> {
|
||||
let compressed = &self.snappy_buffer[..size];
|
||||
let hash = compressed.sha3();
|
||||
|
||||
try!(self.writer.lock().write_block_chunk(hash, compressed));
|
||||
self.writer.lock().write_block_chunk(hash, compressed)?;
|
||||
trace!(target: "snapshot", "wrote block chunk. hash: {}, size: {}, uncompressed size: {}", hash.hex(), size, raw_data.len());
|
||||
|
||||
self.progress.size.fetch_add(size, Ordering::SeqCst);
|
||||
@ -282,7 +281,7 @@ pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_hash: H256, writer: &Mutex<
|
||||
progress: progress,
|
||||
};
|
||||
|
||||
try!(chunker.chunk_all());
|
||||
chunker.chunk_all()?;
|
||||
|
||||
Ok(chunker.hashes)
|
||||
}
|
||||
@ -310,7 +309,7 @@ impl<'a> StateChunker<'a> {
|
||||
};
|
||||
|
||||
if self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
|
||||
try!(self.write_chunk());
|
||||
self.write_chunk()?;
|
||||
}
|
||||
|
||||
self.cur_size += pair.len();
|
||||
@ -334,7 +333,7 @@ impl<'a> StateChunker<'a> {
|
||||
let compressed = &self.snappy_buffer[..compressed_size];
|
||||
let hash = compressed.sha3();
|
||||
|
||||
try!(self.writer.lock().write_state_chunk(hash, compressed));
|
||||
self.writer.lock().write_state_chunk(hash, compressed)?;
|
||||
trace!(target: "snapshot", "wrote state chunk. size: {}, uncompressed size: {}", compressed_size, raw_data.len());
|
||||
|
||||
self.progress.accounts.fetch_add(num_entries, Ordering::SeqCst);
|
||||
@ -353,7 +352,7 @@ impl<'a> StateChunker<'a> {
|
||||
/// Returns a list of hashes of chunks created, or any error it may
|
||||
/// have encountered.
|
||||
pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
|
||||
let account_trie = try!(TrieDB::new(db, &root));
|
||||
let account_trie = TrieDB::new(db, &root)?;
|
||||
|
||||
let mut chunker = StateChunker {
|
||||
hashes: Vec::new(),
|
||||
@ -367,19 +366,19 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
// account_key here is the address' hash.
|
||||
for item in try!(account_trie.iter()) {
|
||||
let (account_key, account_data) = try!(item);
|
||||
for item in account_trie.iter()? {
|
||||
let (account_key, account_data) = item?;
|
||||
let account = Account::from_thin_rlp(&*account_data);
|
||||
let account_key_hash = H256::from_slice(&account_key);
|
||||
|
||||
let account_db = AccountDB::from_hash(db, account_key_hash);
|
||||
|
||||
let fat_rlp = try!(account.to_fat_rlp(&account_db, &mut used_code));
|
||||
try!(chunker.push(account_key, fat_rlp));
|
||||
let fat_rlp = account.to_fat_rlp(&account_db, &mut used_code)?;
|
||||
chunker.push(account_key, fat_rlp)?;
|
||||
}
|
||||
|
||||
if chunker.cur_size != 0 {
|
||||
try!(chunker.write_chunk());
|
||||
chunker.write_chunk()?;
|
||||
}
|
||||
|
||||
Ok(chunker.hashes)
|
||||
@ -415,13 +414,13 @@ impl StateRebuilder {
|
||||
// initialize the pairs vector with empty values so we have slots to write into.
|
||||
pairs.resize(rlp.item_count(), (H256::new(), Vec::new()));
|
||||
|
||||
let status = try!(rebuild_accounts(
|
||||
let status = rebuild_accounts(
|
||||
self.db.as_hashdb_mut(),
|
||||
rlp,
|
||||
&mut pairs,
|
||||
&self.known_code,
|
||||
flag
|
||||
));
|
||||
)?;
|
||||
|
||||
for (addr_hash, code_hash) in status.missing_code {
|
||||
self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
|
||||
@ -442,7 +441,7 @@ impl StateRebuilder {
|
||||
// batch trie writes
|
||||
{
|
||||
let mut account_trie = if self.state_root != SHA3_NULL_RLP {
|
||||
try!(TrieDBMut::from_existing(self.db.as_hashdb_mut(), &mut self.state_root))
|
||||
TrieDBMut::from_existing(self.db.as_hashdb_mut(), &mut self.state_root)?
|
||||
} else {
|
||||
TrieDBMut::new(self.db.as_hashdb_mut(), &mut self.state_root)
|
||||
};
|
||||
@ -453,14 +452,14 @@ impl StateRebuilder {
|
||||
if &thin_rlp[..] != &empty_rlp[..] {
|
||||
self.bloom.set(&*hash);
|
||||
}
|
||||
try!(account_trie.insert(&hash, &thin_rlp));
|
||||
account_trie.insert(&hash, &thin_rlp)?;
|
||||
}
|
||||
}
|
||||
|
||||
let bloom_journal = self.bloom.drain_journal();
|
||||
let mut batch = backing.transaction();
|
||||
try!(StateDB::commit_bloom(&mut batch, bloom_journal));
|
||||
try!(self.db.inject(&mut batch));
|
||||
StateDB::commit_bloom(&mut batch, bloom_journal)?;
|
||||
self.db.inject(&mut batch)?;
|
||||
backing.write_buffered(batch);
|
||||
trace!(target: "snapshot", "current state root: {:?}", self.state_root);
|
||||
Ok(())
|
||||
@ -500,15 +499,15 @@ fn rebuild_accounts(
|
||||
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk) {
|
||||
if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
|
||||
|
||||
let hash: H256 = try!(account_rlp.val_at(0));
|
||||
let fat_rlp = try!(account_rlp.at(1));
|
||||
let hash: H256 = account_rlp.val_at(0)?;
|
||||
let fat_rlp = account_rlp.at(1)?;
|
||||
|
||||
let thin_rlp = {
|
||||
|
||||
// fill out the storage trie and code while decoding.
|
||||
let (acc, maybe_code) = {
|
||||
let mut acct_db = AccountDBMut::from_hash(db, hash);
|
||||
try!(Account::from_fat_rlp(&mut acct_db, fat_rlp))
|
||||
Account::from_fat_rlp(&mut acct_db, fat_rlp)?
|
||||
};
|
||||
|
||||
let code_hash = acc.code_hash().clone();
|
||||
@ -521,9 +520,9 @@ fn rebuild_accounts(
|
||||
match known_code.get(&code_hash) {
|
||||
Some(&first_with) => {
|
||||
// if so, load it from the database.
|
||||
let code = try!(AccountDB::from_hash(db, first_with)
|
||||
let code = AccountDB::from_hash(db, first_with)
|
||||
.get(&code_hash)
|
||||
.ok_or_else(|| Error::MissingCode(vec![first_with])));
|
||||
.ok_or_else(|| Error::MissingCode(vec![first_with]))?;
|
||||
|
||||
// and write it again under a different mangled key
|
||||
AccountDBMut::from_hash(db, hash).emplace(code_hash, code);
|
||||
@ -586,7 +585,7 @@ impl BlockRebuilder {
|
||||
Ok(BlockRebuilder {
|
||||
chain: chain,
|
||||
db: db,
|
||||
rng: try!(OsRng::new()),
|
||||
rng: OsRng::new()?,
|
||||
disconnected: Vec::new(),
|
||||
best_number: manifest.block_number,
|
||||
best_hash: manifest.block_hash,
|
||||
@ -613,22 +612,22 @@ impl BlockRebuilder {
|
||||
}
|
||||
|
||||
// todo: assert here that these values are consistent with chunks being in order.
|
||||
let mut cur_number = try!(rlp.val_at::<u64>(0)) + 1;
|
||||
let mut parent_hash = try!(rlp.val_at::<H256>(1));
|
||||
let parent_total_difficulty = try!(rlp.val_at::<U256>(2));
|
||||
let mut cur_number = rlp.val_at::<u64>(0)? + 1;
|
||||
let mut parent_hash = rlp.val_at::<H256>(1)?;
|
||||
let parent_total_difficulty = rlp.val_at::<U256>(2)?;
|
||||
|
||||
for idx in 3..item_count {
|
||||
if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
|
||||
|
||||
let pair = try!(rlp.at(idx));
|
||||
let abridged_rlp = try!(pair.at(0)).as_raw().to_owned();
|
||||
let pair = rlp.at(idx)?;
|
||||
let abridged_rlp = pair.at(0)?.as_raw().to_owned();
|
||||
let abridged_block = AbridgedBlock::from_raw(abridged_rlp);
|
||||
let receipts: Vec<::receipt::Receipt> = try!(pair.val_at(1));
|
||||
let receipts: Vec<::receipt::Receipt> = pair.val_at(1)?;
|
||||
let receipts_root = ordered_trie_root(
|
||||
try!(pair.at(1)).iter().map(|r| r.as_raw().to_owned())
|
||||
pair.at(1)?.iter().map(|r| r.as_raw().to_owned())
|
||||
);
|
||||
|
||||
let block = try!(abridged_block.to_block(parent_hash, cur_number, receipts_root));
|
||||
let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?;
|
||||
let block_bytes = block.rlp_bytes(With);
|
||||
let is_best = cur_number == self.best_number;
|
||||
|
||||
@ -642,14 +641,14 @@ impl BlockRebuilder {
|
||||
}
|
||||
}
|
||||
|
||||
try!(verify_old_block(
|
||||
verify_old_block(
|
||||
&mut self.rng,
|
||||
&block.header,
|
||||
engine,
|
||||
&self.chain,
|
||||
Some(&block_bytes),
|
||||
is_best
|
||||
));
|
||||
)?;
|
||||
|
||||
let mut batch = self.db.transaction();
|
||||
|
||||
@ -693,7 +692,7 @@ impl BlockRebuilder {
|
||||
let best_number = self.best_number;
|
||||
for num in (0..self.fed_blocks).map(|x| best_number - x) {
|
||||
|
||||
let hash = try!(self.chain.block_hash(num).ok_or(Error::IncompleteChain));
|
||||
let hash = self.chain.block_hash(num).ok_or(Error::IncompleteChain)?;
|
||||
|
||||
if let Some(canon_hash) = canonical.get(&num).cloned() {
|
||||
if canon_hash != hash {
|
||||
|
@ -97,11 +97,11 @@ impl Restoration {
|
||||
let state_chunks = manifest.state_hashes.iter().cloned().collect();
|
||||
let block_chunks = manifest.block_hashes.iter().cloned().collect();
|
||||
|
||||
let raw_db = Arc::new(try!(Database::open(params.db_config, &*params.db_path.to_string_lossy())
|
||||
.map_err(UtilError::SimpleString)));
|
||||
let raw_db = Arc::new(Database::open(params.db_config, &*params.db_path.to_string_lossy())
|
||||
.map_err(UtilError::SimpleString)?);
|
||||
|
||||
let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone(), params.engine);
|
||||
let blocks = try!(BlockRebuilder::new(chain, raw_db.clone(), &manifest));
|
||||
let blocks = BlockRebuilder::new(chain, raw_db.clone(), &manifest)?;
|
||||
|
||||
let root = manifest.state_root.clone();
|
||||
Ok(Restoration {
|
||||
@ -122,12 +122,12 @@ impl Restoration {
|
||||
// feeds a state chunk, aborts early if `flag` becomes false.
|
||||
fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> {
|
||||
if self.state_chunks_left.remove(&hash) {
|
||||
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
|
||||
let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?;
|
||||
|
||||
try!(self.state.feed(&self.snappy_buffer[..len], flag));
|
||||
self.state.feed(&self.snappy_buffer[..len], flag)?;
|
||||
|
||||
if let Some(ref mut writer) = self.writer.as_mut() {
|
||||
try!(writer.write_state_chunk(hash, chunk));
|
||||
writer.write_state_chunk(hash, chunk)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,11 +137,11 @@ impl Restoration {
|
||||
// feeds a block chunk
|
||||
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine, flag: &AtomicBool) -> Result<(), Error> {
|
||||
if self.block_chunks_left.remove(&hash) {
|
||||
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
|
||||
let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?;
|
||||
|
||||
try!(self.blocks.feed(&self.snappy_buffer[..len], engine, flag));
|
||||
self.blocks.feed(&self.snappy_buffer[..len], engine, flag)?;
|
||||
if let Some(ref mut writer) = self.writer.as_mut() {
|
||||
try!(writer.write_block_chunk(hash, chunk));
|
||||
writer.write_block_chunk(hash, chunk)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,13 +167,13 @@ impl Restoration {
|
||||
}
|
||||
|
||||
// check for missing code.
|
||||
try!(self.state.check_missing());
|
||||
self.state.check_missing()?;
|
||||
|
||||
// connect out-of-order chunks and verify chain integrity.
|
||||
try!(self.blocks.finalize(self.canonical_hashes));
|
||||
self.blocks.finalize(self.canonical_hashes)?;
|
||||
|
||||
if let Some(writer) = self.writer {
|
||||
try!(writer.finish(self.manifest));
|
||||
writer.finish(self.manifest)?;
|
||||
}
|
||||
|
||||
self.guard.disarm();
|
||||
@ -315,7 +315,7 @@ impl Service {
|
||||
fn replace_client_db(&self) -> Result<(), Error> {
|
||||
let our_db = self.restoration_db();
|
||||
|
||||
try!(self.db_restore.restore_db(&*our_db.to_string_lossy()));
|
||||
self.db_restore.restore_db(&*our_db.to_string_lossy())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -351,7 +351,7 @@ impl Service {
|
||||
|
||||
let _ = fs::remove_dir_all(&temp_dir);
|
||||
|
||||
let writer = try!(LooseWriter::new(temp_dir.clone()));
|
||||
let writer = LooseWriter::new(temp_dir.clone())?;
|
||||
|
||||
let guard = Guard::new(temp_dir.clone());
|
||||
let res = client.take_snapshot(writer, BlockId::Number(num), &self.progress);
|
||||
@ -378,12 +378,12 @@ impl Service {
|
||||
*reader = None;
|
||||
|
||||
if snapshot_dir.exists() {
|
||||
try!(fs::remove_dir_all(&snapshot_dir));
|
||||
fs::remove_dir_all(&snapshot_dir)?;
|
||||
}
|
||||
|
||||
try!(fs::rename(temp_dir, &snapshot_dir));
|
||||
fs::rename(temp_dir, &snapshot_dir)?;
|
||||
|
||||
*reader = Some(try!(LooseReader::new(snapshot_dir)));
|
||||
*reader = Some(LooseReader::new(snapshot_dir)?);
|
||||
|
||||
guard.disarm();
|
||||
Ok(())
|
||||
@ -410,11 +410,11 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
try!(fs::create_dir_all(&rest_dir));
|
||||
fs::create_dir_all(&rest_dir)?;
|
||||
|
||||
// make new restoration.
|
||||
let writer = match recover {
|
||||
true => Some(try!(LooseWriter::new(self.temp_recovery_dir()))),
|
||||
true => Some(LooseWriter::new(self.temp_recovery_dir())?),
|
||||
false => None
|
||||
};
|
||||
|
||||
@ -432,7 +432,7 @@ impl Service {
|
||||
let state_chunks = params.manifest.state_hashes.len();
|
||||
let block_chunks = params.manifest.block_hashes.len();
|
||||
|
||||
*res = Some(try!(Restoration::new(params)));
|
||||
*res = Some(Restoration::new(params)?);
|
||||
|
||||
*self.status.lock() = RestorationStatus::Ongoing {
|
||||
state_chunks: state_chunks as u32,
|
||||
@ -454,8 +454,8 @@ impl Service {
|
||||
let recover = rest.as_ref().map_or(false, |rest| rest.writer.is_some());
|
||||
|
||||
// destroy the restoration before replacing databases and snapshot.
|
||||
try!(rest.take().map(Restoration::finalize).unwrap_or(Ok(())));
|
||||
try!(self.replace_client_db());
|
||||
rest.take().map(Restoration::finalize).unwrap_or(Ok(()))?;
|
||||
self.replace_client_db()?;
|
||||
|
||||
if recover {
|
||||
let mut reader = self.reader.write();
|
||||
@ -465,13 +465,13 @@ impl Service {
|
||||
|
||||
if snapshot_dir.exists() {
|
||||
trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy());
|
||||
try!(fs::remove_dir_all(&snapshot_dir));
|
||||
fs::remove_dir_all(&snapshot_dir)?;
|
||||
}
|
||||
|
||||
trace!(target: "snapshot", "copying restored snapshot files over");
|
||||
try!(fs::rename(self.temp_recovery_dir(), &snapshot_dir));
|
||||
fs::rename(self.temp_recovery_dir(), &snapshot_dir)?;
|
||||
|
||||
*reader = Some(try!(LooseReader::new(snapshot_dir)));
|
||||
*reader = Some(LooseReader::new(snapshot_dir)?);
|
||||
}
|
||||
|
||||
let _ = fs::remove_dir_all(self.restoration_dir());
|
||||
@ -510,7 +510,7 @@ impl Service {
|
||||
|
||||
match is_done {
|
||||
true => {
|
||||
try!(db.flush().map_err(::util::UtilError::SimpleString));
|
||||
db.flush().map_err(::util::UtilError::SimpleString)?;
|
||||
drop(db);
|
||||
return self.finalize_restoration(&mut *restoration);
|
||||
},
|
||||
|
@ -20,7 +20,6 @@ use util::Mutex;
|
||||
use client::{BlockChainClient, Client, ChainNotify};
|
||||
use ids::BlockId;
|
||||
use service::ClientIoMessage;
|
||||
use views::HeaderView;
|
||||
|
||||
use io::IoChannel;
|
||||
use util::{H256, Bytes};
|
||||
@ -43,7 +42,7 @@ impl<F> Oracle for StandardOracle<F>
|
||||
where F: Send + Sync + Fn() -> bool
|
||||
{
|
||||
fn to_number(&self, hash: H256) -> Option<u64> {
|
||||
self.client.block_header(BlockId::Hash(hash)).map(|h| HeaderView::new(&h).number())
|
||||
self.client.block_header(BlockId::Hash(hash)).map(|h| h.number())
|
||||
}
|
||||
|
||||
fn is_major_importing(&self) -> bool {
|
||||
|
@ -19,6 +19,13 @@
|
||||
use util::*;
|
||||
use builtin::Builtin;
|
||||
use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound, Tendermint};
|
||||
use factory::Factories;
|
||||
use executive::Executive;
|
||||
use trace::{NoopTracer, NoopVMTracer};
|
||||
use action_params::{ActionValue, ActionParams};
|
||||
use types::executed::CallType;
|
||||
use state::{State, Substate};
|
||||
use env_info::EnvInfo;
|
||||
use pod_state::*;
|
||||
use account_db::*;
|
||||
use header::{BlockNumber, Header};
|
||||
@ -99,10 +106,13 @@ pub struct Spec {
|
||||
/// Each seal field, expressed as RLP, concatenated.
|
||||
pub seal_rlp: Bytes,
|
||||
|
||||
// May be prepopulated if we know this in advance.
|
||||
/// Contract constructors to be executed on genesis.
|
||||
constructors: Vec<(Address, Bytes)>,
|
||||
|
||||
/// May be prepopulated if we know this in advance.
|
||||
state_root_memo: RwLock<Option<H256>>,
|
||||
|
||||
// Genesis state as plain old data.
|
||||
/// Genesis state as plain old data.
|
||||
genesis_state: PodState,
|
||||
}
|
||||
|
||||
@ -128,6 +138,7 @@ impl From<ethjson::spec::Spec> for Spec {
|
||||
timestamp: g.timestamp,
|
||||
extra_data: g.extra_data,
|
||||
seal_rlp: seal_rlp,
|
||||
constructors: s.accounts.constructors().into_iter().map(|(a, c)| (a.into(), c.into())).collect(),
|
||||
state_root_memo: RwLock::new(g.state_root),
|
||||
genesis_state: From::from(s.accounts),
|
||||
}
|
||||
@ -238,25 +249,71 @@ impl Spec {
|
||||
}
|
||||
|
||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||
pub fn ensure_db_good(&self, db: &mut StateDB, factory: &TrieFactory) -> Result<bool, Box<TrieError>> {
|
||||
if !db.as_hashdb().contains(&self.state_root()) {
|
||||
trace!(target: "spec", "ensure_db_good: Fresh database? Cannot find state root {}", self.state_root());
|
||||
let mut root = H256::new();
|
||||
pub fn ensure_db_good(&self, mut db: StateDB, factories: &Factories) -> Result<StateDB, Box<TrieError>> {
|
||||
if db.as_hashdb().contains(&self.state_root()) {
|
||||
return Ok(db)
|
||||
}
|
||||
trace!(target: "spec", "ensure_db_good: Fresh database? Cannot find state root {}", self.state_root());
|
||||
let mut root = H256::new();
|
||||
|
||||
{
|
||||
let mut t = factories.trie.create(db.as_hashdb_mut(), &mut root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
t.insert(&**address, &account.rlp())?;
|
||||
}
|
||||
}
|
||||
|
||||
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
db.note_non_null_account(address);
|
||||
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address), &factories.trie);
|
||||
}
|
||||
|
||||
// Execute contract constructors.
|
||||
let env_info = EnvInfo {
|
||||
number: 0,
|
||||
author: self.author,
|
||||
timestamp: self.timestamp,
|
||||
difficulty: self.difficulty,
|
||||
last_hashes: Default::default(),
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: U256::max_value(),
|
||||
};
|
||||
let from = Address::default();
|
||||
let start_nonce = self.engine.account_start_nonce();
|
||||
|
||||
let mut state = State::from_existing(db, root, start_nonce, factories.clone())?;
|
||||
// Mutate the state with each constructor.
|
||||
for &(ref address, ref constructor) in self.constructors.iter() {
|
||||
trace!(target: "spec", "ensure_db_good: Creating a contract at {}.", address);
|
||||
let params = ActionParams {
|
||||
code_address: address.clone(),
|
||||
code_hash: constructor.sha3(),
|
||||
address: address.clone(),
|
||||
sender: from.clone(),
|
||||
origin: from.clone(),
|
||||
gas: U256::max_value(),
|
||||
gas_price: Default::default(),
|
||||
value: ActionValue::Transfer(Default::default()),
|
||||
code: Some(Arc::new(constructor.clone())),
|
||||
data: None,
|
||||
call_type: CallType::None,
|
||||
};
|
||||
let mut substate = Substate::new();
|
||||
{
|
||||
let mut t = factory.create(db.as_hashdb_mut(), &mut root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
try!(t.insert(&**address, &account.rlp()));
|
||||
let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref(), &factories.vm);
|
||||
if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) {
|
||||
warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
|
||||
}
|
||||
}
|
||||
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
db.note_non_null_account(address);
|
||||
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address), factory);
|
||||
if let Err(e) = state.commit() {
|
||||
warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e);
|
||||
}
|
||||
assert!(db.as_hashdb().contains(&self.state_root()));
|
||||
Ok(true)
|
||||
} else { Ok(false) }
|
||||
}
|
||||
let (root, db) = state.drop();
|
||||
|
||||
*self.state_root_memo.write() = Some(root);
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
/// Loads spec from json file.
|
||||
@ -273,6 +330,9 @@ impl Spec {
|
||||
/// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is sha3('').
|
||||
pub fn new_null() -> Spec { load_bundled!("null") }
|
||||
|
||||
/// Create a new Spec which constructs a contract at address 5 with storage at 0 equal to 1.
|
||||
pub fn new_test_constructor() -> Spec { load_bundled!("constructor") }
|
||||
|
||||
/// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring work).
|
||||
pub fn new_instant() -> Spec { load_bundled!("instant_seal") }
|
||||
|
||||
@ -287,10 +347,10 @@ impl Spec {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use util::hash::*;
|
||||
use util::sha3::*;
|
||||
use util::*;
|
||||
use views::*;
|
||||
use tests::helpers::get_temp_state_db;
|
||||
use state::State;
|
||||
use super::*;
|
||||
|
||||
// https://github.com/ethcore/parity/issues/1840
|
||||
@ -307,4 +367,14 @@ mod tests {
|
||||
let genesis = test_spec.genesis_block();
|
||||
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_constructor() {
|
||||
let spec = Spec::new_test_constructor();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let state = State::from_existing(db.boxed_clone(), spec.state_root(), spec.engine.account_start_nonce(), Default::default()).unwrap();
|
||||
let expected = H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap();
|
||||
assert_eq!(state.storage_at(&Address::from_str("0000000000000000000000000000000000000005").unwrap(), &H256::zero()), expected);
|
||||
}
|
||||
}
|
||||
|
@ -450,8 +450,8 @@ impl Account {
|
||||
|
||||
let mut recorder = TrieRecorder::with_depth(from_level);
|
||||
|
||||
let trie = try!(TrieDB::new(db, &self.storage_root));
|
||||
let _ = try!(trie.get_recorded(&storage_key, &mut recorder));
|
||||
let trie = TrieDB::new(db, &self.storage_root)?;
|
||||
let _ = trie.get_recorded(&storage_key, &mut recorder)?;
|
||||
|
||||
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
|
||||
}
|
||||
|
@ -515,11 +515,11 @@ impl State {
|
||||
|
||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
||||
let vm_factory = self.factories.vm.clone();
|
||||
let e = try!(Executive::new(self, env_info, engine, &vm_factory).transact(t, options));
|
||||
let e = Executive::new(self, env_info, engine, &vm_factory).transact(t, options)?;
|
||||
|
||||
// TODO uncomment once to_pod() works correctly.
|
||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||
try!(self.commit());
|
||||
self.commit()?;
|
||||
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
||||
trace!(target: "state", "Transaction receipt: {:?}", receipt);
|
||||
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
||||
@ -551,15 +551,15 @@ impl State {
|
||||
}
|
||||
|
||||
{
|
||||
let mut trie = try!(factories.trie.from_existing(db.as_hashdb_mut(), root));
|
||||
let mut trie = factories.trie.from_existing(db.as_hashdb_mut(), root)?;
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
a.state = AccountState::Committed;
|
||||
match a.account {
|
||||
Some(ref mut account) => {
|
||||
try!(trie.insert(address, &account.rlp()));
|
||||
trie.insert(address, &account.rlp())?;
|
||||
},
|
||||
None => {
|
||||
try!(trie.remove(address));
|
||||
trie.remove(address)?;
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -771,8 +771,8 @@ impl State {
|
||||
/// `account_key` == sha3(address)
|
||||
pub fn prove_account(&self, account_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
|
||||
let mut recorder = TrieRecorder::with_depth(from_level);
|
||||
let trie = try!(TrieDB::new(self.db.as_hashdb(), &self.root));
|
||||
let _ = try!(trie.get_recorded(&account_key, &mut recorder));
|
||||
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
|
||||
let _ = trie.get_recorded(&account_key, &mut recorder)?;
|
||||
|
||||
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
|
||||
}
|
||||
@ -785,8 +785,8 @@ impl State {
|
||||
pub fn prove_storage(&self, account_key: H256, storage_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
|
||||
// TODO: probably could look into cache somehow but it's keyed by
|
||||
// address, not sha3(address).
|
||||
let trie = try!(TrieDB::new(self.db.as_hashdb(), &self.root));
|
||||
let acc = match try!(trie.get(&account_key)) {
|
||||
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
|
||||
let acc = match trie.get(&account_key)? {
|
||||
Some(rlp) => Account::from_rlp(&rlp),
|
||||
None => return Ok(Vec::new()),
|
||||
};
|
||||
@ -798,8 +798,8 @@ impl State {
|
||||
/// Get code by address hash.
|
||||
/// Only works when backed by a secure trie.
|
||||
pub fn code_by_address_hash(&self, account_key: H256) -> Result<Option<Bytes>, Box<TrieError>> {
|
||||
let trie = try!(TrieDB::new(self.db.as_hashdb(), &self.root));
|
||||
let mut acc = match try!(trie.get(&account_key)) {
|
||||
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
|
||||
let mut acc = match trie.get(&account_key)? {
|
||||
Some(rlp) => Account::from_rlp(&rlp),
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
@ -195,9 +195,9 @@ impl StateDB {
|
||||
pub fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> Result<u32, UtilError> {
|
||||
{
|
||||
let mut bloom_lock = self.account_bloom.lock();
|
||||
try!(Self::commit_bloom(batch, bloom_lock.drain_journal()));
|
||||
Self::commit_bloom(batch, bloom_lock.drain_journal())?;
|
||||
}
|
||||
let records = try!(self.db.journal_under(batch, now, id));
|
||||
let records = self.db.journal_under(batch, now, id)?;
|
||||
self.commit_hash = Some(id.clone());
|
||||
self.commit_number = Some(now);
|
||||
Ok(records)
|
||||
|
@ -24,7 +24,7 @@ use types::filter::Filter;
|
||||
use util::*;
|
||||
use devtools::*;
|
||||
use miner::Miner;
|
||||
use rlp::{Rlp, View};
|
||||
use rlp::View;
|
||||
use spec::Spec;
|
||||
use views::BlockView;
|
||||
use util::stats::Histogram;
|
||||
@ -103,7 +103,7 @@ fn imports_good_block() {
|
||||
client.import_verified_blocks();
|
||||
|
||||
let block = client.block_header(BlockId::Number(1)).unwrap();
|
||||
assert!(!block.is_empty());
|
||||
assert!(!block.into_inner().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -128,7 +128,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<_> = client.block_header(BlockId::Number(1));
|
||||
|
||||
assert!(bad_block.is_none());
|
||||
}
|
||||
@ -180,7 +180,7 @@ fn returns_block_body() {
|
||||
let client = client_result.reference();
|
||||
let block = BlockView::new(&dummy_block);
|
||||
let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap();
|
||||
let body = Rlp::new(&body);
|
||||
let body = body.rlp();
|
||||
assert_eq!(body.item_count(), 2);
|
||||
assert_eq!(body.at(0).as_raw()[..], block.rlp().at(1).as_raw()[..]);
|
||||
assert_eq!(body.at(1).as_raw()[..], block.rlp().at(2).as_raw()[..]);
|
||||
@ -192,7 +192,7 @@ fn imports_block_sequence() {
|
||||
let client = client_result.reference();
|
||||
let block = client.block_header(BlockId::Number(5)).unwrap();
|
||||
|
||||
assert!(!block.is_empty());
|
||||
assert!(!block.into_inner().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -18,7 +18,6 @@ use ethkey::KeyPair;
|
||||
use io::*;
|
||||
use client::{BlockChainClient, Client, ClientConfig};
|
||||
use util::*;
|
||||
use util::trie::TrieSpec;
|
||||
use spec::*;
|
||||
use state_db::StateDB;
|
||||
use block::{OpenBlock, Drain};
|
||||
@ -157,8 +156,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
||||
let test_engine = &*test_spec.engine;
|
||||
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
test_spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
|
||||
let mut db = test_spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let genesis_header = test_spec.genesis_header();
|
||||
|
||||
let mut rolling_timestamp = 40;
|
||||
|
@ -73,7 +73,7 @@ impl Encodable for BlockTracesBloom {
|
||||
|
||||
impl Decodable for BlockTracesBloomGroup {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let blooms = try!(Decodable::decode(decoder));
|
||||
let blooms = Decodable::decode(decoder)?;
|
||||
let group = BlockTracesBloomGroup {
|
||||
blooms: blooms
|
||||
};
|
||||
|
@ -80,9 +80,9 @@ pub enum Existance {
|
||||
impl fmt::Display for Existance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Existance::Born => try!(write!(f, "+++")),
|
||||
Existance::Alive => try!(write!(f, "***")),
|
||||
Existance::Died => try!(write!(f, "XXX")),
|
||||
Existance::Born => write!(f, "+++")?,
|
||||
Existance::Alive => write!(f, "***")?,
|
||||
Existance::Died => write!(f, "XXX")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -117,24 +117,24 @@ impl fmt::Display for AccountDiff {
|
||||
use util::bytes::ToPretty;
|
||||
|
||||
match self.nonce {
|
||||
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
|
||||
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),
|
||||
Diff::Born(ref x) => write!(f, " non {}", x)?,
|
||||
Diff::Changed(ref pre, ref post) => write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))?,
|
||||
_ => {},
|
||||
}
|
||||
match self.balance {
|
||||
Diff::Born(ref x) => try!(write!(f, " bal {}", x)),
|
||||
Diff::Changed(ref pre, ref post) => try!(write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))),
|
||||
Diff::Born(ref x) => write!(f, " bal {}", x)?,
|
||||
Diff::Changed(ref pre, ref post) => write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))?,
|
||||
_ => {},
|
||||
}
|
||||
if let Diff::Born(ref x) = self.code {
|
||||
try!(write!(f, " code {}", x.pretty()));
|
||||
write!(f, " code {}", x.pretty())?;
|
||||
}
|
||||
try!(write!(f, "\n"));
|
||||
write!(f, "\n")?;
|
||||
for (k, dv) in &self.storage {
|
||||
match *dv {
|
||||
Diff::Born(ref v) => try!(write!(f, " + {} => {}\n", interpreted_hash(k), interpreted_hash(v))),
|
||||
Diff::Changed(ref pre, ref post) => try!(write!(f, " * {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre))),
|
||||
Diff::Died(_) => try!(write!(f, " X {}\n", interpreted_hash(k))),
|
||||
Diff::Born(ref v) => write!(f, " + {} => {}\n", interpreted_hash(k), interpreted_hash(v))?,
|
||||
Diff::Changed(ref pre, ref post) => write!(f, " * {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre))?,
|
||||
Diff::Died(_) => write!(f, " X {}\n", interpreted_hash(k))?,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
273
ethcore/src/types/encoded.rs
Normal file
273
ethcore/src/types/encoded.rs
Normal file
@ -0,0 +1,273 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
//! Lazily-decoded owning views of RLP-encoded blockchain objects.
|
||||
//! These views are meant to contain _trusted_ data -- without encoding
|
||||
//! errors or inconsistencies.
|
||||
//!
|
||||
//! In general these views are useful when only a few fields of an object
|
||||
//! are relevant. In these cases it's more efficient to decode the object piecemeal.
|
||||
//! When the entirety of the object is needed, it's better to upgrade it to a fully
|
||||
//! decoded object where parts like the hash can be saved.
|
||||
|
||||
use block::Block as FullBlock;
|
||||
use header::{BlockNumber, Header as FullHeader};
|
||||
use transaction::SignedTransaction;
|
||||
use views;
|
||||
|
||||
use util::{Address, Hashable, H256, H2048, U256};
|
||||
use rlp::{Rlp, View};
|
||||
|
||||
/// Owning header view.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct Header(Vec<u8>);
|
||||
|
||||
impl Header {
|
||||
/// Create a new owning header view.
|
||||
/// Expects the data to be an RLP-encoded header -- any other case will likely lead to
|
||||
/// panics further down the line.
|
||||
pub fn new(encoded: Vec<u8>) -> Self { Header(encoded) }
|
||||
|
||||
/// Upgrade this encoded view to a fully owned `Header` object.
|
||||
pub fn decode(&self) -> FullHeader { ::rlp::decode(&self.0) }
|
||||
|
||||
/// Get a borrowed header view onto the data.
|
||||
#[inline]
|
||||
pub fn view(&self) -> views::HeaderView { views::HeaderView::new(&self.0) }
|
||||
|
||||
/// Get the rlp of the header.
|
||||
#[inline]
|
||||
pub fn rlp(&self) -> Rlp { Rlp::new(&self.0) }
|
||||
|
||||
/// Consume the view and return the raw bytes.
|
||||
pub fn into_inner(self) -> Vec<u8> { self.0 }
|
||||
}
|
||||
|
||||
// forwarders to borrowed view.
|
||||
impl Header {
|
||||
/// Returns the header hash.
|
||||
pub fn hash(&self) -> H256 { self.sha3() }
|
||||
|
||||
/// Returns the parent hash.
|
||||
pub fn parent_hash(&self) -> H256 { self.view().parent_hash() }
|
||||
|
||||
/// Returns the uncles hash.
|
||||
pub fn uncles_hash(&self) -> H256 { self.view().uncles_hash() }
|
||||
|
||||
/// Returns the author.
|
||||
pub fn author(&self) -> Address { self.view().author() }
|
||||
|
||||
/// Returns the state root.
|
||||
pub fn state_root(&self) -> H256 { self.view().state_root() }
|
||||
|
||||
/// Returns the transaction trie root.
|
||||
pub fn transactions_root(&self) -> H256 { self.view().transactions_root() }
|
||||
|
||||
/// Returns the receipts trie root
|
||||
pub fn receipts_root(&self) -> H256 { self.view().receipts_root() }
|
||||
|
||||
/// Returns the block log bloom
|
||||
pub fn log_bloom(&self) -> H2048 { self.view().log_bloom() }
|
||||
|
||||
/// Difficulty of this block
|
||||
pub fn difficulty(&self) -> U256 { self.view().difficulty() }
|
||||
|
||||
/// Number of this block.
|
||||
pub fn number(&self) -> BlockNumber { self.view().number() }
|
||||
|
||||
/// Time this block was produced.
|
||||
pub fn timestamp(&self) -> u64 { self.view().timestamp() }
|
||||
|
||||
/// Gas limit of this block.
|
||||
pub fn gas_limit(&self) -> U256 { self.view().gas_limit() }
|
||||
|
||||
/// Total gas used in this block.
|
||||
pub fn gas_used(&self) -> U256 { self.view().gas_used() }
|
||||
|
||||
/// Block extra data.
|
||||
pub fn extra_data(&self) -> Vec<u8> { self.view().extra_data() }
|
||||
|
||||
/// Engine-specific seal fields.
|
||||
pub fn seal(&self) -> Vec<Vec<u8>> { self.view().seal() }
|
||||
}
|
||||
|
||||
impl Hashable for Header {
|
||||
fn sha3(&self) -> H256 {
|
||||
self.0.sha3()
|
||||
}
|
||||
}
|
||||
|
||||
/// Owning block body view.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct Body(Vec<u8>);
|
||||
|
||||
impl Body {
|
||||
/// Create a new owning block body view. The raw bytes passed in must be an rlp-encoded block
|
||||
/// body.
|
||||
pub fn new(raw: Vec<u8>) -> Self { Body(raw) }
|
||||
|
||||
/// Get a borrowed view of the data within.
|
||||
#[inline]
|
||||
pub fn view(&self) -> views::BodyView { views::BodyView::new(&self.0) }
|
||||
|
||||
/// Fully decode this block body.
|
||||
pub fn decode(&self) -> (Vec<SignedTransaction>, Vec<FullHeader>) {
|
||||
(self.view().transactions(), self.view().uncles())
|
||||
}
|
||||
|
||||
/// Get the RLP of this block body.
|
||||
#[inline]
|
||||
pub fn rlp(&self) -> Rlp {
|
||||
Rlp::new(&self.0)
|
||||
}
|
||||
|
||||
/// Consume the view and return the raw bytes.
|
||||
pub fn into_inner(self) -> Vec<u8> { self.0 }
|
||||
}
|
||||
|
||||
// forwarders to borrowed view.
|
||||
impl Body {
|
||||
/// Get a vector of all transactions.
|
||||
pub fn transactions(&self) -> Vec<SignedTransaction> { self.view().transactions() }
|
||||
|
||||
/// Number of transactions in the block.
|
||||
pub fn transactions_count(&self) -> usize { self.view().transactions_count() }
|
||||
|
||||
/// A view over each transaction in the block.
|
||||
pub fn transaction_views(&self) -> Vec<views::TransactionView> { self.view().transaction_views() }
|
||||
|
||||
/// The hash of each transaction in the block.
|
||||
pub fn transaction_hashes(&self) -> Vec<H256> { self.view().transaction_hashes() }
|
||||
|
||||
/// Decode uncle headers.
|
||||
pub fn uncles(&self) -> Vec<FullHeader> { self.view().uncles() }
|
||||
|
||||
/// Number of uncles.
|
||||
pub fn uncles_count(&self) -> usize { self.view().uncles_count() }
|
||||
|
||||
/// Borrowed view over each uncle.
|
||||
pub fn uncle_views(&self) -> Vec<views::HeaderView> { self.view().uncle_views() }
|
||||
|
||||
/// Hash of each uncle.
|
||||
pub fn uncle_hashes(&self) -> Vec<H256> { self.view().uncle_hashes() }
|
||||
}
|
||||
|
||||
/// Owning block view.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct Block(Vec<u8>);
|
||||
|
||||
impl Block {
|
||||
/// Create a new owning block view. The raw bytes passed in must be an rlp-encoded block.
|
||||
pub fn new(raw: Vec<u8>) -> Self { Block(raw) }
|
||||
|
||||
/// Get a borrowed view of the whole block.
|
||||
#[inline]
|
||||
pub fn view(&self) -> views::BlockView { views::BlockView::new(&self.0) }
|
||||
|
||||
/// Get a borrowed view of the block header.
|
||||
#[inline]
|
||||
pub fn header_view(&self) -> views::HeaderView { self.view().header_view() }
|
||||
|
||||
/// Decode to a full block.
|
||||
pub fn decode(&self) -> FullBlock { ::rlp::decode(&self.0) }
|
||||
|
||||
/// Get the rlp of this block.
|
||||
#[inline]
|
||||
pub fn rlp(&self) -> Rlp {
|
||||
Rlp::new(&self.0)
|
||||
}
|
||||
|
||||
/// Consume the view and return the raw bytes.
|
||||
pub fn into_inner(self) -> Vec<u8> { self.0 }
|
||||
}
|
||||
|
||||
// forwarders to borrowed header view.
|
||||
impl Block {
|
||||
/// Returns the header hash.
|
||||
pub fn hash(&self) -> H256 { self.header_view().sha3() }
|
||||
|
||||
/// Returns the parent hash.
|
||||
pub fn parent_hash(&self) -> H256 { self.header_view().parent_hash() }
|
||||
|
||||
/// Returns the uncles hash.
|
||||
pub fn uncles_hash(&self) -> H256 { self.header_view().uncles_hash() }
|
||||
|
||||
/// Returns the author.
|
||||
pub fn author(&self) -> Address { self.header_view().author() }
|
||||
|
||||
/// Returns the state root.
|
||||
pub fn state_root(&self) -> H256 { self.header_view().state_root() }
|
||||
|
||||
/// Returns the transaction trie root.
|
||||
pub fn transactions_root(&self) -> H256 { self.header_view().transactions_root() }
|
||||
|
||||
/// Returns the receipts trie root
|
||||
pub fn receipts_root(&self) -> H256 { self.header_view().receipts_root() }
|
||||
|
||||
/// Returns the block log bloom
|
||||
pub fn log_bloom(&self) -> H2048 { self.header_view().log_bloom() }
|
||||
|
||||
/// Difficulty of this block
|
||||
pub fn difficulty(&self) -> U256 { self.header_view().difficulty() }
|
||||
|
||||
/// Number of this block.
|
||||
pub fn number(&self) -> BlockNumber { self.header_view().number() }
|
||||
|
||||
/// Time this block was produced.
|
||||
pub fn timestamp(&self) -> u64 { self.header_view().timestamp() }
|
||||
|
||||
/// Gas limit of this block.
|
||||
pub fn gas_limit(&self) -> U256 { self.header_view().gas_limit() }
|
||||
|
||||
/// Total gas used in this block.
|
||||
pub fn gas_used(&self) -> U256 { self.header_view().gas_used() }
|
||||
|
||||
/// Block extra data.
|
||||
pub fn extra_data(&self) -> Vec<u8> { self.header_view().extra_data() }
|
||||
|
||||
/// Engine-specific seal fields.
|
||||
pub fn seal(&self) -> Vec<Vec<u8>> { self.header_view().seal() }
|
||||
}
|
||||
|
||||
// forwarders to body view.
|
||||
impl Block {
|
||||
/// Get a vector of all transactions.
|
||||
pub fn transactions(&self) -> Vec<SignedTransaction> { self.view().transactions() }
|
||||
|
||||
/// Number of transactions in the block.
|
||||
pub fn transactions_count(&self) -> usize { self.view().transactions_count() }
|
||||
|
||||
/// A view over each transaction in the block.
|
||||
pub fn transaction_views(&self) -> Vec<views::TransactionView> { self.view().transaction_views() }
|
||||
|
||||
/// The hash of each transaction in the block.
|
||||
pub fn transaction_hashes(&self) -> Vec<H256> { self.view().transaction_hashes() }
|
||||
|
||||
/// Decode uncle headers.
|
||||
pub fn uncles(&self) -> Vec<FullHeader> { self.view().uncles() }
|
||||
|
||||
/// Number of uncles.
|
||||
pub fn uncles_count(&self) -> usize { self.view().uncles_count() }
|
||||
|
||||
/// Borrowed view over each uncle.
|
||||
pub fn uncle_views(&self) -> Vec<views::HeaderView> { self.view().uncle_views() }
|
||||
|
||||
/// Hash of each uncle.
|
||||
pub fn uncle_hashes(&self) -> Vec<H256> { self.view().uncle_hashes() }
|
||||
}
|
@ -50,9 +50,9 @@ impl Decodable for LogEntry {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let entry = LogEntry {
|
||||
address: try!(d.val_at(0)),
|
||||
topics: try!(d.val_at(1)),
|
||||
data: try!(d.val_at(2)),
|
||||
address: d.val_at(0)?,
|
||||
topics: d.val_at(1)?,
|
||||
data: d.val_at(2)?,
|
||||
};
|
||||
Ok(entry)
|
||||
}
|
||||
|
@ -35,4 +35,5 @@ pub mod restoration_status;
|
||||
pub mod snapshot_manifest;
|
||||
pub mod mode;
|
||||
pub mod pruning_info;
|
||||
pub mod security_level;
|
||||
pub mod security_level;
|
||||
pub mod encoded;
|
||||
|
@ -28,4 +28,6 @@ pub struct PruningInfo {
|
||||
pub earliest_chain: u64,
|
||||
/// The first block where state requests may be served.
|
||||
pub earliest_state: u64,
|
||||
/// State pruning history size.
|
||||
pub state_history_size: Option<u64>,
|
||||
}
|
||||
|
@ -64,10 +64,10 @@ impl Decodable for Receipt {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let receipt = Receipt {
|
||||
state_root: try!(d.val_at(0)),
|
||||
gas_used: try!(d.val_at(1)),
|
||||
log_bloom: try!(d.val_at(2)),
|
||||
logs: try!(d.val_at(3)),
|
||||
state_root: d.val_at(0)?,
|
||||
gas_used: d.val_at(1)?,
|
||||
log_bloom: d.val_at(2)?,
|
||||
logs: d.val_at(3)?,
|
||||
};
|
||||
Ok(receipt)
|
||||
}
|
||||
|
@ -53,11 +53,11 @@ impl ManifestData {
|
||||
pub fn from_rlp(raw: &[u8]) -> Result<Self, DecoderError> {
|
||||
let decoder = UntrustedRlp::new(raw);
|
||||
|
||||
let state_hashes: Vec<H256> = try!(decoder.val_at(0));
|
||||
let block_hashes: Vec<H256> = try!(decoder.val_at(1));
|
||||
let state_root: H256 = try!(decoder.val_at(2));
|
||||
let block_number: u64 = try!(decoder.val_at(3));
|
||||
let block_hash: H256 = try!(decoder.val_at(4));
|
||||
let state_hashes: Vec<H256> = decoder.val_at(0)?;
|
||||
let block_hashes: Vec<H256> = decoder.val_at(1)?;
|
||||
let state_root: H256 = decoder.val_at(2)?;
|
||||
let block_number: u64 = decoder.val_at(3)?;
|
||||
let block_hash: H256 = decoder.val_at(4)?;
|
||||
|
||||
Ok(ManifestData {
|
||||
state_hashes: state_hashes,
|
||||
|
@ -41,7 +41,7 @@ impl StateDiff {
|
||||
impl fmt::Display for StateDiff {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for (add, acc) in &self.raw {
|
||||
try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
|
||||
write!(f, "{} {}: {}", acc.existance(), add, acc)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ impl Encodable for Error {
|
||||
impl Decodable for Error {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
use self::Error::*;
|
||||
let value: u8 = try!(decoder.as_rlp().as_val());
|
||||
let value: u8 = decoder.as_rlp().as_val()?;
|
||||
match value {
|
||||
0 => Ok(OutOfGas),
|
||||
1 => Ok(BadJumpDestination),
|
||||
|
@ -66,11 +66,11 @@ impl Encodable for FlatTrace {
|
||||
impl Decodable for FlatTrace {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let v: Vec<usize> = try!(d.val_at(3));
|
||||
let v: Vec<usize> = d.val_at(3)?;
|
||||
let res = FlatTrace {
|
||||
action: try!(d.val_at(0)),
|
||||
result: try!(d.val_at(1)),
|
||||
subtraces: try!(d.val_at(2)),
|
||||
action: d.val_at(0)?,
|
||||
result: d.val_at(1)?,
|
||||
subtraces: d.val_at(2)?,
|
||||
trace_address: v.into_iter().collect(),
|
||||
};
|
||||
|
||||
@ -109,7 +109,7 @@ impl Encodable for FlatTransactionTraces {
|
||||
|
||||
impl Decodable for FlatTransactionTraces {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
Ok(FlatTransactionTraces(try!(Decodable::decode(decoder))))
|
||||
Ok(FlatTransactionTraces(Decodable::decode(decoder)?))
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ impl Encodable for FlatBlockTraces {
|
||||
|
||||
impl Decodable for FlatBlockTraces {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
Ok(FlatBlockTraces(try!(Decodable::decode(decoder))))
|
||||
Ok(FlatBlockTraces(Decodable::decode(decoder)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ impl Decodable for CallResult {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = CallResult {
|
||||
gas_used: try!(d.val_at(0)),
|
||||
output: try!(d.val_at(1)),
|
||||
gas_used: d.val_at(0)?,
|
||||
output: d.val_at(1)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@ -81,9 +81,9 @@ impl Decodable for CreateResult {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = CreateResult {
|
||||
gas_used: try!(d.val_at(0)),
|
||||
code: try!(d.val_at(1)),
|
||||
address: try!(d.val_at(2)),
|
||||
gas_used: d.val_at(0)?,
|
||||
code: d.val_at(1)?,
|
||||
address: d.val_at(2)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@ -144,12 +144,12 @@ impl Decodable for Call {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = Call {
|
||||
from: try!(d.val_at(0)),
|
||||
to: try!(d.val_at(1)),
|
||||
value: try!(d.val_at(2)),
|
||||
gas: try!(d.val_at(3)),
|
||||
input: try!(d.val_at(4)),
|
||||
call_type: try!(d.val_at(5)),
|
||||
from: d.val_at(0)?,
|
||||
to: d.val_at(1)?,
|
||||
value: d.val_at(2)?,
|
||||
gas: d.val_at(3)?,
|
||||
input: d.val_at(4)?,
|
||||
call_type: d.val_at(5)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@ -204,10 +204,10 @@ impl Decodable for Create {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = Create {
|
||||
from: try!(d.val_at(0)),
|
||||
value: try!(d.val_at(1)),
|
||||
gas: try!(d.val_at(2)),
|
||||
init: try!(d.val_at(3)),
|
||||
from: d.val_at(0)?,
|
||||
value: d.val_at(1)?,
|
||||
gas: d.val_at(2)?,
|
||||
init: d.val_at(3)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@ -255,9 +255,9 @@ impl Decodable for Suicide {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = Suicide {
|
||||
address: try!(d.val_at(0)),
|
||||
refund_address: try!(d.val_at(1)),
|
||||
balance: try!(d.val_at(2)),
|
||||
address: d.val_at(0)?,
|
||||
refund_address: d.val_at(1)?,
|
||||
balance: d.val_at(2)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@ -300,7 +300,7 @@ impl Encodable for Action {
|
||||
impl Decodable for Action {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let action_type: u8 = try!(d.val_at(0));
|
||||
let action_type: u8 = d.val_at(0)?;
|
||||
match action_type {
|
||||
0 => d.val_at(1).map(Action::Call),
|
||||
1 => d.val_at(1).map(Action::Create),
|
||||
@ -371,7 +371,7 @@ impl Encodable for Res {
|
||||
impl Decodable for Res {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let action_type: u8 = try!(d.val_at(0));
|
||||
let action_type: u8 = d.val_at(0)?;
|
||||
match action_type {
|
||||
0 => d.val_at(1).map(Res::Call),
|
||||
1 => d.val_at(1).map(Res::Create),
|
||||
@ -415,8 +415,8 @@ impl Decodable for MemoryDiff {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
Ok(MemoryDiff {
|
||||
offset: try!(d.val_at(0)),
|
||||
data: try!(d.val_at(1)),
|
||||
offset: d.val_at(0)?,
|
||||
data: d.val_at(1)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -443,8 +443,8 @@ impl Decodable for StorageDiff {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
Ok(StorageDiff {
|
||||
location: try!(d.val_at(0)),
|
||||
value: try!(d.val_at(1)),
|
||||
location: d.val_at(0)?,
|
||||
value: d.val_at(1)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -477,10 +477,10 @@ impl Decodable for VMExecutedOperation {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
Ok(VMExecutedOperation {
|
||||
gas_used: try!(d.val_at(0)),
|
||||
stack_push: try!(d.val_at(1)),
|
||||
mem_diff: try!(d.val_at(2)),
|
||||
store_diff: try!(d.val_at(3)),
|
||||
gas_used: d.val_at(0)?,
|
||||
stack_push: d.val_at(1)?,
|
||||
mem_diff: d.val_at(2)?,
|
||||
store_diff: d.val_at(3)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -513,10 +513,10 @@ impl Decodable for VMOperation {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = VMOperation {
|
||||
pc: try!(d.val_at(0)),
|
||||
instruction: try!(d.val_at(1)),
|
||||
gas_cost: try!(d.val_at(2)),
|
||||
executed: try!(d.val_at(3)),
|
||||
pc: d.val_at(0)?,
|
||||
instruction: d.val_at(1)?,
|
||||
gas_cost: d.val_at(2)?,
|
||||
executed: d.val_at(3)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@ -552,10 +552,10 @@ impl Decodable for VMTrace {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = VMTrace {
|
||||
parent_step: try!(d.val_at(0)),
|
||||
code: try!(d.val_at(1)),
|
||||
operations: try!(d.val_at(2)),
|
||||
subs: try!(d.val_at(3)),
|
||||
parent_step: d.val_at(0)?,
|
||||
code: d.val_at(1)?,
|
||||
operations: d.val_at(2)?,
|
||||
subs: d.val_at(3)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
|
@ -48,7 +48,7 @@ impl Decodable for Action {
|
||||
if rlp.is_empty() {
|
||||
Ok(Action::Create)
|
||||
} else {
|
||||
Ok(Action::Call(try!(rlp.as_val())))
|
||||
Ok(Action::Call(rlp.as_val()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -247,16 +247,16 @@ impl Decodable for SignedTransaction {
|
||||
}
|
||||
Ok(SignedTransaction {
|
||||
unsigned: Transaction {
|
||||
nonce: try!(d.val_at(0)),
|
||||
gas_price: try!(d.val_at(1)),
|
||||
gas: try!(d.val_at(2)),
|
||||
action: try!(d.val_at(3)),
|
||||
value: try!(d.val_at(4)),
|
||||
data: try!(d.val_at(5)),
|
||||
nonce: d.val_at(0)?,
|
||||
gas_price: d.val_at(1)?,
|
||||
gas: d.val_at(2)?,
|
||||
action: d.val_at(3)?,
|
||||
value: d.val_at(4)?,
|
||||
data: d.val_at(5)?,
|
||||
},
|
||||
v: try!(d.val_at(6)),
|
||||
r: try!(d.val_at(7)),
|
||||
s: try!(d.val_at(8)),
|
||||
v: d.val_at(6)?,
|
||||
r: d.val_at(7)?,
|
||||
s: d.val_at(8)?,
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
})
|
||||
@ -338,7 +338,7 @@ impl SignedTransaction {
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = public_to_address(&try!(self.public_key()));
|
||||
let s = public_to_address(&self.public_key()?);
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
@ -347,7 +347,7 @@ impl SignedTransaction {
|
||||
|
||||
/// Returns the public key of the sender.
|
||||
pub fn public_key(&self) -> Result<Public, Error> {
|
||||
Ok(try!(recover(&self.signature(), &self.unsigned.hash(self.network_id()))))
|
||||
Ok(recover(&self.signature(), &self.unsigned.hash(self.network_id()))?)
|
||||
}
|
||||
|
||||
/// Do basic validation, checking for valid signature and minimum gas,
|
||||
@ -363,7 +363,7 @@ impl SignedTransaction {
|
||||
Some(1) if allow_network_id_of_one => {},
|
||||
_ => return Err(TransactionError::InvalidNetworkId.into()),
|
||||
}
|
||||
try!(self.sender());
|
||||
self.sender()?;
|
||||
if self.gas < U256::from(self.gas_required(&schedule)) {
|
||||
Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into())
|
||||
} else {
|
||||
|
@ -19,18 +19,21 @@
|
||||
use engines::Engine;
|
||||
use error::Error;
|
||||
|
||||
use util::{HeapSizeOf, H256};
|
||||
use util::{HeapSizeOf, H256, U256};
|
||||
|
||||
pub use self::blocks::Blocks;
|
||||
pub use self::headers::Headers;
|
||||
|
||||
/// Something which can produce a hash and a parent hash.
|
||||
pub trait HasHash {
|
||||
pub trait BlockLike {
|
||||
/// Get the hash of this item.
|
||||
fn hash(&self) -> H256;
|
||||
|
||||
/// Get the hash of this item's parent.
|
||||
fn parent_hash(&self) -> H256;
|
||||
|
||||
/// Get the difficulty of this item.
|
||||
fn difficulty(&self) -> U256;
|
||||
}
|
||||
|
||||
/// Defines transitions between stages of verification.
|
||||
@ -45,13 +48,13 @@ pub trait HasHash {
|
||||
/// consistent.
|
||||
pub trait Kind: 'static + Sized + Send + Sync {
|
||||
/// The first stage: completely unverified.
|
||||
type Input: Sized + Send + HasHash + HeapSizeOf;
|
||||
type Input: Sized + Send + BlockLike + HeapSizeOf;
|
||||
|
||||
/// The second stage: partially verified.
|
||||
type Unverified: Sized + Send + HasHash + HeapSizeOf;
|
||||
type Unverified: Sized + Send + BlockLike + HeapSizeOf;
|
||||
|
||||
/// The third stage: completely verified.
|
||||
type Verified: Sized + Send + HasHash + HeapSizeOf;
|
||||
type Verified: Sized + Send + BlockLike + HeapSizeOf;
|
||||
|
||||
/// Attempt to create the `Unverified` item from the input.
|
||||
fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error>;
|
||||
@ -62,14 +65,14 @@ pub trait Kind: 'static + Sized + Send + Sync {
|
||||
|
||||
/// The blocks verification module.
|
||||
pub mod blocks {
|
||||
use super::{Kind, HasHash};
|
||||
use super::{Kind, BlockLike};
|
||||
|
||||
use engines::Engine;
|
||||
use error::Error;
|
||||
use header::Header;
|
||||
use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered};
|
||||
|
||||
use util::{Bytes, HeapSizeOf, H256};
|
||||
use util::{Bytes, HeapSizeOf, H256, U256};
|
||||
|
||||
/// A mode for verifying blocks.
|
||||
pub struct Blocks;
|
||||
@ -126,7 +129,7 @@ pub mod blocks {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasHash for Unverified {
|
||||
impl BlockLike for Unverified {
|
||||
fn hash(&self) -> H256 {
|
||||
self.header.hash()
|
||||
}
|
||||
@ -134,9 +137,13 @@ pub mod blocks {
|
||||
fn parent_hash(&self) -> H256 {
|
||||
self.header.parent_hash().clone()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header.difficulty().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasHash for PreverifiedBlock {
|
||||
impl BlockLike for PreverifiedBlock {
|
||||
fn hash(&self) -> H256 {
|
||||
self.header.hash()
|
||||
}
|
||||
@ -144,12 +151,16 @@ pub mod blocks {
|
||||
fn parent_hash(&self) -> H256 {
|
||||
self.header.parent_hash().clone()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header.difficulty().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verification for headers.
|
||||
pub mod headers {
|
||||
use super::{Kind, HasHash};
|
||||
use super::{Kind, BlockLike};
|
||||
|
||||
use engines::Engine;
|
||||
use error::Error;
|
||||
@ -157,10 +168,12 @@ pub mod headers {
|
||||
use verification::verify_header_params;
|
||||
|
||||
use util::hash::H256;
|
||||
use util::U256;
|
||||
|
||||
impl HasHash for Header {
|
||||
impl BlockLike for Header {
|
||||
fn hash(&self) -> H256 { self.hash() }
|
||||
fn parent_hash(&self) -> H256 { self.parent_hash().clone() }
|
||||
fn difficulty(&self) -> U256 { self.difficulty().clone() }
|
||||
}
|
||||
|
||||
/// A mode for verifying headers.
|
||||
|
@ -26,7 +26,7 @@ use error::*;
|
||||
use engines::Engine;
|
||||
use service::*;
|
||||
|
||||
use self::kind::{HasHash, Kind};
|
||||
use self::kind::{BlockLike, Kind};
|
||||
|
||||
pub use types::verification_queue_info::VerificationQueueInfo as QueueInfo;
|
||||
|
||||
@ -132,13 +132,14 @@ pub struct VerificationQueue<K: Kind> {
|
||||
deleting: Arc<AtomicBool>,
|
||||
ready_signal: Arc<QueueSignal>,
|
||||
empty: Arc<SCondvar>,
|
||||
processing: RwLock<HashSet<H256>>,
|
||||
processing: RwLock<HashMap<H256, U256>>, // hash to difficulty
|
||||
ticks_since_adjustment: AtomicUsize,
|
||||
max_queue_size: usize,
|
||||
max_mem_use: usize,
|
||||
scale_verifiers: bool,
|
||||
verifier_handles: Vec<JoinHandle<()>>,
|
||||
state: Arc<(Mutex<State>, Condvar)>,
|
||||
total_difficulty: RwLock<U256>,
|
||||
}
|
||||
|
||||
struct QueueSignal {
|
||||
@ -269,7 +270,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
more_to_verify: more_to_verify,
|
||||
verification: verification,
|
||||
deleting: deleting,
|
||||
processing: RwLock::new(HashSet::new()),
|
||||
processing: RwLock::new(HashMap::new()),
|
||||
empty: empty,
|
||||
ticks_since_adjustment: AtomicUsize::new(0),
|
||||
max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT),
|
||||
@ -277,6 +278,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
scale_verifiers: scale_verifiers,
|
||||
verifier_handles: verifier_handles,
|
||||
state: state,
|
||||
total_difficulty: RwLock::new(0.into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,6 +436,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
sizes.unverified.store(0, AtomicOrdering::Release);
|
||||
sizes.verifying.store(0, AtomicOrdering::Release);
|
||||
sizes.verified.store(0, AtomicOrdering::Release);
|
||||
*self.total_difficulty.write() = 0.into();
|
||||
|
||||
self.processing.write().clear();
|
||||
}
|
||||
@ -448,7 +451,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
|
||||
/// Check if the item is currently in the queue
|
||||
pub fn status(&self, hash: &H256) -> Status {
|
||||
if self.processing.read().contains(hash) {
|
||||
if self.processing.read().contains_key(hash) {
|
||||
return Status::Queued;
|
||||
}
|
||||
if self.verification.bad.lock().contains(hash) {
|
||||
@ -461,7 +464,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
pub fn import(&self, input: K::Input) -> ImportResult {
|
||||
let h = input.hash();
|
||||
{
|
||||
if self.processing.read().contains(&h) {
|
||||
if self.processing.read().contains_key(&h) {
|
||||
return Err(ImportError::AlreadyQueued.into());
|
||||
}
|
||||
|
||||
@ -480,7 +483,11 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
Ok(item) => {
|
||||
self.verification.sizes.unverified.fetch_add(item.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
|
||||
self.processing.write().insert(h.clone());
|
||||
self.processing.write().insert(h.clone(), item.difficulty());
|
||||
{
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td + item.difficulty();
|
||||
}
|
||||
self.verification.unverified.lock().push_back(item);
|
||||
self.more_to_verify.notify_all();
|
||||
Ok(h)
|
||||
@ -511,7 +518,10 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
bad.reserve(hashes.len());
|
||||
for hash in hashes {
|
||||
bad.insert(hash.clone());
|
||||
processing.remove(hash);
|
||||
if let Some(difficulty) = processing.remove(hash) {
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td - difficulty;
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_verified = VecDeque::new();
|
||||
@ -520,7 +530,10 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
if bad.contains(&output.parent_hash()) {
|
||||
removed_size += output.heap_size_of_children();
|
||||
bad.insert(output.hash());
|
||||
processing.remove(&output.hash());
|
||||
if let Some(difficulty) = processing.remove(&output.hash()) {
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td - difficulty;
|
||||
}
|
||||
} else {
|
||||
new_verified.push_back(output);
|
||||
}
|
||||
@ -538,7 +551,10 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
}
|
||||
let mut processing = self.processing.write();
|
||||
for hash in hashes {
|
||||
processing.remove(hash);
|
||||
if let Some(difficulty) = processing.remove(hash) {
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td - difficulty;
|
||||
}
|
||||
}
|
||||
processing.is_empty()
|
||||
}
|
||||
@ -592,6 +608,11 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the total difficulty of all the blocks in the queue.
|
||||
pub fn total_difficulty(&self) -> U256 {
|
||||
self.total_difficulty.read().clone()
|
||||
}
|
||||
|
||||
/// Get the current number of working verifiers.
|
||||
pub fn num_verifiers(&self) -> usize {
|
||||
match *self.state.0.lock() {
|
||||
@ -760,6 +781,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_total_difficulty() {
|
||||
let queue = get_test_queue(false);
|
||||
let block = get_good_dummy_block();
|
||||
let hash = BlockView::new(&block).header().hash().clone();
|
||||
if let Err(e) = queue.import(Unverified::new(block)) {
|
||||
panic!("error importing block that is valid by definition({:?})", e);
|
||||
}
|
||||
queue.flush();
|
||||
assert_eq!(queue.total_difficulty(), 131072.into());
|
||||
queue.drain(10);
|
||||
assert_eq!(queue.total_difficulty(), 131072.into());
|
||||
queue.mark_as_good(&[ hash ]);
|
||||
assert_eq!(queue.total_difficulty(), 0.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_ok_for_drained_duplicates() {
|
||||
let queue = get_test_queue(false);
|
||||
|
@ -51,19 +51,19 @@ impl HeapSizeOf for PreverifiedBlock {
|
||||
|
||||
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
|
||||
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
||||
try!(verify_header_params(&header, engine));
|
||||
try!(verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash()));
|
||||
try!(engine.verify_block_basic(&header, Some(bytes)));
|
||||
for u in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
let u = try!(u);
|
||||
try!(verify_header_params(&u, engine));
|
||||
try!(engine.verify_block_basic(&u, None));
|
||||
verify_header_params(&header, engine)?;
|
||||
verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash())?;
|
||||
engine.verify_block_basic(&header, Some(bytes))?;
|
||||
for u in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
let u = u?;
|
||||
verify_header_params(&u, engine)?;
|
||||
engine.verify_block_basic(&u, None)?;
|
||||
}
|
||||
// Verify transactions.
|
||||
// TODO: either use transaction views or cache the decoded transactions.
|
||||
let v = BlockView::new(bytes);
|
||||
for t in v.transactions() {
|
||||
try!(engine.verify_transaction_basic(&t, &header));
|
||||
engine.verify_transaction_basic(&t, &header)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -73,9 +73,9 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
|
||||
/// Returns a `PreverifiedBlock` structure populated with transactions
|
||||
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, check_seal: bool) -> Result<PreverifiedBlock, Error> {
|
||||
if check_seal {
|
||||
try!(engine.verify_block_unordered(&header, Some(&bytes)));
|
||||
for u in try!(UntrustedRlp::new(&bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
try!(engine.verify_block_unordered(&try!(u), None));
|
||||
engine.verify_block_unordered(&header, Some(&bytes))?;
|
||||
for u in UntrustedRlp::new(&bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
engine.verify_block_unordered(&u?, None)?;
|
||||
}
|
||||
}
|
||||
// Verify transactions.
|
||||
@ -83,7 +83,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, che
|
||||
{
|
||||
let v = BlockView::new(&bytes);
|
||||
for t in v.transactions() {
|
||||
try!(engine.verify_transaction(&t, &header));
|
||||
engine.verify_transaction(&t, &header)?;
|
||||
transactions.push(t);
|
||||
}
|
||||
}
|
||||
@ -97,11 +97,11 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, che
|
||||
/// Phase 3 verification. Check block information against parent and uncles.
|
||||
pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
|
||||
// TODO: verify timestamp
|
||||
let parent = try!(bc.block_header(&header.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash().clone()))));
|
||||
try!(verify_parent(&header, &parent));
|
||||
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
|
||||
let parent = bc.block_header(&header.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash().clone())))?;
|
||||
verify_parent(&header, &parent)?;
|
||||
engine.verify_block_family(&header, &parent, Some(bytes))?;
|
||||
|
||||
let num_uncles = try!(UntrustedRlp::new(bytes).at(2)).item_count();
|
||||
let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count();
|
||||
if num_uncles != 0 {
|
||||
if num_uncles > engine.maximum_uncle_count() {
|
||||
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles })));
|
||||
@ -117,15 +117,15 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
||||
excluded.insert(details.parent.clone());
|
||||
let b = bc.block(&hash)
|
||||
.expect("parent already known to be stored; qed");
|
||||
excluded.extend(BlockView::new(&b).uncle_hashes());
|
||||
excluded.extend(b.uncle_hashes());
|
||||
hash = details.parent;
|
||||
}
|
||||
None => break
|
||||
}
|
||||
}
|
||||
|
||||
for uncle in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
let uncle = try!(uncle);
|
||||
for uncle in UntrustedRlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
let uncle = uncle?;
|
||||
if excluded.contains(&uncle.hash()) {
|
||||
return Err(From::from(BlockError::UncleInChain(uncle.hash())))
|
||||
}
|
||||
@ -157,7 +157,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
||||
// cB.p^7 -------------/
|
||||
// cB.p^8
|
||||
let mut expected_uncle_parent = header.parent_hash().clone();
|
||||
let uncle_parent = try!(bc.block_header(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone()))));
|
||||
let uncle_parent = bc.block_header(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone())))?;
|
||||
for _ in 0..depth {
|
||||
match bc.block_details(&expected_uncle_parent) {
|
||||
Some(details) => {
|
||||
@ -170,8 +170,8 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
||||
return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash())));
|
||||
}
|
||||
|
||||
try!(verify_parent(&uncle, &uncle_parent));
|
||||
try!(engine.verify_block_family(&uncle, &uncle_parent, Some(bytes)));
|
||||
verify_parent(&uncle, &uncle_parent)?;
|
||||
engine.verify_block_family(&uncle, &uncle_parent, Some(bytes))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -234,12 +234,12 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
|
||||
/// Verify block data against header: transactions root and uncles hash.
|
||||
fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> {
|
||||
let block = UntrustedRlp::new(block);
|
||||
let tx = try!(block.at(1));
|
||||
let tx = block.at(1)?;
|
||||
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec())); //TODO: get rid of vectors here
|
||||
if expected_root != transactions_root {
|
||||
return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() })))
|
||||
}
|
||||
let expected_uncles = &try!(block.at(2)).as_raw().sha3();
|
||||
let expected_uncles = &block.at(2)?.as_raw().sha3();
|
||||
if expected_uncles != uncles_hash {
|
||||
return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() })))
|
||||
}
|
||||
@ -264,6 +264,7 @@ mod tests {
|
||||
use types::log_entry::{LogEntry, LocalizedLogEntry};
|
||||
use rlp::View;
|
||||
use time::get_time;
|
||||
use encoded;
|
||||
|
||||
fn check_ok(result: Result<(), Error>) {
|
||||
result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e));
|
||||
@ -322,16 +323,20 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Get raw block data
|
||||
fn block(&self, hash: &H256) -> Option<Bytes> {
|
||||
self.blocks.get(hash).cloned()
|
||||
fn block(&self, hash: &H256) -> Option<encoded::Block> {
|
||||
self.blocks.get(hash).cloned().map(encoded::Block::new)
|
||||
}
|
||||
|
||||
fn block_header_data(&self, hash: &H256) -> Option<Bytes> {
|
||||
self.block(hash).map(|b| BlockView::new(&b).header_rlp().as_raw().to_vec())
|
||||
fn block_header_data(&self, hash: &H256) -> Option<encoded::Header> {
|
||||
self.block(hash)
|
||||
.map(|b| b.header_view().rlp().as_raw().to_vec())
|
||||
.map(encoded::Header::new)
|
||||
}
|
||||
|
||||
fn block_body(&self, hash: &H256) -> Option<Bytes> {
|
||||
self.block(hash).map(|b| BlockChain::block_to_body(&b))
|
||||
fn block_body(&self, hash: &H256) -> Option<encoded::Body> {
|
||||
self.block(hash)
|
||||
.map(|b| BlockChain::block_to_body(&b.into_inner()))
|
||||
.map(encoded::Body::new)
|
||||
}
|
||||
|
||||
fn best_ancient_block(&self) -> Option<H256> {
|
||||
|
@ -94,7 +94,7 @@ impl<'a> BlockView<'a> {
|
||||
}
|
||||
|
||||
/// Return List of transactions in given block.
|
||||
pub fn transaction_views(&self) -> Vec<TransactionView> {
|
||||
pub fn transaction_views(&self) -> Vec<TransactionView<'a>> {
|
||||
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ impl<'a> BlockView<'a> {
|
||||
}
|
||||
|
||||
/// Return List of transactions in given block.
|
||||
pub fn uncle_views(&self) -> Vec<HeaderView> {
|
||||
pub fn uncle_views(&self) -> Vec<HeaderView<'a>> {
|
||||
self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect()
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ impl<'a> BodyView<'a> {
|
||||
}
|
||||
|
||||
/// Return List of transactions in given block.
|
||||
pub fn transaction_views(&self) -> Vec<TransactionView> {
|
||||
pub fn transaction_views(&self) -> Vec<TransactionView<'a>> {
|
||||
self.rlp.at(0).iter().map(TransactionView::new_from_rlp).collect()
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ impl<'a> BodyView<'a> {
|
||||
}
|
||||
|
||||
/// Return List of transactions in given block.
|
||||
pub fn uncle_views(&self) -> Vec<HeaderView> {
|
||||
pub fn uncle_views(&self) -> Vec<HeaderView<'a>> {
|
||||
self.rlp.at(1).iter().map(HeaderView::new_from_rlp).collect()
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ pub mod aes {
|
||||
let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec());
|
||||
let len = dest.len();
|
||||
let mut buffer = RefWriteBuffer::new(dest);
|
||||
try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true));
|
||||
encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)?;
|
||||
Ok(len - buffer.remaining())
|
||||
}
|
||||
}
|
||||
@ -179,7 +179,7 @@ pub mod ecdh {
|
||||
temp
|
||||
};
|
||||
|
||||
let publ = try!(key::PublicKey::from_slice(context, &pdata));
|
||||
let publ = key::PublicKey::from_slice(context, &pdata)?;
|
||||
// no way to create SecretKey from raw byte array.
|
||||
let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) };
|
||||
let shared = ecdh::SharedSecret::new_raw(context, &publ, sec);
|
||||
@ -206,7 +206,7 @@ pub mod ecies {
|
||||
let r = Random.generate()
|
||||
.expect("context known to have key-generation capabilities; qed");
|
||||
|
||||
let z = try!(ecdh::agree(r.secret(), public));
|
||||
let z = ecdh::agree(r.secret(), public)?;
|
||||
let mut key = [0u8; 32];
|
||||
let mut mkey = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
@ -243,7 +243,7 @@ pub mod ecies {
|
||||
let r = Random.generate()
|
||||
.expect("context known to have key-generation capabilities");
|
||||
|
||||
let z = try!(ecdh::agree(r.secret(), public));
|
||||
let z = ecdh::agree(r.secret(), public)?;
|
||||
let mut key = [0u8; 32];
|
||||
let mut mkey = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
@ -274,7 +274,7 @@ pub mod ecies {
|
||||
|
||||
let e = &encrypted[1..];
|
||||
let p = Public::from_slice(&e[0..64]);
|
||||
let z = try!(ecdh::agree(secret, &p));
|
||||
let z = ecdh::agree(secret, &p)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
let ekey = &key[0..16];
|
||||
@ -314,7 +314,7 @@ pub mod ecies {
|
||||
|
||||
let e = encrypted;
|
||||
let p = Public::from_slice(&e[0..64]);
|
||||
let z = try!(ecdh::agree(secret, &p));
|
||||
let z = ecdh::agree(secret, &p)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
let ekey = &key[0..16];
|
||||
|
@ -160,37 +160,37 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
||||
|
||||
return if args.cmd_info {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let secret = try!(args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret));
|
||||
let keypair = try!(KeyPair::from_secret(secret));
|
||||
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
let keypair = KeyPair::from_secret(secret)?;
|
||||
Ok(display(keypair, display_mode))
|
||||
} else if args.cmd_generate {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let keypair = if args.cmd_random {
|
||||
Random.generate()
|
||||
} else if args.cmd_prefix {
|
||||
let prefix = try!(args.arg_prefix.from_hex());
|
||||
let iterations = try!(usize::from_str_radix(&args.arg_iterations, 10));
|
||||
let prefix = args.arg_prefix.from_hex()?;
|
||||
let iterations = usize::from_str_radix(&args.arg_iterations, 10)?;
|
||||
Prefix::new(prefix, iterations).generate()
|
||||
} else if args.cmd_brain {
|
||||
Brain::new(args.arg_seed).generate()
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
Ok(display(try!(keypair), display_mode))
|
||||
Ok(display(keypair?, display_mode))
|
||||
} else if args.cmd_sign {
|
||||
let secret = try!(args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret));
|
||||
let message = try!(args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage));
|
||||
let signature = try!(sign(&secret, &message));
|
||||
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
let signature = sign(&secret, &message)?;
|
||||
Ok(format!("{}", signature))
|
||||
} else if args.cmd_verify {
|
||||
let signature = try!(args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature));
|
||||
let message = try!(args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage));
|
||||
let signature = args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature)?;
|
||||
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
let ok = if args.cmd_public {
|
||||
let public = try!(args.arg_public.parse().map_err(|_| EthkeyError::InvalidPublic));
|
||||
try!(verify_public(&public, &signature, &message))
|
||||
let public = args.arg_public.parse().map_err(|_| EthkeyError::InvalidPublic)?;
|
||||
verify_public(&public, &signature, &message)?
|
||||
} else if args.cmd_address {
|
||||
let address = try!(args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress));
|
||||
try!(verify_address(&address, &signature, &message))
|
||||
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
verify_address(&address, &signature, &message)?
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
|
@ -35,8 +35,8 @@ pub struct KeyPair {
|
||||
|
||||
impl fmt::Display for KeyPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
try!(writeln!(f, "secret: {}", self.secret.to_hex()));
|
||||
try!(writeln!(f, "public: {}", self.public.to_hex()));
|
||||
writeln!(f, "secret: {}", self.secret.to_hex())?;
|
||||
writeln!(f, "public: {}", self.public.to_hex())?;
|
||||
write!(f, "address: {}", self.address().to_hex())
|
||||
}
|
||||
}
|
||||
@ -45,8 +45,8 @@ impl KeyPair {
|
||||
/// Create a pair from secret key
|
||||
pub fn from_secret(secret: Secret) -> Result<KeyPair, Error> {
|
||||
let context = &SECP256K1;
|
||||
let s: key::SecretKey = try!(key::SecretKey::from_slice(context, &secret[..]));
|
||||
let pub_key = try!(key::PublicKey::from_secret_key(context, &s));
|
||||
let s: key::SecretKey = key::SecretKey::from_slice(context, &secret[..])?;
|
||||
let pub_key = key::PublicKey::from_secret_key(context, &s)?;
|
||||
let serialized = pub_key.serialize_vec(context, false);
|
||||
|
||||
let mut public = Public::default();
|
||||
|
@ -34,7 +34,7 @@ impl Prefix {
|
||||
impl Generator for Prefix {
|
||||
fn generate(self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let keypair = try!(Random.generate());
|
||||
let keypair = Random.generate()?;
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
return Ok(keypair)
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ pub struct Random;
|
||||
impl Generator for Random {
|
||||
fn generate(self) -> Result<KeyPair, Error> {
|
||||
let context = &SECP256K1;
|
||||
let mut rng = try!(OsRng::new());
|
||||
let (sec, publ) = try!(context.generate_keypair(&mut rng));
|
||||
let mut rng = OsRng::new()?;
|
||||
let (sec, publ) = context.generate_keypair(&mut rng)?;
|
||||
|
||||
Ok(KeyPair::from_keypair(sec, publ))
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
|
||||
let context = &SECP256K1;
|
||||
// no way to create from raw byte array.
|
||||
let sec: &SecretKey = unsafe { mem::transmute(secret) };
|
||||
let s = try!(context.sign_recoverable(&try!(SecpMessage::from_slice(&message[..])), sec));
|
||||
let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, sec)?;
|
||||
let (rec_id, data) = s.serialize_compact(context);
|
||||
let mut data_arr = [0; 65];
|
||||
|
||||
@ -183,7 +183,7 @@ pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
|
||||
|
||||
pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result<bool, Error> {
|
||||
let context = &SECP256K1;
|
||||
let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32))));
|
||||
let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?;
|
||||
let sig = rsig.to_standard(context);
|
||||
|
||||
let pdata: [u8; 65] = {
|
||||
@ -192,8 +192,8 @@ pub fn verify_public(public: &Public, signature: &Signature, message: &Message)
|
||||
temp
|
||||
};
|
||||
|
||||
let publ = try!(PublicKey::from_slice(context, &pdata));
|
||||
match context.verify(&try!(SecpMessage::from_slice(&message[..])), &sig, &publ) {
|
||||
let publ = PublicKey::from_slice(context, &pdata)?;
|
||||
match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(SecpError::IncorrectSignature) => Ok(false),
|
||||
Err(x) => Err(Error::from(x))
|
||||
@ -201,15 +201,15 @@ pub fn verify_public(public: &Public, signature: &Signature, message: &Message)
|
||||
}
|
||||
|
||||
pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result<bool, Error> {
|
||||
let public = try!(recover(signature, message));
|
||||
let public = recover(signature, message)?;
|
||||
let recovered_address = public_to_address(&public);
|
||||
Ok(address == &recovered_address)
|
||||
}
|
||||
|
||||
pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error> {
|
||||
let context = &SECP256K1;
|
||||
let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32))));
|
||||
let pubkey = try!(context.recover(&try!(SecpMessage::from_slice(&message[..])), &rsig));
|
||||
let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?;
|
||||
let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?;
|
||||
let serialized = pubkey.serialize_vec(context, false);
|
||||
|
||||
let mut public = Public::default();
|
||||
|
@ -113,7 +113,7 @@ impl Crypto {
|
||||
|
||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c),
|
||||
Kdf::Scrypt(ref params) => try!(crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)),
|
||||
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)?,
|
||||
};
|
||||
|
||||
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
|
||||
@ -171,22 +171,22 @@ impl SafeAccount {
|
||||
}
|
||||
|
||||
pub fn sign(&self, password: &str, message: &Message) -> Result<Signature, Error> {
|
||||
let secret = try!(self.crypto.secret(password));
|
||||
let secret = self.crypto.secret(password)?;
|
||||
sign(&secret, message).map_err(From::from)
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let secret = try!(self.crypto.secret(password));
|
||||
let secret = self.crypto.secret(password)?;
|
||||
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
}
|
||||
|
||||
pub fn public(&self, password: &str) -> Result<Public, Error> {
|
||||
let secret = try!(self.crypto.secret(password));
|
||||
Ok(try!(KeyPair::from_secret(secret)).public().clone())
|
||||
let secret = self.crypto.secret(password)?;
|
||||
Ok(KeyPair::from_secret(secret)?.public().clone())
|
||||
}
|
||||
|
||||
pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result<Self, Error> {
|
||||
let secret = try!(self.crypto.secret(old_password));
|
||||
let secret = self.crypto.secret(old_password)?;
|
||||
let result = SafeAccount {
|
||||
id: self.id.clone(),
|
||||
version: self.version.clone(),
|
||||
|
@ -93,11 +93,11 @@ fn main() {
|
||||
|
||||
fn key_dir(location: &str) -> Result<Box<KeyDirectory>, Error> {
|
||||
let dir: Box<KeyDirectory> = match location {
|
||||
"parity" => Box::new(try!(ParityDirectory::create(DirectoryType::Main))),
|
||||
"parity-test" => Box::new(try!(ParityDirectory::create(DirectoryType::Testnet))),
|
||||
"geth" => Box::new(try!(GethDirectory::create(DirectoryType::Main))),
|
||||
"geth-test" => Box::new(try!(GethDirectory::create(DirectoryType::Testnet))),
|
||||
path => Box::new(try!(DiskDirectory::create(path))),
|
||||
"parity" => Box::new(ParityDirectory::create(DirectoryType::Main)?),
|
||||
"parity-test" => Box::new(ParityDirectory::create(DirectoryType::Testnet)?),
|
||||
"geth" => Box::new(GethDirectory::create(DirectoryType::Main)?),
|
||||
"geth-test" => Box::new(GethDirectory::create(DirectoryType::Testnet)?),
|
||||
path => Box::new(DiskDirectory::create(path)?),
|
||||
};
|
||||
|
||||
Ok(dir)
|
||||
@ -112,9 +112,9 @@ fn format_accounts(accounts: &[Address]) -> String {
|
||||
}
|
||||
|
||||
fn load_password(path: &str) -> Result<String, Error> {
|
||||
let mut file = try!(fs::File::open(path));
|
||||
let mut file = fs::File::open(path)?;
|
||||
let mut password = String::new();
|
||||
try!(file.read_to_string(&mut password));
|
||||
file.read_to_string(&mut password)?;
|
||||
// drop EOF
|
||||
let _ = password.pop();
|
||||
Ok(password)
|
||||
@ -125,48 +125,48 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
||||
.and_then(|d| d.argv(command).decode())
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
|
||||
let store = try!(EthStore::open(try!(key_dir(&args.flag_dir))));
|
||||
let store = EthStore::open(key_dir(&args.flag_dir)?)?;
|
||||
|
||||
return if args.cmd_insert {
|
||||
let secret = try!(args.arg_secret.parse().map_err(|_| Error::InvalidSecret));
|
||||
let password = try!(load_password(&args.arg_password));
|
||||
let address = try!(store.insert_account(secret, &password));
|
||||
let secret = args.arg_secret.parse().map_err(|_| Error::InvalidSecret)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let address = store.insert_account(secret, &password)?;
|
||||
Ok(format!("0x{:?}", address))
|
||||
} else if args.cmd_change_pwd {
|
||||
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||
let old_pwd = try!(load_password(&args.arg_old_pwd));
|
||||
let new_pwd = try!(load_password(&args.arg_new_pwd));
|
||||
let address = args.arg_address.parse().map_err(|_| Error::InvalidAccount)?;
|
||||
let old_pwd = load_password(&args.arg_old_pwd)?;
|
||||
let new_pwd = load_password(&args.arg_new_pwd)?;
|
||||
let ok = store.change_password(&address, &old_pwd, &new_pwd).is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_list {
|
||||
let accounts = try!(store.accounts());
|
||||
let accounts = store.accounts()?;
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import {
|
||||
let src = try!(key_dir(&args.flag_src));
|
||||
let dst = try!(key_dir(&args.flag_dir));
|
||||
let accounts = try!(import_accounts(&*src, &*dst));
|
||||
let src = key_dir(&args.flag_src)?;
|
||||
let dst = key_dir(&args.flag_dir)?;
|
||||
let accounts = import_accounts(&*src, &*dst)?;
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import_wallet {
|
||||
let wallet = try!(PresaleWallet::open(&args.arg_path));
|
||||
let password = try!(load_password(&args.arg_password));
|
||||
let kp = try!(wallet.decrypt(&password));
|
||||
let address = try!(store.insert_account(kp.secret().clone(), &password));
|
||||
let wallet = PresaleWallet::open(&args.arg_path)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let kp = wallet.decrypt(&password)?;
|
||||
let address = store.insert_account(kp.secret().clone(), &password)?;
|
||||
Ok(format!("0x{:?}", address))
|
||||
} else if args.cmd_remove {
|
||||
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||
let password = try!(load_password(&args.arg_password));
|
||||
let address = args.arg_address.parse().map_err(|_| Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let ok = store.remove_account(&address, &password).is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_sign {
|
||||
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||
let message = try!(args.arg_message.parse().map_err(|_| Error::InvalidMessage));
|
||||
let password = try!(load_password(&args.arg_password));
|
||||
let signature = try!(store.sign(&address, &password, &message));
|
||||
let address = args.arg_address.parse().map_err(|_| Error::InvalidAccount)?;
|
||||
let message = args.arg_message.parse().map_err(|_| Error::InvalidMessage)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let signature = store.sign(&address, &password, &message)?;
|
||||
Ok(format!("0x{:?}", signature))
|
||||
} else if args.cmd_public {
|
||||
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||
let password = try!(load_password(&args.arg_password));
|
||||
let public = try!(store.public(&address, &password));
|
||||
let address = args.arg_address.parse().map_err(|_| Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let public = store.public(&address, &password)?;
|
||||
Ok(format!("0x{:?}", public))
|
||||
} else {
|
||||
Ok(format!("{}", USAGE))
|
||||
|
@ -29,8 +29,8 @@ fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> {
|
||||
use std::ffi;
|
||||
use libc;
|
||||
|
||||
let cstr = try!(ffi::CString::new(&*file_path.to_string_lossy())
|
||||
.map_err(|_| -1));
|
||||
let cstr = ffi::CString::new(&*file_path.to_string_lossy())
|
||||
.map_err(|_| -1)?;
|
||||
match unsafe { libc::chmod(cstr.as_ptr(), libc::S_IWUSR | libc::S_IRUSR) } {
|
||||
0 => Ok(()),
|
||||
x => Err(x),
|
||||
@ -48,7 +48,7 @@ pub struct DiskDirectory {
|
||||
|
||||
impl DiskDirectory {
|
||||
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
|
||||
try!(fs::create_dir_all(&path));
|
||||
fs::create_dir_all(&path)?;
|
||||
Ok(Self::at(path))
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ impl DiskDirectory {
|
||||
fn files(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
|
||||
// it's not done using one iterator cause
|
||||
// there is an issue with rustc and it takes tooo much time to compile
|
||||
let paths = try!(fs::read_dir(&self.path))
|
||||
let paths = fs::read_dir(&self.path)?
|
||||
.flat_map(Result::ok)
|
||||
.filter(|entry| {
|
||||
let metadata = entry.metadata().ok();
|
||||
@ -102,7 +102,7 @@ impl DiskDirectory {
|
||||
|
||||
impl KeyDirectory for DiskDirectory {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
let accounts = try!(self.files())
|
||||
let accounts = self.files()?
|
||||
.into_iter()
|
||||
.map(|(_, account)| account)
|
||||
.collect();
|
||||
@ -134,8 +134,8 @@ impl KeyDirectory for DiskDirectory {
|
||||
keyfile_path.push(filename.as_str());
|
||||
|
||||
// save the file
|
||||
let mut file = try!(fs::File::create(&keyfile_path));
|
||||
try!(keyfile.write(&mut file).map_err(|e| Error::Custom(format!("{:?}", e))));
|
||||
let mut file = fs::File::create(&keyfile_path)?;
|
||||
keyfile.write(&mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
|
||||
if let Err(_) = restrict_permissions_to_owner(keyfile_path.as_path()) {
|
||||
fs::remove_file(keyfile_path).expect("Expected to remove recently created file");
|
||||
@ -149,7 +149,7 @@ impl KeyDirectory for DiskDirectory {
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
// enumerate all entries in keystore
|
||||
// and find entry with given address
|
||||
let to_remove = try!(self.files())
|
||||
let to_remove = self.files()?
|
||||
.into_iter()
|
||||
.find(|&(_, ref acc)| acc == account);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user