Merge remote-tracking branch 'parity/master' into bft
This commit is contained in:
commit
2f3d162f57
@ -1,5 +1,6 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: "3"
|
GIT_DEPTH: "3"
|
||||||
@ -17,8 +18,10 @@ linux-beta:
|
|||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
script:
|
script:
|
||||||
|
- export
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
|
- cp target/release/parity parity
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-beta
|
- rust-beta
|
||||||
@ -26,24 +29,12 @@ linux-beta:
|
|||||||
paths:
|
paths:
|
||||||
- target/release/parity
|
- target/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "${CI_BUILD_NAME}_parity"
|
||||||
linux-stable:
|
stage: deploy
|
||||||
stage: build
|
|
||||||
image: ethcore/rust:stable
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
script:
|
|
||||||
- cargo build --release --verbose
|
|
||||||
- strip target/release/parity
|
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-stable
|
- rust-beta
|
||||||
artifacts:
|
script:
|
||||||
paths:
|
- ./deploy.sh
|
||||||
- target/release/parity
|
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
|
||||||
linux-nightly:
|
linux-nightly:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust:nightly
|
image: ethcore/rust:nightly
|
||||||
@ -92,6 +83,12 @@ linux-armv7:
|
|||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
script:
|
script:
|
||||||
|
- export
|
||||||
|
- rm -rf .cargo
|
||||||
|
- mkdir -p .cargo
|
||||||
|
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
||||||
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
|
- cat .cargo/config
|
||||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
||||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||||
tags:
|
tags:
|
||||||
@ -110,6 +107,12 @@ linux-arm:
|
|||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
script:
|
script:
|
||||||
|
- export
|
||||||
|
- rm -rf .cargo
|
||||||
|
- mkdir -p .cargo
|
||||||
|
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
||||||
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
|
- cat .cargo/config
|
||||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
||||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
tags:
|
tags:
|
||||||
@ -129,6 +132,12 @@ linux-armv6:
|
|||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
script:
|
script:
|
||||||
|
- export
|
||||||
|
- rm -rf .cargo
|
||||||
|
- mkdir -p .cargo
|
||||||
|
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
||||||
|
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||||
|
- cat .cargo/config
|
||||||
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
||||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||||
tags:
|
tags:
|
||||||
@ -148,6 +157,12 @@ linux-aarch64:
|
|||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
script:
|
script:
|
||||||
|
- export
|
||||||
|
- rm -rf .cargo
|
||||||
|
- mkdir -p .cargo
|
||||||
|
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
||||||
|
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||||
|
- cat .cargo/config
|
||||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
||||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||||
tags:
|
tags:
|
||||||
@ -193,3 +208,30 @@ windows:
|
|||||||
- target/release/parity.exe
|
- target/release/parity.exe
|
||||||
- target/release/parity.pdb
|
- target/release/parity.pdb
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "${CI_BUILD_NAME}_parity"
|
||||||
|
linux-stable:
|
||||||
|
stage: build
|
||||||
|
image: ethcore/rust:stable
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
|
script:
|
||||||
|
- export
|
||||||
|
- cargo build --release --verbose
|
||||||
|
- strip target/release/parity
|
||||||
|
tags:
|
||||||
|
- rust
|
||||||
|
- rust-stable
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- target/release/parity
|
||||||
|
name: "${CI_BUILD_NAME}_parity"
|
||||||
|
test-linux:
|
||||||
|
stage: test
|
||||||
|
before_script:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
script:
|
||||||
|
- ./test.sh --verbose
|
||||||
|
dependencies:
|
||||||
|
- linux-stable
|
||||||
|
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -295,6 +295,7 @@ dependencies = [
|
|||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
|
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
@ -793,6 +794,11 @@ name = "libc"
|
|||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@ -1699,6 +1705,7 @@ dependencies = [
|
|||||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||||
"checksum libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "97def9dc7ce1d8e153e693e3a33020bc69972181adb2f871e87e888876feae49"
|
"checksum libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "97def9dc7ce1d8e153e693e3a33020bc69972181adb2f871e87e888876feae49"
|
||||||
|
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
||||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||||
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
||||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||||
|
16
README.md
16
README.md
@ -84,9 +84,21 @@ $ cargo build --release
|
|||||||
|
|
||||||
This will produce an executable in the `./target/release` subdirectory.
|
This will produce an executable in the `./target/release` subdirectory.
|
||||||
|
|
||||||
To get started, just run
|
## Start Parity
|
||||||
|
### Manually
|
||||||
|
To start Parity manually, just run
|
||||||
```bash
|
```bash
|
||||||
$ ./target/release/parity
|
$ ./target/release/parity
|
||||||
```
|
```
|
||||||
|
|
||||||
and parity will begin syncing the Ethereum blockchain.
|
and Parity will begin syncing the Ethereum blockchain.
|
||||||
|
|
||||||
|
### Using systemd service file
|
||||||
|
To start Parity as a regular user using systemd init:
|
||||||
|
|
||||||
|
1. Copy ```parity/scripts/parity.service``` to your
|
||||||
|
systemd user directory (usually ```~/.config/systemd/user```).
|
||||||
|
2. To pass any argument to Parity, write a ```~/.parity/parity.conf``` file this way:
|
||||||
|
```ARGS="ARG1 ARG2 ARG3"```.
|
||||||
|
|
||||||
|
Example: ```ARGS="ui --geth --identity MyMachine"```.
|
||||||
|
@ -22,6 +22,7 @@ serde_json = "0.7.0"
|
|||||||
serde_macros = { version = "0.7.0", optional = true }
|
serde_macros = { version = "0.7.0", optional = true }
|
||||||
zip = { version = "0.1", default-features = false }
|
zip = { version = "0.1", default-features = false }
|
||||||
ethabi = "0.2.1"
|
ethabi = "0.2.1"
|
||||||
|
linked-hash-map = "0.3"
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
|
128
dapps/src/apps/cache.rs
Normal file
128
dapps/src/apps/cache.rs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Fetchable Dapps support.
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::sync::{Arc};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
use linked_hash_map::LinkedHashMap;
|
||||||
|
use page::LocalPageEndpoint;
|
||||||
|
|
||||||
|
pub enum ContentStatus {
|
||||||
|
Fetching(Arc<AtomicBool>),
|
||||||
|
Ready(LocalPageEndpoint),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ContentCache {
|
||||||
|
cache: LinkedHashMap<String, ContentStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentCache {
|
||||||
|
pub fn insert(&mut self, content_id: String, status: ContentStatus) -> Option<ContentStatus> {
|
||||||
|
self.cache.insert(content_id, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, content_id: &str) -> Option<ContentStatus> {
|
||||||
|
self.cache.remove(content_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, content_id: &str) -> Option<&mut ContentStatus> {
|
||||||
|
self.cache.get_refresh(content_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
||||||
|
let mut len = self.cache.len();
|
||||||
|
|
||||||
|
if len <= expected_size {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut removed = Vec::with_capacity(len - expected_size);
|
||||||
|
while len > expected_size {
|
||||||
|
let entry = self.cache.pop_front().unwrap();
|
||||||
|
match entry.1 {
|
||||||
|
ContentStatus::Fetching(ref abort) => {
|
||||||
|
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
||||||
|
// Mark as aborted
|
||||||
|
abort.store(true, Ordering::Relaxed);
|
||||||
|
},
|
||||||
|
ContentStatus::Ready(ref endpoint) => {
|
||||||
|
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
||||||
|
// Remove path
|
||||||
|
let res = fs::remove_dir_all(&endpoint.path());
|
||||||
|
if let Err(e) = res {
|
||||||
|
warn!(target: "dapps", "Unable to remove dapp: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removed.push(entry);
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
removed
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.cache.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn only_keys(data: Vec<(String, ContentStatus)>) -> Vec<String> {
|
||||||
|
data.into_iter().map(|x| x.0).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_remove_least_recently_used() {
|
||||||
|
// given
|
||||||
|
let mut cache = ContentCache::default();
|
||||||
|
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
|
||||||
|
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
|
||||||
|
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = cache.clear_garbage(2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(cache.len(), 2);
|
||||||
|
assert_eq!(only_keys(res), vec!["a"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_update_lru_if_accessed() {
|
||||||
|
// given
|
||||||
|
let mut cache = ContentCache::default();
|
||||||
|
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
|
||||||
|
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
|
||||||
|
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
|
||||||
|
|
||||||
|
// when
|
||||||
|
cache.get("a");
|
||||||
|
let res = cache.clear_garbage(2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(cache.len(), 2);
|
||||||
|
assert_eq!(only_keys(res), vec!["b"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -23,7 +23,7 @@ use std::{fs, env};
|
|||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashMap;
|
use std::sync::atomic::{AtomicBool};
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
|
|
||||||
use hyper::Control;
|
use hyper::Control;
|
||||||
@ -33,20 +33,18 @@ use random_filename;
|
|||||||
use util::{Mutex, H256};
|
use util::{Mutex, H256};
|
||||||
use util::sha3::sha3;
|
use util::sha3::sha3;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use handlers::{ContentHandler, AppFetcherHandler, DappHandler};
|
use handlers::{ContentHandler, ContentFetcherHandler, ContentValidator};
|
||||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||||
|
use apps::cache::{ContentCache, ContentStatus};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||||
use apps::urlhint::{URLHintContract, URLHint};
|
use apps::urlhint::{URLHintContract, URLHint};
|
||||||
|
|
||||||
enum AppStatus {
|
const MAX_CACHED_DAPPS: usize = 10;
|
||||||
Fetching,
|
|
||||||
Ready(LocalPageEndpoint),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppFetcher<R: URLHint = URLHintContract> {
|
pub struct AppFetcher<R: URLHint = URLHintContract> {
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
resolver: R,
|
resolver: R,
|
||||||
dapps: Arc<Mutex<HashMap<String, AppStatus>>>,
|
dapps: Arc<Mutex<ContentCache>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: URLHint> Drop for AppFetcher<R> {
|
impl<R: URLHint> Drop for AppFetcher<R> {
|
||||||
@ -65,17 +63,17 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
AppFetcher {
|
AppFetcher {
|
||||||
dapps_path: dapps_path,
|
dapps_path: dapps_path,
|
||||||
resolver: resolver,
|
resolver: resolver,
|
||||||
dapps: Arc::new(Mutex::new(HashMap::new())),
|
dapps: Arc::new(Mutex::new(ContentCache::default())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn set_status(&self, app_id: &str, status: AppStatus) {
|
fn set_status(&self, app_id: &str, status: ContentStatus) {
|
||||||
self.dapps.lock().insert(app_id.to_owned(), status);
|
self.dapps.lock().insert(app_id.to_owned(), status);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, app_id: &str) -> bool {
|
pub fn contains(&self, app_id: &str) -> bool {
|
||||||
let dapps = self.dapps.lock();
|
let mut dapps = self.dapps.lock();
|
||||||
match dapps.get(app_id) {
|
match dapps.get(app_id) {
|
||||||
// Check if we already have the app
|
// Check if we already have the app
|
||||||
Some(_) => true,
|
Some(_) => true,
|
||||||
@ -95,11 +93,11 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
let status = dapps.get(&app_id);
|
let status = dapps.get(&app_id);
|
||||||
match status {
|
match status {
|
||||||
// Just server dapp
|
// Just server dapp
|
||||||
Some(&AppStatus::Ready(ref endpoint)) => {
|
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
||||||
(None, endpoint.to_handler(path))
|
(None, endpoint.to_handler(path))
|
||||||
},
|
},
|
||||||
// App is already being fetched
|
// App is already being fetched
|
||||||
Some(&AppStatus::Fetching) => {
|
Some(&mut ContentStatus::Fetching(_)) => {
|
||||||
(None, Box::new(ContentHandler::html(
|
(None, Box::new(ContentHandler::html(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
format!(
|
format!(
|
||||||
@ -111,11 +109,13 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
},
|
},
|
||||||
// We need to start fetching app
|
// We need to start fetching app
|
||||||
None => {
|
None => {
|
||||||
// TODO [todr] Keep only last N dapps available!
|
|
||||||
let app_hex = app_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
let app_hex = app_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||||
let app = self.resolver.resolve(app_hex).expect("to_handler is called only when `contains` returns true.");
|
let app = self.resolver.resolve(app_hex).expect("to_handler is called only when `contains` returns true.");
|
||||||
(Some(AppStatus::Fetching), Box::new(AppFetcherHandler::new(
|
let abort = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
(Some(ContentStatus::Fetching(abort.clone())), Box::new(ContentFetcherHandler::new(
|
||||||
app,
|
app,
|
||||||
|
abort,
|
||||||
control,
|
control,
|
||||||
path.using_dapps_domains,
|
path.using_dapps_domains,
|
||||||
DappInstaller {
|
DappInstaller {
|
||||||
@ -129,6 +129,7 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(status) = new_status {
|
if let Some(status) = new_status {
|
||||||
|
dapps.clear_garbage(MAX_CACHED_DAPPS);
|
||||||
dapps.insert(app_id, status);
|
dapps.insert(app_id, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ impl From<zip::result::ZipError> for ValidationError {
|
|||||||
struct DappInstaller {
|
struct DappInstaller {
|
||||||
dapp_id: String,
|
dapp_id: String,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
dapps: Arc<Mutex<HashMap<String, AppStatus>>>,
|
dapps: Arc<Mutex<ContentCache>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DappInstaller {
|
impl DappInstaller {
|
||||||
@ -196,7 +197,7 @@ impl DappInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DappHandler for DappInstaller {
|
impl ContentValidator for DappInstaller {
|
||||||
type Error = ValidationError;
|
type Error = ValidationError;
|
||||||
|
|
||||||
fn validate_and_install(&self, app_path: PathBuf) -> Result<Manifest, ValidationError> {
|
fn validate_and_install(&self, app_path: PathBuf) -> Result<Manifest, ValidationError> {
|
||||||
@ -262,7 +263,7 @@ impl DappHandler for DappInstaller {
|
|||||||
Some(manifest) => {
|
Some(manifest) => {
|
||||||
let path = self.dapp_target_path(manifest);
|
let path = self.dapp_target_path(manifest);
|
||||||
let app = LocalPageEndpoint::new(path, manifest.clone().into());
|
let app = LocalPageEndpoint::new(path, manifest.clone().into());
|
||||||
dapps.insert(self.dapp_id.clone(), AppStatus::Ready(app));
|
dapps.insert(self.dapp_id.clone(), ContentStatus::Ready(app));
|
||||||
},
|
},
|
||||||
// In case of error
|
// In case of error
|
||||||
None => {
|
None => {
|
||||||
@ -274,12 +275,13 @@ impl DappHandler for DappInstaller {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::path::PathBuf;
|
use std::env;
|
||||||
use super::{AppFetcher, AppStatus};
|
use util::Bytes;
|
||||||
use apps::urlhint::{GithubApp, URLHint};
|
|
||||||
use endpoint::EndpointInfo;
|
use endpoint::EndpointInfo;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use util::Bytes;
|
use apps::cache::ContentStatus;
|
||||||
|
use apps::urlhint::{GithubApp, URLHint};
|
||||||
|
use super::AppFetcher;
|
||||||
|
|
||||||
struct FakeResolver;
|
struct FakeResolver;
|
||||||
impl URLHint for FakeResolver {
|
impl URLHint for FakeResolver {
|
||||||
@ -291,8 +293,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_true_if_contains_the_app() {
|
fn should_true_if_contains_the_app() {
|
||||||
// given
|
// given
|
||||||
|
let path = env::temp_dir();
|
||||||
let fetcher = AppFetcher::new(FakeResolver);
|
let fetcher = AppFetcher::new(FakeResolver);
|
||||||
let handler = LocalPageEndpoint::new(PathBuf::from("/tmp/test"), EndpointInfo {
|
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
||||||
name: "fake".into(),
|
name: "fake".into(),
|
||||||
description: "".into(),
|
description: "".into(),
|
||||||
version: "".into(),
|
version: "".into(),
|
||||||
@ -301,8 +304,8 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
fetcher.set_status("test", AppStatus::Ready(handler));
|
fetcher.set_status("test", ContentStatus::Ready(handler));
|
||||||
fetcher.set_status("test2", AppStatus::Fetching);
|
fetcher.set_status("test2", ContentStatus::Fetching(Default::default()));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(fetcher.contains("test"), true);
|
assert_eq!(fetcher.contains("test"), true);
|
||||||
|
@ -19,6 +19,7 @@ use page::PageEndpoint;
|
|||||||
use proxypac::ProxyPac;
|
use proxypac::ProxyPac;
|
||||||
use parity_dapps::WebApp;
|
use parity_dapps::WebApp;
|
||||||
|
|
||||||
|
mod cache;
|
||||||
mod fs;
|
mod fs;
|
||||||
pub mod urlhint;
|
pub mod urlhint;
|
||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
use std::{env, io, fs, fmt};
|
use std::{env, io, fs, fmt};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc;
|
use std::sync::{mpsc, Arc};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use random_filename;
|
use random_filename;
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ use hyper::{self, Decoder, Encoder, Next};
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
Aborted,
|
||||||
NotStarted,
|
NotStarted,
|
||||||
UnexpectedStatus(StatusCode),
|
UnexpectedStatus(StatusCode),
|
||||||
IoError(io::Error),
|
IoError(io::Error),
|
||||||
@ -40,6 +42,7 @@ pub type OnDone = Box<Fn() + Send>;
|
|||||||
|
|
||||||
pub struct Fetch {
|
pub struct Fetch {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
abort: Arc<AtomicBool>,
|
||||||
file: Option<fs::File>,
|
file: Option<fs::File>,
|
||||||
result: Option<FetchResult>,
|
result: Option<FetchResult>,
|
||||||
sender: mpsc::Sender<FetchResult>,
|
sender: mpsc::Sender<FetchResult>,
|
||||||
@ -56,7 +59,7 @@ impl Drop for Fetch {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let res = self.result.take().unwrap_or(Err(Error::NotStarted));
|
let res = self.result.take().unwrap_or(Err(Error::NotStarted));
|
||||||
// Remove file if there was an error
|
// Remove file if there was an error
|
||||||
if res.is_err() {
|
if res.is_err() || self.is_aborted() {
|
||||||
if let Some(file) = self.file.take() {
|
if let Some(file) = self.file.take() {
|
||||||
drop(file);
|
drop(file);
|
||||||
// Remove file
|
// Remove file
|
||||||
@ -72,12 +75,13 @@ impl Drop for Fetch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fetch {
|
impl Fetch {
|
||||||
pub fn new(sender: mpsc::Sender<FetchResult>, on_done: OnDone) -> Self {
|
pub fn new(sender: mpsc::Sender<FetchResult>, abort: Arc<AtomicBool>, on_done: OnDone) -> Self {
|
||||||
let mut dir = env::temp_dir();
|
let mut dir = env::temp_dir();
|
||||||
dir.push(random_filename());
|
dir.push(random_filename());
|
||||||
|
|
||||||
Fetch {
|
Fetch {
|
||||||
path: dir,
|
path: dir,
|
||||||
|
abort: abort,
|
||||||
file: None,
|
file: None,
|
||||||
result: None,
|
result: None,
|
||||||
sender: sender,
|
sender: sender,
|
||||||
@ -86,17 +90,36 @@ impl Fetch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Fetch {
|
||||||
|
fn is_aborted(&self) -> bool {
|
||||||
|
self.abort.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
fn mark_aborted(&mut self) -> Next {
|
||||||
|
self.result = Some(Err(Error::Aborted));
|
||||||
|
Next::end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl hyper::client::Handler<HttpStream> for Fetch {
|
impl hyper::client::Handler<HttpStream> for Fetch {
|
||||||
fn on_request(&mut self, req: &mut Request) -> Next {
|
fn on_request(&mut self, req: &mut Request) -> Next {
|
||||||
|
if self.is_aborted() {
|
||||||
|
return self.mark_aborted();
|
||||||
|
}
|
||||||
req.headers_mut().set(Connection::close());
|
req.headers_mut().set(Connection::close());
|
||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
if self.is_aborted() {
|
||||||
|
return self.mark_aborted();
|
||||||
|
}
|
||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_response(&mut self, res: Response) -> Next {
|
fn on_response(&mut self, res: Response) -> Next {
|
||||||
|
if self.is_aborted() {
|
||||||
|
return self.mark_aborted();
|
||||||
|
}
|
||||||
if *res.status() != StatusCode::Ok {
|
if *res.status() != StatusCode::Ok {
|
||||||
self.result = Some(Err(Error::UnexpectedStatus(*res.status())));
|
self.result = Some(Err(Error::UnexpectedStatus(*res.status())));
|
||||||
return Next::end();
|
return Next::end();
|
||||||
@ -117,6 +140,9 @@ impl hyper::client::Handler<HttpStream> for Fetch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
|
if self.is_aborted() {
|
||||||
|
return self.mark_aborted();
|
||||||
|
}
|
||||||
match io::copy(decoder, self.file.as_mut().expect("File is there because on_response has created it.")) {
|
match io::copy(decoder, self.file.as_mut().expect("File is there because on_response has created it.")) {
|
||||||
Ok(0) => Next::end(),
|
Ok(0) => Next::end(),
|
||||||
Ok(_) => read(),
|
Ok(_) => read(),
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
use std::{fs, fmt};
|
use std::{fs, fmt};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc;
|
use std::sync::{mpsc, Arc};
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
use hyper::{header, server, Decoder, Encoder, Next, Method, Control, Client};
|
use hyper::{header, server, Decoder, Encoder, Next, Method, Control, Client};
|
||||||
@ -38,19 +39,20 @@ enum FetchState {
|
|||||||
Error(ContentHandler),
|
Error(ContentHandler),
|
||||||
InProgress {
|
InProgress {
|
||||||
deadline: Instant,
|
deadline: Instant,
|
||||||
receiver: mpsc::Receiver<FetchResult>
|
receiver: mpsc::Receiver<FetchResult>,
|
||||||
},
|
},
|
||||||
Done(Manifest),
|
Done(Manifest),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DappHandler {
|
pub trait ContentValidator {
|
||||||
type Error: fmt::Debug;
|
type Error: fmt::Debug;
|
||||||
|
|
||||||
fn validate_and_install(&self, app: PathBuf) -> Result<Manifest, Self::Error>;
|
fn validate_and_install(&self, app: PathBuf) -> Result<Manifest, Self::Error>;
|
||||||
fn done(&self, Option<&Manifest>);
|
fn done(&self, Option<&Manifest>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppFetcherHandler<H: DappHandler> {
|
pub struct ContentFetcherHandler<H: ContentValidator> {
|
||||||
|
abort: Arc<AtomicBool>,
|
||||||
control: Option<Control>,
|
control: Option<Control>,
|
||||||
status: FetchState,
|
status: FetchState,
|
||||||
client: Option<Client<Fetch>>,
|
client: Option<Client<Fetch>>,
|
||||||
@ -58,7 +60,7 @@ pub struct AppFetcherHandler<H: DappHandler> {
|
|||||||
dapp: H,
|
dapp: H,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: DappHandler> Drop for AppFetcherHandler<H> {
|
impl<H: ContentValidator> Drop for ContentFetcherHandler<H> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let manifest = match self.status {
|
let manifest = match self.status {
|
||||||
FetchState::Done(ref manifest) => Some(manifest),
|
FetchState::Done(ref manifest) => Some(manifest),
|
||||||
@ -68,16 +70,18 @@ impl<H: DappHandler> Drop for AppFetcherHandler<H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: DappHandler> AppFetcherHandler<H> {
|
impl<H: ContentValidator> ContentFetcherHandler<H> {
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
app: GithubApp,
|
app: GithubApp,
|
||||||
|
abort: Arc<AtomicBool>,
|
||||||
control: Control,
|
control: Control,
|
||||||
using_dapps_domains: bool,
|
using_dapps_domains: bool,
|
||||||
handler: H) -> Self {
|
handler: H) -> Self {
|
||||||
|
|
||||||
let client = Client::new().expect("Failed to create a Client");
|
let client = Client::new().expect("Failed to create a Client");
|
||||||
AppFetcherHandler {
|
ContentFetcherHandler {
|
||||||
|
abort: abort,
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
client: Some(client),
|
client: Some(client),
|
||||||
status: FetchState::NotStarted(app),
|
status: FetchState::NotStarted(app),
|
||||||
@ -94,12 +98,12 @@ impl<H: DappHandler> AppFetcherHandler<H> {
|
|||||||
|
|
||||||
|
|
||||||
// TODO [todr] https support
|
// TODO [todr] https support
|
||||||
fn fetch_app(client: &mut Client<Fetch>, app: &GithubApp, control: Control) -> Result<mpsc::Receiver<FetchResult>, String> {
|
fn fetch_app(client: &mut Client<Fetch>, app: &GithubApp, abort: Arc<AtomicBool>, control: Control) -> Result<mpsc::Receiver<FetchResult>, String> {
|
||||||
let url = try!(app.url().parse().map_err(|e| format!("{:?}", e)));
|
let url = try!(app.url().parse().map_err(|e| format!("{:?}", e)));
|
||||||
trace!(target: "dapps", "Fetching from: {:?}", url);
|
trace!(target: "dapps", "Fetching from: {:?}", url);
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let res = client.request(url, Fetch::new(tx, Box::new(move || {
|
let res = client.request(url, Fetch::new(tx, abort, Box::new(move || {
|
||||||
trace!(target: "dapps", "Fetching finished.");
|
trace!(target: "dapps", "Fetching finished.");
|
||||||
// Ignoring control errors
|
// Ignoring control errors
|
||||||
let _ = control.ready(Next::read());
|
let _ = control.ready(Next::read());
|
||||||
@ -111,7 +115,7 @@ impl<H: DappHandler> AppFetcherHandler<H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: DappHandler> server::Handler<HttpStream> for AppFetcherHandler<H> {
|
impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<H> {
|
||||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||||
let status = if let FetchState::NotStarted(ref app) = self.status {
|
let status = if let FetchState::NotStarted(ref app) = self.status {
|
||||||
Some(match *request.method() {
|
Some(match *request.method() {
|
||||||
@ -120,7 +124,7 @@ impl<H: DappHandler> server::Handler<HttpStream> for AppFetcherHandler<H> {
|
|||||||
trace!(target: "dapps", "Fetching dapp: {:?}", app);
|
trace!(target: "dapps", "Fetching dapp: {:?}", app);
|
||||||
let control = self.control.take().expect("on_request is called only once, thus control is always Some");
|
let control = self.control.take().expect("on_request is called only once, thus control is always Some");
|
||||||
let client = self.client.as_mut().expect("on_request is called before client is closed.");
|
let client = self.client.as_mut().expect("on_request is called before client is closed.");
|
||||||
let fetch = Self::fetch_app(client, app, control);
|
let fetch = Self::fetch_app(client, app, self.abort.clone(), control);
|
||||||
match fetch {
|
match fetch {
|
||||||
Ok(receiver) => FetchState::InProgress {
|
Ok(receiver) => FetchState::InProgress {
|
||||||
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
|
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
|
||||||
|
@ -27,7 +27,7 @@ pub use self::auth::AuthRequiredHandler;
|
|||||||
pub use self::echo::EchoHandler;
|
pub use self::echo::EchoHandler;
|
||||||
pub use self::content::ContentHandler;
|
pub use self::content::ContentHandler;
|
||||||
pub use self::redirect::Redirection;
|
pub use self::redirect::Redirection;
|
||||||
pub use self::fetch::{AppFetcherHandler, DappHandler};
|
pub use self::fetch::{ContentFetcherHandler, ContentValidator};
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use hyper::{server, header, net, uri};
|
use hyper::{server, header, net, uri};
|
||||||
|
@ -42,7 +42,8 @@ impl server::Handler<HttpStream> for Redirection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(StatusCode::MovedPermanently);
|
// Don't use `MovedPermanently` here to prevent browser from caching the redirections.
|
||||||
|
res.set_status(StatusCode::Found);
|
||||||
res.headers_mut().set(header::Location(self.to_url.to_owned()));
|
res.headers_mut().set(header::Location(self.to_url.to_owned()));
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ extern crate rustc_serialize;
|
|||||||
extern crate parity_dapps;
|
extern crate parity_dapps;
|
||||||
extern crate ethcore_rpc;
|
extern crate ethcore_rpc;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
|
extern crate linked_hash_map;
|
||||||
|
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod apps;
|
mod apps;
|
||||||
@ -70,6 +71,8 @@ mod rpc;
|
|||||||
mod api;
|
mod api;
|
||||||
mod proxypac;
|
mod proxypac;
|
||||||
mod url;
|
mod url;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
pub use self::apps::urlhint::ContractClient;
|
pub use self::apps::urlhint::ContractClient;
|
||||||
|
|
||||||
@ -108,14 +111,28 @@ impl ServerBuilder {
|
|||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// returns result with `Server` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
Server::start_http(
|
||||||
|
addr,
|
||||||
|
hosts,
|
||||||
|
NoAuth,
|
||||||
|
self.handler.clone(),
|
||||||
|
self.dapps_path.clone(),
|
||||||
|
self.registrar.clone()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||||
/// return result with `Server` handle on success or an error.
|
/// return result with `Server` handle on success or an error.
|
||||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, 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, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
Server::start_http(
|
||||||
|
addr,
|
||||||
|
hosts,
|
||||||
|
HttpBasicAuth::single_user(username, password),
|
||||||
|
self.handler.clone(),
|
||||||
|
self.dapps_path.clone(),
|
||||||
|
self.registrar.clone()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +143,24 @@ pub struct Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
|
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
|
||||||
|
fn allowed_hosts(hosts: Option<Vec<String>>, bind_address: String) -> Option<Vec<String>> {
|
||||||
|
let mut allowed = Vec::new();
|
||||||
|
|
||||||
|
match hosts {
|
||||||
|
Some(hosts) => allowed.extend_from_slice(&hosts),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add localhost domain as valid too if listening on loopback interface.
|
||||||
|
allowed.push(bind_address.replace("127.0.0.1", "localhost").into());
|
||||||
|
allowed.push(bind_address.into());
|
||||||
|
Some(allowed)
|
||||||
|
}
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static>(
|
fn start_http<A: Authorization + 'static>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
|
hosts: Option<Vec<String>>,
|
||||||
authorization: A,
|
authorization: A,
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
@ -144,7 +177,7 @@ impl Server {
|
|||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
let bind_address = format!("{}", addr);
|
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||||
|
|
||||||
try!(hyper::Server::http(addr))
|
try!(hyper::Server::http(addr))
|
||||||
.handle(move |ctrl| router::Router::new(
|
.handle(move |ctrl| router::Router::new(
|
||||||
@ -154,7 +187,7 @@ impl Server {
|
|||||||
endpoints.clone(),
|
endpoints.clone(),
|
||||||
special.clone(),
|
special.clone(),
|
||||||
authorization.clone(),
|
authorization.clone(),
|
||||||
bind_address.clone(),
|
hosts.clone(),
|
||||||
))
|
))
|
||||||
.map(|(l, srv)| {
|
.map(|(l, srv)| {
|
||||||
|
|
||||||
@ -174,6 +207,12 @@ impl Server {
|
|||||||
pub fn set_panic_handler<F>(&self, handler: F) where F : Fn() -> () + Send + 'static {
|
pub fn set_panic_handler<F>(&self, handler: F) where F : Fn() -> () + Send + 'static {
|
||||||
*self.panic_handler.lock().unwrap() = Some(Box::new(handler));
|
*self.panic_handler.lock().unwrap() = Some(Box::new(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Returns address that this server is bound to.
|
||||||
|
pub fn addr(&self) -> &SocketAddr {
|
||||||
|
self.server.as_ref().expect("server is always Some at the start; it's consumed only when object is dropped; qed").addr()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Server {
|
impl Drop for Server {
|
||||||
@ -207,3 +246,23 @@ pub fn random_filename() -> String {
|
|||||||
rng.gen_ascii_chars().take(12).collect()
|
rng.gen_ascii_chars().take(12).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod util_tests {
|
||||||
|
use super::Server;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_allowed_hosts() {
|
||||||
|
// given
|
||||||
|
let bind_address = "127.0.0.1".to_owned();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let all = Server::allowed_hosts(None, bind_address.clone());
|
||||||
|
let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone());
|
||||||
|
let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone());
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(all, None);
|
||||||
|
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
||||||
|
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,6 +33,10 @@ impl LocalPageEndpoint {
|
|||||||
info: info,
|
info: info,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> PathBuf {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for LocalPageEndpoint {
|
impl Endpoint for LocalPageEndpoint {
|
||||||
|
@ -22,13 +22,11 @@ use hyper::net::HttpStream;
|
|||||||
use jsonrpc_http_server::{is_host_header_valid};
|
use jsonrpc_http_server::{is_host_header_valid};
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
|
|
||||||
pub fn is_valid(request: &server::Request<HttpStream>, bind_address: &str, endpoints: Vec<String>) -> bool {
|
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
|
||||||
let mut endpoints = endpoints.into_iter()
|
let mut endpoints = endpoints.iter()
|
||||||
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
// Add localhost domain as valid too if listening on loopback interface.
|
endpoints.extend_from_slice(allowed_hosts);
|
||||||
endpoints.push(bind_address.replace("127.0.0.1", "localhost").into());
|
|
||||||
endpoints.push(bind_address.into());
|
|
||||||
|
|
||||||
let header_valid = is_host_header_valid(request, &endpoints);
|
let header_valid = is_host_header_valid(request, &endpoints);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ pub struct Router<A: Authorization + 'static> {
|
|||||||
fetch: Arc<AppFetcher>,
|
fetch: Arc<AppFetcher>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
bind_address: String,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
handler: Box<server::Handler<HttpStream> + Send>,
|
handler: Box<server::Handler<HttpStream> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,10 +56,12 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
|
|
||||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||||
// Validate Host header
|
// Validate Host header
|
||||||
if !host_validation::is_valid(&req, &self.bind_address, self.endpoints.keys().cloned().collect()) {
|
if let Some(ref hosts) = self.allowed_hosts {
|
||||||
|
if !host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()) {
|
||||||
self.handler = host_validation::host_invalid_response();
|
self.handler = host_validation::host_invalid_response();
|
||||||
return self.handler.on_request(req);
|
return self.handler.on_request(req);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check authorization
|
// Check authorization
|
||||||
let auth = self.authorization.is_authorized(&req);
|
let auth = self.authorization.is_authorized(&req);
|
||||||
@ -81,7 +83,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||||
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
|
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
|
||||||
},
|
},
|
||||||
// Try to resolve and fetch dapp
|
// 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) => {
|
||||||
let control = self.control.take().expect("on_request is called only once, thus control is always defined.");
|
let control = self.control.take().expect("on_request is called only once, thus control is always defined.");
|
||||||
self.fetch.to_handler(path.clone(), control)
|
self.fetch.to_handler(path.clone(), control)
|
||||||
@ -91,6 +93,11 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
||||||
Redirection::new(address.as_str())
|
Redirection::new(address.as_str())
|
||||||
},
|
},
|
||||||
|
// Redirect any GET request to home.
|
||||||
|
_ if *req.method() == hyper::method::Method::Get => {
|
||||||
|
let address = apps::redirection_address(false, self.main_page);
|
||||||
|
Redirection::new(address.as_str())
|
||||||
|
},
|
||||||
// RPC by default
|
// RPC by default
|
||||||
_ => {
|
_ => {
|
||||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
|
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
|
||||||
@ -125,7 +132,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
bind_address: String,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
||||||
@ -136,7 +143,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
fetch: app_fetcher,
|
fetch: app_fetcher,
|
||||||
special: special,
|
special: special,
|
||||||
authorization: authorization,
|
authorization: authorization,
|
||||||
bind_address: bind_address,
|
allowed_hosts: allowed_hosts,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
84
dapps/src/tests/api.rs
Normal file
84
dapps/src/tests/api.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use tests::helpers::{serve, request};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_error() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /api/empty HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||||
|
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serve_apps() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /api/apps HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||||
|
assert!(response.body.contains("Parity Home Screen"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_handle_ping() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /api/ping HTTP/1.1\r\n\
|
||||||
|
Host: home.parity\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||||
|
assert_eq!(response.body, "0\n\n".to_owned());
|
||||||
|
}
|
||||||
|
|
79
dapps/src/tests/authorization.rs
Normal file
79
dapps/src/tests/authorization.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use tests::helpers::{serve_with_auth, request};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_require_authorization() {
|
||||||
|
// given
|
||||||
|
let server = serve_with_auth("test", "test");
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "WWW-Authenticate: Basic realm=\"Parity\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_reject_on_invalid_auth() {
|
||||||
|
// given
|
||||||
|
let server = serve_with_auth("test", "test");
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
|
||||||
|
assert_eq!(response.body, "15\n<h1>Unauthorized</h1>\n0\n\n".to_owned());
|
||||||
|
assert_eq!(response.headers_raw.contains("WWW-Authenticate"), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_allow_on_valid_auth() {
|
||||||
|
// given
|
||||||
|
let server = serve_with_auth("Aladdin", "OpenSesame");
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /home/ HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
}
|
123
dapps/src/tests/helpers.rs
Normal file
123
dapps/src/tests/helpers.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::str::{self, Lines};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use rustc_serialize::hex::{ToHex, FromHex};
|
||||||
|
|
||||||
|
use ServerBuilder;
|
||||||
|
use Server;
|
||||||
|
use apps::urlhint::ContractClient;
|
||||||
|
use util::{Bytes, Address, Mutex, ToPretty};
|
||||||
|
|
||||||
|
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||||
|
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serve_hosts(hosts: Option<Vec<String>>) -> 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 builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar);
|
||||||
|
builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serve_with_auth(user: &str, pass: &str) -> Server {
|
||||||
|
let registrar = Arc::new(FakeRegistrar::new());
|
||||||
|
let builder = ServerBuilder::new(env::temp_dir().to_str().unwrap().into(), registrar);
|
||||||
|
builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serve() -> Server {
|
||||||
|
serve_hosts(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Response {
|
||||||
|
pub status: String,
|
||||||
|
pub headers: Vec<String>,
|
||||||
|
pub headers_raw: String,
|
||||||
|
pub body: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_block(lines: &mut Lines, all: bool) -> String {
|
||||||
|
let mut block = String::new();
|
||||||
|
loop {
|
||||||
|
let line = lines.next();
|
||||||
|
match line {
|
||||||
|
None => break,
|
||||||
|
Some("") if !all => break,
|
||||||
|
Some(v) => {
|
||||||
|
block.push_str(v);
|
||||||
|
block.push_str("\n");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(server: Server, request: &str) -> Response {
|
||||||
|
let mut req = TcpStream::connect(server.addr()).unwrap();
|
||||||
|
req.write_all(request.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let mut response = String::new();
|
||||||
|
req.read_to_string(&mut response).unwrap();
|
||||||
|
|
||||||
|
let mut lines = response.lines();
|
||||||
|
let status = lines.next().unwrap().to_owned();
|
||||||
|
let headers_raw = read_block(&mut lines, false);
|
||||||
|
let headers = headers_raw.split('\n').map(|v| v.to_owned()).collect();
|
||||||
|
let body = read_block(&mut lines, true);
|
||||||
|
|
||||||
|
Response {
|
||||||
|
status: status,
|
||||||
|
headers: headers,
|
||||||
|
headers_raw: headers_raw,
|
||||||
|
body: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
dapps/src/tests/mod.rs
Normal file
25
dapps/src/tests/mod.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Dapps server test suite
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
mod authorization;
|
||||||
|
mod redirection;
|
||||||
|
mod validation;
|
||||||
|
|
185
dapps/src/tests/redirection.rs
Normal file
185
dapps/src/tests/redirection.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use tests::helpers::{serve, request};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_redirect_to_home() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_redirect_to_home_when_trailing_slash_is_missing() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /app HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_redirect_to_home_on_invalid_dapp() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /invaliddapp/ HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_redirect_to_home_on_invalid_dapp_with_domain() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: invaliddapp.parity\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Location: http://home.parity/");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serve_rpc() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST / HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
Content-Type: application/json\r\n
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_eq!(response.body, format!("57\n{}\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serve_rpc_at_slash_rpc() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /rpc HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
Content-Type: application/json\r\n
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_eq!(response.body, format!("57\n{}\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serve_proxy_pac() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /proxy/proxy.pac HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_eq!(response.body, "86\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serve_utils() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /parity-utils/inject.js HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_eq!(response.body.contains("function(){"), true);
|
||||||
|
}
|
||||||
|
|
79
dapps/src/tests/validation.rs
Normal file
79
dapps/src/tests/validation.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use tests::helpers::{serve_hosts, request};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_reject_invalid_host() {
|
||||||
|
// given
|
||||||
|
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
|
||||||
|
assert_eq!(response.body, "85\n\n\t\t<h1>Request with disallowed <code>Host</code> header has been blocked.</h1>\n\t\t<p>Check the URL in your browser address bar.</p>\n\t\t\n0\n\n".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_allow_valid_host() {
|
||||||
|
// given
|
||||||
|
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /home/ HTTP/1.1\r\n\
|
||||||
|
Host: localhost:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serve_dapps_domains() {
|
||||||
|
// given
|
||||||
|
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: home.parity\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
}
|
||||||
|
|
@ -245,11 +245,11 @@ impl<'x> OpenBlock<'x> {
|
|||||||
last_hashes: last_hashes,
|
last_hashes: last_hashes,
|
||||||
};
|
};
|
||||||
|
|
||||||
r.block.base.header.parent_hash = parent.hash();
|
r.block.base.header.set_parent_hash(parent.hash());
|
||||||
r.block.base.header.number = parent.number + 1;
|
r.block.base.header.set_number(parent.number() + 1);
|
||||||
r.block.base.header.author = author;
|
r.block.base.header.set_author(author);
|
||||||
r.block.base.header.set_timestamp_now(parent.timestamp());
|
r.block.base.header.set_timestamp_now(parent.timestamp());
|
||||||
r.block.base.header.extra_data = extra_data;
|
r.block.base.header.set_extra_data(extra_data);
|
||||||
r.block.base.header.note_dirty();
|
r.block.base.header.note_dirty();
|
||||||
|
|
||||||
engine.populate_from_parent(&mut r.block.base.header, parent, gas_range_target.0, gas_range_target.1);
|
engine.populate_from_parent(&mut r.block.base.header, parent, gas_range_target.0, gas_range_target.1);
|
||||||
@ -309,13 +309,13 @@ impl<'x> OpenBlock<'x> {
|
|||||||
pub fn env_info(&self) -> EnvInfo {
|
pub fn env_info(&self) -> EnvInfo {
|
||||||
// TODO: memoise.
|
// TODO: memoise.
|
||||||
EnvInfo {
|
EnvInfo {
|
||||||
number: self.block.base.header.number,
|
number: self.block.base.header.number(),
|
||||||
author: self.block.base.header.author.clone(),
|
author: self.block.base.header.author().clone(),
|
||||||
timestamp: self.block.base.header.timestamp,
|
timestamp: self.block.base.header.timestamp(),
|
||||||
difficulty: self.block.base.header.difficulty.clone(),
|
difficulty: self.block.base.header.difficulty().clone(),
|
||||||
last_hashes: self.last_hashes.clone(),
|
last_hashes: self.last_hashes.clone(),
|
||||||
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
|
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
|
||||||
gas_limit: self.block.base.header.gas_limit.clone(),
|
gas_limit: self.block.base.header.gas_limit().clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,14 +349,13 @@ impl<'x> OpenBlock<'x> {
|
|||||||
let unclosed_state = s.block.state.clone();
|
let unclosed_state = s.block.state.clone();
|
||||||
|
|
||||||
s.engine.on_close_block(&mut s.block);
|
s.engine.on_close_block(&mut s.block);
|
||||||
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect());
|
s.block.base.header.set_transactions_root(ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect()));
|
||||||
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
||||||
s.block.base.header.uncles_hash = uncle_bytes.sha3();
|
s.block.base.header.set_uncles_hash(uncle_bytes.sha3());
|
||||||
s.block.base.header.state_root = s.block.state.root().clone();
|
s.block.base.header.set_state_root(s.block.state.root().clone());
|
||||||
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect());
|
s.block.base.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect()));
|
||||||
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator
|
s.block.base.header.set_log_bloom(s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator
|
||||||
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
|
s.block.base.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used));
|
||||||
s.block.base.header.note_dirty();
|
|
||||||
|
|
||||||
ClosedBlock {
|
ClosedBlock {
|
||||||
block: s.block,
|
block: s.block,
|
||||||
@ -371,20 +370,19 @@ impl<'x> OpenBlock<'x> {
|
|||||||
let mut s = self;
|
let mut s = self;
|
||||||
|
|
||||||
s.engine.on_close_block(&mut s.block);
|
s.engine.on_close_block(&mut s.block);
|
||||||
if s.block.base.header.transactions_root.is_zero() || s.block.base.header.transactions_root == SHA3_NULL_RLP {
|
if s.block.base.header.transactions_root().is_zero() || s.block.base.header.transactions_root() == &SHA3_NULL_RLP {
|
||||||
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect());
|
s.block.base.header.set_transactions_root(ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect()));
|
||||||
}
|
}
|
||||||
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
||||||
if s.block.base.header.uncles_hash.is_zero() {
|
if s.block.base.header.uncles_hash().is_zero() {
|
||||||
s.block.base.header.uncles_hash = uncle_bytes.sha3();
|
s.block.base.header.set_uncles_hash(uncle_bytes.sha3());
|
||||||
}
|
}
|
||||||
if s.block.base.header.receipts_root.is_zero() || s.block.base.header.receipts_root == SHA3_NULL_RLP {
|
if s.block.base.header.receipts_root().is_zero() || s.block.base.header.receipts_root() == &SHA3_NULL_RLP {
|
||||||
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect());
|
s.block.base.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect()));
|
||||||
}
|
}
|
||||||
s.block.base.header.state_root = s.block.state.root().clone();
|
s.block.base.header.set_state_root(s.block.state.root().clone());
|
||||||
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator
|
s.block.base.header.set_log_bloom(s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator
|
||||||
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
|
s.block.base.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used));
|
||||||
s.block.base.header.note_dirty();
|
|
||||||
|
|
||||||
LockedBlock {
|
LockedBlock {
|
||||||
block: s.block,
|
block: s.block,
|
||||||
@ -625,9 +623,9 @@ mod tests {
|
|||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
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 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();
|
let mut uncle1_header = Header::new();
|
||||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
uncle1_header.set_extra_data(b"uncle1".to_vec());
|
||||||
let mut uncle2_header = Header::new();
|
let mut uncle2_header = Header::new();
|
||||||
uncle2_header.extra_data = b"uncle2".to_vec();
|
uncle2_header.set_extra_data(b"uncle2".to_vec());
|
||||||
open_block.push_uncle(uncle1_header).unwrap();
|
open_block.push_uncle(uncle1_header).unwrap();
|
||||||
open_block.push_uncle(uncle2_header).unwrap();
|
open_block.push_uncle(uncle2_header).unwrap();
|
||||||
let b = open_block.close_and_lock().seal(engine, vec![]).unwrap();
|
let b = open_block.close_and_lock().seal(engine, vec![]).unwrap();
|
||||||
@ -643,7 +641,7 @@ mod tests {
|
|||||||
let bytes = e.rlp_bytes();
|
let bytes = e.rlp_bytes();
|
||||||
assert_eq!(bytes, orig_bytes);
|
assert_eq!(bytes, orig_bytes);
|
||||||
let uncles = BlockView::new(&bytes).uncles();
|
let uncles = BlockView::new(&bytes).uncles();
|
||||||
assert_eq!(uncles[1].extra_data, b"uncle2");
|
assert_eq!(uncles[1].extra_data(), b"uncle2");
|
||||||
|
|
||||||
let db = e.drain();
|
let db = e.drain();
|
||||||
assert_eq!(orig_db.keys(), db.keys());
|
assert_eq!(orig_db.keys(), db.keys());
|
||||||
|
@ -260,7 +260,7 @@ impl BlockQueue {
|
|||||||
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
|
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
|
||||||
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
|
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
|
||||||
let block = verifying.pop_front().unwrap().block.unwrap();
|
let block = verifying.pop_front().unwrap().block.unwrap();
|
||||||
if bad.contains(&block.header.parent_hash) {
|
if bad.contains(block.header.parent_hash()) {
|
||||||
bad.insert(block.header.hash());
|
bad.insert(block.header.hash());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -313,7 +313,7 @@ impl BlockQueue {
|
|||||||
return Err(ImportError::KnownBad.into());
|
return Err(ImportError::KnownBad.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if bad.contains(&header.parent_hash) {
|
if bad.contains(header.parent_hash()) {
|
||||||
bad.insert(h.clone());
|
bad.insert(h.clone());
|
||||||
return Err(ImportError::KnownBad.into());
|
return Err(ImportError::KnownBad.into());
|
||||||
}
|
}
|
||||||
@ -351,7 +351,7 @@ impl BlockQueue {
|
|||||||
|
|
||||||
let mut new_verified = VecDeque::new();
|
let mut new_verified = VecDeque::new();
|
||||||
for block in verified.drain(..) {
|
for block in verified.drain(..) {
|
||||||
if bad.contains(&block.header.parent_hash) {
|
if bad.contains(block.header.parent_hash()) {
|
||||||
bad.insert(block.header.hash());
|
bad.insert(block.header.hash());
|
||||||
processing.remove(&block.header.hash());
|
processing.remove(&block.header.hash());
|
||||||
} else {
|
} else {
|
||||||
|
@ -1434,7 +1434,7 @@ mod tests {
|
|||||||
let mut block_header = bc.block_header(&best_hash);
|
let mut block_header = bc.block_header(&best_hash);
|
||||||
|
|
||||||
while !block_header.is_none() {
|
while !block_header.is_none() {
|
||||||
block_header = bc.block_header(&block_header.unwrap().parent_hash);
|
block_header = bc.block_header(&block_header.unwrap().parent_hash());
|
||||||
}
|
}
|
||||||
assert!(bc.cache_size().blocks > 1024 * 1024);
|
assert!(bc.cache_size().blocks > 1024 * 1024);
|
||||||
|
|
||||||
|
@ -44,21 +44,22 @@ impl Encodable for Block {
|
|||||||
|
|
||||||
impl Forkable for Block {
|
impl Forkable for Block {
|
||||||
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
||||||
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
|
let difficulty = self.header.difficulty().clone() - U256::from(fork_number);
|
||||||
|
self.header.set_difficulty(difficulty);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithBloom for Block {
|
impl WithBloom for Block {
|
||||||
fn with_bloom(mut self, bloom: H2048) -> Self where Self: Sized {
|
fn with_bloom(mut self, bloom: H2048) -> Self where Self: Sized {
|
||||||
self.header.log_bloom = bloom;
|
self.header.set_log_bloom(bloom);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompleteBlock for Block {
|
impl CompleteBlock for Block {
|
||||||
fn complete(mut self, parent_hash: H256) -> Bytes {
|
fn complete(mut self, parent_hash: H256) -> Bytes {
|
||||||
self.header.parent_hash = parent_hash;
|
self.header.set_parent_hash(parent_hash);
|
||||||
encode(&self).to_vec()
|
encode(&self).to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,8 @@ pub struct ChainGenerator {
|
|||||||
impl ChainGenerator {
|
impl ChainGenerator {
|
||||||
fn prepare_block(&self) -> Block {
|
fn prepare_block(&self) -> Block {
|
||||||
let mut block = Block::default();
|
let mut block = Block::default();
|
||||||
block.header.number = self.number;
|
block.header.set_number(self.number);
|
||||||
block.header.difficulty = self.difficulty;
|
block.header.set_difficulty(self.difficulty);
|
||||||
block
|
block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,190 +14,250 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256 as Sha256Digest;
|
||||||
use crypto::ripemd160::Ripemd160;
|
use crypto::ripemd160::Ripemd160 as Ripemd160Digest;
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethkey::{Signature, recover};
|
use ethkey::{Signature, recover as ec_recover};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
/// Definition of a contract whose implementation is built-in.
|
/// Native implementation of a built-in contract.
|
||||||
pub struct Builtin {
|
pub trait Impl: Send + Sync {
|
||||||
/// The gas cost of running this built-in for the given size of input data.
|
/// execute this built-in on the given input, writing to the given output.
|
||||||
pub cost: Box<Fn(usize) -> U256>, // TODO: U256 should be bignum.
|
fn execute(&self, input: &[u8], out: &mut [u8]);
|
||||||
/// Run this built-in function with the input being the first argument and the output
|
|
||||||
/// being placed into the second.
|
|
||||||
pub execute: Box<Fn(&[u8], &mut [u8])>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rust does not mark closurer that do not capture as Sync
|
/// A gas pricing scheme for built-in contracts.
|
||||||
// We promise that all builtins are thread safe since they only operate on given input.
|
pub trait Pricer: Send + Sync {
|
||||||
unsafe impl Sync for Builtin {}
|
/// The gas cost of running this built-in for the given size of input data.
|
||||||
unsafe impl Send for Builtin {}
|
fn cost(&self, in_size: usize) -> U256;
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Builtin {
|
/// A linear pricing model. This computes a price using a base cost and a cost per-word.
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
struct Linear {
|
||||||
write!(f, "<Builtin>")
|
base: usize,
|
||||||
|
word: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pricer for Linear {
|
||||||
|
fn cost(&self, in_size: usize) -> U256 {
|
||||||
|
U256::from(self.base) + U256::from(self.word) * U256::from((in_size + 31) / 32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pricing scheme and execution definition for a built-in contract.
|
||||||
|
pub struct Builtin {
|
||||||
|
pricer: Box<Pricer>,
|
||||||
|
native: Box<Impl>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Builtin {
|
impl Builtin {
|
||||||
/// Create a new object from components.
|
|
||||||
pub fn new(cost: Box<Fn(usize) -> U256>, execute: Box<Fn(&[u8], &mut [u8])>) -> Builtin {
|
|
||||||
Builtin {cost: cost, execute: execute}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new object from a builtin-function name with a linear cost associated with input size.
|
|
||||||
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Builtin {
|
|
||||||
let cost = Box::new(move|s: usize| -> U256 {
|
|
||||||
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::new(cost, new_builtin_exec(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Simple forwarder for cost.
|
/// Simple forwarder for cost.
|
||||||
pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) }
|
pub fn cost(&self, s: usize) -> U256 { self.pricer.cost(s) }
|
||||||
|
|
||||||
/// Simple forwarder for execute.
|
/// Simple forwarder for execute.
|
||||||
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
|
pub fn execute(&self, input: &[u8], output: &mut[u8]) { self.native.execute(input, output) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::Builtin> for Builtin {
|
impl From<ethjson::spec::Builtin> for Builtin {
|
||||||
fn from(b: ethjson::spec::Builtin) -> Self {
|
fn from(b: ethjson::spec::Builtin) -> Self {
|
||||||
match b.pricing {
|
let pricer = match b.pricing {
|
||||||
ethjson::spec::Pricing::Linear(linear) => {
|
ethjson::spec::Pricing::Linear(linear) => {
|
||||||
Self::from_named_linear(b.name.as_ref(), linear.base, linear.word)
|
Box::new(Linear {
|
||||||
|
base: linear.base,
|
||||||
|
word: linear.word,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Builtin {
|
||||||
|
pricer: pricer,
|
||||||
|
native: ethereum_builtin(&b.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy a bunch of bytes to a destination; if the `src` is too small to fill `dest`,
|
// Ethereum builtin creator.
|
||||||
/// leave the rest unchanged.
|
fn ethereum_builtin(name: &str) -> Box<Impl> {
|
||||||
pub fn copy_to(src: &[u8], dest: &mut[u8]) {
|
|
||||||
// NICE: optimise
|
|
||||||
for i in 0..min(src.len(), dest.len()) {
|
|
||||||
dest[i] = src[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new builtin executor according to `name`.
|
|
||||||
/// TODO: turn in to a factory with dynamic registration.
|
|
||||||
pub fn new_builtin_exec(name: &str) -> Box<Fn(&[u8], &mut [u8])> {
|
|
||||||
match name {
|
match name {
|
||||||
"identity" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
"identity" => Box::new(Identity) as Box<Impl>,
|
||||||
for i in 0..min(input.len(), output.len()) {
|
"ecrecover" => Box::new(EcRecover) as Box<Impl>,
|
||||||
output[i] = input[i];
|
"sha256" => Box::new(Sha256) as Box<Impl>,
|
||||||
|
"ripemd160" => Box::new(Ripemd160) as Box<Impl>,
|
||||||
|
_ => panic!("invalid builtin name: {}", name),
|
||||||
}
|
}
|
||||||
}),
|
}
|
||||||
"ecrecover" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
|
||||||
#[repr(packed)]
|
// Ethereum builtins:
|
||||||
#[derive(Debug, Default)]
|
//
|
||||||
struct InType {
|
// - The identity function
|
||||||
hash: H256,
|
// - ec recovery
|
||||||
v: H256,
|
// - sha256
|
||||||
r: H256,
|
// - ripemd160
|
||||||
s: H256,
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Identity;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct EcRecover;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Sha256;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Ripemd160;
|
||||||
|
|
||||||
|
impl Impl for Identity {
|
||||||
|
fn execute(&self, input: &[u8], output: &mut [u8]) {
|
||||||
|
let len = min(input.len(), output.len());
|
||||||
|
output[..len].copy_from_slice(&input[..len]);
|
||||||
}
|
}
|
||||||
let mut it = InType::default();
|
}
|
||||||
it.copy_raw(input);
|
|
||||||
if it.v == H256::from(&U256::from(27)) || it.v == H256::from(&U256::from(28)) {
|
impl Impl for EcRecover {
|
||||||
let s = Signature::from_rsv(&it.r, &it.s, it.v[31] - 27);
|
fn execute(&self, i: &[u8], output: &mut [u8]) {
|
||||||
|
let len = min(i.len(), 128);
|
||||||
|
|
||||||
|
let mut input = [0; 128];
|
||||||
|
input[..len].copy_from_slice(&i[..len]);
|
||||||
|
|
||||||
|
let hash = H256::from_slice(&input[0..32]);
|
||||||
|
let v = H256::from_slice(&input[32..64]);
|
||||||
|
let r = H256::from_slice(&input[64..96]);
|
||||||
|
let s = H256::from_slice(&input[96..128]);
|
||||||
|
|
||||||
|
let bit = match v[31] {
|
||||||
|
27 | 28 if &v.as_slice()[..31] == &[0; 31] => v[31] - 27,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = Signature::from_rsv(&r, &s, bit);
|
||||||
if s.is_valid() {
|
if s.is_valid() {
|
||||||
if let Ok(p) = recover(&s, &it.hash) {
|
if let Ok(p) = ec_recover(&s, &hash) {
|
||||||
let r = p.as_slice().sha3();
|
let r = p.as_slice().sha3();
|
||||||
// NICE: optimise and separate out into populate-like function
|
|
||||||
for i in 0..min(32, output.len()) {
|
let out_len = min(output.len(), 32);
|
||||||
output[i] = if i < 12 {0} else {r[i]};
|
|
||||||
|
for x in &mut output[0.. min(12, out_len)] {
|
||||||
|
*x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if out_len > 12 {
|
||||||
|
output[12..out_len].copy_from_slice(&r[12..out_len]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
"sha256" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
|
||||||
let mut sha = Sha256::new();
|
|
||||||
sha.input(input);
|
|
||||||
if output.len() >= 32 {
|
|
||||||
sha.result(output);
|
|
||||||
} else {
|
|
||||||
let mut ret = H256::new();
|
|
||||||
sha.result(ret.as_slice_mut());
|
|
||||||
copy_to(&ret, output);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"ripemd160" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
|
||||||
let mut sha = Ripemd160::new();
|
|
||||||
sha.input(input);
|
|
||||||
let mut ret = H256::new();
|
|
||||||
sha.result(&mut ret.as_slice_mut()[12..32]);
|
|
||||||
copy_to(&ret, output);
|
|
||||||
}),
|
|
||||||
_ => {
|
|
||||||
panic!("invalid builtin name {}", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
impl Impl for Sha256 {
|
||||||
fn identity() {
|
fn execute(&self, input: &[u8], output: &mut [u8]) {
|
||||||
let f = new_builtin_exec("identity");
|
let out_len = min(output.len(), 32);
|
||||||
|
|
||||||
|
let mut sha = Sha256Digest::new();
|
||||||
|
sha.input(input);
|
||||||
|
|
||||||
|
if out_len == 32 {
|
||||||
|
sha.result(&mut output[0..32]);
|
||||||
|
} else {
|
||||||
|
let mut out = [0; 32];
|
||||||
|
sha.result(&mut out);
|
||||||
|
|
||||||
|
output.copy_from_slice(&out[..out_len])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Impl for Ripemd160 {
|
||||||
|
fn execute(&self, input: &[u8], output: &mut [u8]) {
|
||||||
|
let out_len = min(output.len(), 32);
|
||||||
|
|
||||||
|
let mut sha = Ripemd160Digest::new();
|
||||||
|
sha.input(input);
|
||||||
|
|
||||||
|
for x in &mut output[0.. min(12, out_len)] {
|
||||||
|
*x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if out_len >= 32 {
|
||||||
|
sha.result(&mut output[12..32]);
|
||||||
|
} else if out_len > 12 {
|
||||||
|
let mut out = [0; 20];
|
||||||
|
sha.result(&mut out);
|
||||||
|
|
||||||
|
output.copy_from_slice(&out[12..out_len])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{Builtin, Linear, ethereum_builtin, Pricer};
|
||||||
|
use ethjson;
|
||||||
|
use util::U256;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identity() {
|
||||||
|
let f = ethereum_builtin("identity");
|
||||||
|
|
||||||
let i = [0u8, 1, 2, 3];
|
let i = [0u8, 1, 2, 3];
|
||||||
|
|
||||||
let mut o2 = [255u8; 2];
|
let mut o2 = [255u8; 2];
|
||||||
f(&i[..], &mut o2[..]);
|
f.execute(&i[..], &mut o2[..]);
|
||||||
assert_eq!(i[0..2], o2);
|
assert_eq!(i[0..2], o2);
|
||||||
|
|
||||||
let mut o4 = [255u8; 4];
|
let mut o4 = [255u8; 4];
|
||||||
f(&i[..], &mut o4[..]);
|
f.execute(&i[..], &mut o4[..]);
|
||||||
assert_eq!(i, o4);
|
assert_eq!(i, o4);
|
||||||
|
|
||||||
let mut o8 = [255u8; 8];
|
let mut o8 = [255u8; 8];
|
||||||
f(&i[..], &mut o8[..]);
|
f.execute(&i[..], &mut o8[..]);
|
||||||
assert_eq!(i, o8[..4]);
|
assert_eq!(i, o8[..4]);
|
||||||
assert_eq!([255u8; 4], o8[4..]);
|
assert_eq!([255u8; 4], o8[4..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sha256() {
|
fn sha256() {
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
let f = new_builtin_exec("sha256");
|
let f = ethereum_builtin("sha256");
|
||||||
|
|
||||||
let i = [0u8; 0];
|
let i = [0u8; 0];
|
||||||
|
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i[..], &mut o[..]);
|
f.execute(&i[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
|
||||||
|
|
||||||
let mut o8 = [255u8; 8];
|
let mut o8 = [255u8; 8];
|
||||||
f(&i[..], &mut o8[..]);
|
f.execute(&i[..], &mut o8[..]);
|
||||||
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
|
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
|
||||||
|
|
||||||
let mut o34 = [255u8; 34];
|
let mut o34 = [255u8; 34];
|
||||||
f(&i[..], &mut o34[..]);
|
f.execute(&i[..], &mut o34[..]);
|
||||||
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
|
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ripemd160() {
|
fn ripemd160() {
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
let f = new_builtin_exec("ripemd160");
|
let f = ethereum_builtin("ripemd160");
|
||||||
|
|
||||||
let i = [0u8; 0];
|
let i = [0u8; 0];
|
||||||
|
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i[..], &mut o[..]);
|
f.execute(&i[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
|
||||||
|
|
||||||
let mut o8 = [255u8; 8];
|
let mut o8 = [255u8; 8];
|
||||||
f(&i[..], &mut o8[..]);
|
f.execute(&i[..], &mut o8[..]);
|
||||||
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
|
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
|
||||||
|
|
||||||
let mut o34 = [255u8; 34];
|
let mut o34 = [255u8; 34];
|
||||||
f(&i[..], &mut o34[..]);
|
f.execute(&i[..], &mut o34[..]);
|
||||||
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
|
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ecrecover() {
|
fn ecrecover() {
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
/*let k = KeyPair::from_secret(b"test".sha3()).unwrap();
|
/*let k = KeyPair::from_secret(b"test".sha3()).unwrap();
|
||||||
let a: Address = From::from(k.public().sha3());
|
let a: Address = From::from(k.public().sha3());
|
||||||
@ -207,75 +267,81 @@ fn ecrecover() {
|
|||||||
let s = k.sign(&m).unwrap();
|
let s = k.sign(&m).unwrap();
|
||||||
println!("Signed: {}", s);*/
|
println!("Signed: {}", s);*/
|
||||||
|
|
||||||
let f = new_builtin_exec("ecrecover");
|
let f = ethereum_builtin("ecrecover");
|
||||||
|
|
||||||
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||||
|
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i[..], &mut o[..]);
|
f.execute(&i[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
|
||||||
|
|
||||||
let mut o8 = [255u8; 8];
|
let mut o8 = [255u8; 8];
|
||||||
f(&i[..], &mut o8[..]);
|
f.execute(&i[..], &mut o8[..]);
|
||||||
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
|
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
|
||||||
|
|
||||||
let mut o34 = [255u8; 34];
|
let mut o34 = [255u8; 34];
|
||||||
f(&i[..], &mut o34[..]);
|
f.execute(&i[..], &mut o34[..]);
|
||||||
assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]);
|
assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]);
|
||||||
|
|
||||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i_bad[..], &mut o[..]);
|
f.execute(&i_bad[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||||
|
|
||||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i_bad[..], &mut o[..]);
|
f.execute(&i_bad[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||||
|
|
||||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
|
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i_bad[..], &mut o[..]);
|
f.execute(&i_bad[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||||
|
|
||||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
|
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i_bad[..], &mut o[..]);
|
f.execute(&i_bad[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||||
|
|
||||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
|
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i_bad[..], &mut o[..]);
|
f.execute(&i_bad[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||||
|
|
||||||
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
|
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
|
||||||
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||||
let mut o = [255u8; 32];
|
let mut o = [255u8; 32];
|
||||||
f(&i_bad[..], &mut o[..]);
|
f.execute(&i_bad[..], &mut o[..]);
|
||||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
|
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn from_unknown_linear() {
|
fn from_unknown_linear() {
|
||||||
let _ = Builtin::from_named_linear("dw", 10, 20);
|
let _ = ethereum_builtin("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_named_linear() {
|
fn from_named_linear() {
|
||||||
let b = Builtin::from_named_linear("identity", 10, 20);
|
let pricer = Box::new(Linear { base: 10, word: 20 });
|
||||||
assert_eq!((*b.cost)(0), U256::from(10));
|
let b = Builtin {
|
||||||
assert_eq!((*b.cost)(1), U256::from(30));
|
pricer: pricer as Box<Pricer>,
|
||||||
assert_eq!((*b.cost)(32), U256::from(30));
|
native: ethereum_builtin("identity"),
|
||||||
assert_eq!((*b.cost)(33), U256::from(50));
|
};
|
||||||
|
|
||||||
|
assert_eq!(b.cost(0), U256::from(10));
|
||||||
|
assert_eq!(b.cost(1), U256::from(30));
|
||||||
|
assert_eq!(b.cost(32), U256::from(30));
|
||||||
|
assert_eq!(b.cost(33), U256::from(50));
|
||||||
|
|
||||||
let i = [0u8, 1, 2, 3];
|
let i = [0u8, 1, 2, 3];
|
||||||
let mut o = [255u8; 4];
|
let mut o = [255u8; 4];
|
||||||
(*b.execute)(&i[..], &mut o[..]);
|
b.execute(&i[..], &mut o[..]);
|
||||||
assert_eq!(i, o);
|
assert_eq!(i, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_json() {
|
fn from_json() {
|
||||||
let b = Builtin::from(ethjson::spec::Builtin {
|
let b = Builtin::from(ethjson::spec::Builtin {
|
||||||
name: "identity".to_owned(),
|
name: "identity".to_owned(),
|
||||||
pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
|
pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
|
||||||
@ -284,13 +350,14 @@ fn from_json() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!((*b.cost)(0), U256::from(10));
|
assert_eq!(b.cost(0), U256::from(10));
|
||||||
assert_eq!((*b.cost)(1), U256::from(30));
|
assert_eq!(b.cost(1), U256::from(30));
|
||||||
assert_eq!((*b.cost)(32), U256::from(30));
|
assert_eq!(b.cost(32), U256::from(30));
|
||||||
assert_eq!((*b.cost)(33), U256::from(50));
|
assert_eq!(b.cost(33), U256::from(50));
|
||||||
|
|
||||||
let i = [0u8, 1, 2, 3];
|
let i = [0u8, 1, 2, 3];
|
||||||
let mut o = [255u8; 4];
|
let mut o = [255u8; 4];
|
||||||
(*b.execute)(&i[..], &mut o[..]);
|
b.execute(&i[..], &mut o[..]);
|
||||||
assert_eq!(i, o);
|
assert_eq!(i, o);
|
||||||
|
}
|
||||||
}
|
}
|
@ -100,7 +100,7 @@ impl ClientReport {
|
|||||||
pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
|
pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
|
||||||
self.blocks_imported += 1;
|
self.blocks_imported += 1;
|
||||||
self.transactions_applied += block.transactions.len();
|
self.transactions_applied += block.transactions.len();
|
||||||
self.gas_processed = self.gas_processed + block.header.gas_used;
|
self.gas_processed = self.gas_processed + block.header.gas_used().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,15 +285,15 @@ impl Client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if Parent is in chain
|
// Check if Parent is in chain
|
||||||
let chain_has_parent = self.chain.block_header(&header.parent_hash);
|
let chain_has_parent = self.chain.block_header(header.parent_hash());
|
||||||
if let None = chain_has_parent {
|
if let None = chain_has_parent {
|
||||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
|
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
|
||||||
return Err(());
|
return Err(());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enact Verified Block
|
// Enact Verified Block
|
||||||
let parent = chain_has_parent.unwrap();
|
let parent = chain_has_parent.unwrap();
|
||||||
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
||||||
let db = self.state_db.lock().boxed_clone();
|
let db = self.state_db.lock().boxed_clone();
|
||||||
|
|
||||||
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||||
@ -353,7 +353,7 @@ impl Client {
|
|||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
if invalid_blocks.contains(&header.parent_hash) {
|
if invalid_blocks.contains(header.parent_hash()) {
|
||||||
invalid_blocks.insert(header.hash());
|
invalid_blocks.insert(header.hash());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -169,19 +169,19 @@ impl TestBlockChainClient {
|
|||||||
let len = self.numbers.read().len();
|
let len = self.numbers.read().len();
|
||||||
for n in len..(len + count) {
|
for n in len..(len + count) {
|
||||||
let mut header = BlockHeader::new();
|
let mut header = BlockHeader::new();
|
||||||
header.difficulty = From::from(n);
|
header.set_difficulty(From::from(n));
|
||||||
header.parent_hash = self.last_hash.read().clone();
|
header.set_parent_hash(self.last_hash.read().clone());
|
||||||
header.number = n as BlockNumber;
|
header.set_number(n as BlockNumber);
|
||||||
header.gas_limit = U256::from(1_000_000);
|
header.set_gas_limit(U256::from(1_000_000));
|
||||||
let uncles = match with {
|
let uncles = match with {
|
||||||
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
||||||
let mut uncles = RlpStream::new_list(1);
|
let mut uncles = RlpStream::new_list(1);
|
||||||
let mut uncle_header = BlockHeader::new();
|
let mut uncle_header = BlockHeader::new();
|
||||||
uncle_header.difficulty = From::from(n);
|
uncle_header.set_difficulty(From::from(n));
|
||||||
uncle_header.parent_hash = self.last_hash.read().clone();
|
uncle_header.set_parent_hash(self.last_hash.read().clone());
|
||||||
uncle_header.number = n as BlockNumber;
|
uncle_header.set_number(n as BlockNumber);
|
||||||
uncles.append(&uncle_header);
|
uncles.append(&uncle_header);
|
||||||
header.uncles_hash = uncles.as_raw().sha3();
|
header.set_uncles_hash(uncles.as_raw().sha3());
|
||||||
uncles
|
uncles
|
||||||
},
|
},
|
||||||
_ => RlpStream::new_list(0)
|
_ => RlpStream::new_list(0)
|
||||||
@ -219,7 +219,7 @@ impl TestBlockChainClient {
|
|||||||
pub fn corrupt_block(&mut self, n: BlockNumber) {
|
pub fn corrupt_block(&mut self, n: BlockNumber) {
|
||||||
let hash = self.block_hash(BlockID::Number(n)).unwrap();
|
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 = decode(&self.block_header(BlockID::Number(n)).unwrap());
|
||||||
header.extra_data = b"This extra data is way too long to be considered valid".to_vec();
|
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);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(&header);
|
rlp.append(&header);
|
||||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||||
@ -231,7 +231,7 @@ impl TestBlockChainClient {
|
|||||||
pub fn corrupt_block_parent(&mut self, n: BlockNumber) {
|
pub fn corrupt_block_parent(&mut self, n: BlockNumber) {
|
||||||
let hash = self.block_hash(BlockID::Number(n)).unwrap();
|
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 = decode(&self.block_header(BlockID::Number(n)).unwrap());
|
||||||
header.parent_hash = H256::from(42);
|
header.set_parent_hash(H256::from(42));
|
||||||
let mut rlp = RlpStream::new_list(3);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(&header);
|
rlp.append(&header);
|
||||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||||
@ -470,20 +470,20 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
|
fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
|
||||||
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
||||||
let h = header.hash();
|
let h = header.hash();
|
||||||
let number: usize = header.number as usize;
|
let number: usize = header.number() as usize;
|
||||||
if number > self.blocks.read().len() {
|
if number > self.blocks.read().len() {
|
||||||
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number);
|
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number);
|
||||||
}
|
}
|
||||||
if number > 0 {
|
if number > 0 {
|
||||||
match self.blocks.read().get(&header.parent_hash) {
|
match self.blocks.read().get(header.parent_hash()) {
|
||||||
Some(parent) => {
|
Some(parent) => {
|
||||||
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
|
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
|
||||||
if parent.number != (header.number - 1) {
|
if parent.number() != (header.number() - 1) {
|
||||||
panic!("Unexpected block parent");
|
panic!("Unexpected block parent");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
panic!("Unknown block parent {:?} for block {}", header.parent_hash, number);
|
panic!("Unknown block parent {:?} for block {}", header.parent_hash(), number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -491,18 +491,18 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
if number == len {
|
if number == len {
|
||||||
{
|
{
|
||||||
let mut difficulty = self.difficulty.write();
|
let mut difficulty = self.difficulty.write();
|
||||||
*difficulty = *difficulty + header.difficulty;
|
*difficulty = *difficulty + header.difficulty().clone();
|
||||||
}
|
}
|
||||||
mem::replace(&mut *self.last_hash.write(), h.clone());
|
mem::replace(&mut *self.last_hash.write(), h.clone());
|
||||||
self.blocks.write().insert(h.clone(), b);
|
self.blocks.write().insert(h.clone(), b);
|
||||||
self.numbers.write().insert(number, h.clone());
|
self.numbers.write().insert(number, h.clone());
|
||||||
let mut parent_hash = header.parent_hash;
|
let mut parent_hash = header.parent_hash().clone();
|
||||||
if number > 0 {
|
if number > 0 {
|
||||||
let mut n = number - 1;
|
let mut n = number - 1;
|
||||||
while n > 0 && self.numbers.read()[&n] != parent_hash {
|
while n > 0 && self.numbers.read()[&n] != parent_hash {
|
||||||
*self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone();
|
*self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone();
|
||||||
n -= 1;
|
n -= 1;
|
||||||
parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
|
parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,17 +82,16 @@ impl Engine for BasicAuthority {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||||
header.difficulty = parent.difficulty;
|
header.set_difficulty(parent.difficulty().clone());
|
||||||
header.gas_limit = {
|
header.set_gas_limit({
|
||||||
let gas_limit = parent.gas_limit;
|
let gas_limit = parent.gas_limit().clone();
|
||||||
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||||
if gas_limit < gas_floor_target {
|
if gas_limit < gas_floor_target {
|
||||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||||
} else {
|
} else {
|
||||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
header.note_dirty();
|
|
||||||
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +122,9 @@ impl Engine for BasicAuthority {
|
|||||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
// check the seal fields.
|
// check the seal fields.
|
||||||
// TODO: pull this out into common code.
|
// TODO: pull this out into common code.
|
||||||
if header.seal.len() != self.seal_fields() {
|
if header.seal().len() != self.seal_fields() {
|
||||||
return Err(From::from(BlockError::InvalidSealArity(
|
return Err(From::from(BlockError::InvalidSealArity(
|
||||||
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
|
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -133,7 +132,7 @@ impl Engine for BasicAuthority {
|
|||||||
|
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
// check the signature is legit.
|
// check the signature is legit.
|
||||||
let sig = try!(UntrustedRlp::new(&header.seal[0]).as_val::<H520>());
|
let sig = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H520>());
|
||||||
let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash())));
|
let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash())));
|
||||||
if !self.our_params.authorities.contains(&signer) {
|
if !self.our_params.authorities.contains(&signer) {
|
||||||
return try!(Err(BlockError::InvalidSeal));
|
return try!(Err(BlockError::InvalidSeal));
|
||||||
@ -152,10 +151,10 @@ impl Engine for BasicAuthority {
|
|||||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
|
||||||
}
|
}
|
||||||
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||||
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
|
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||||
let max_gas = parent.gas_limit + parent.gas_limit / 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 {
|
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -131,9 +131,8 @@ pub trait Engine : Sync + Send {
|
|||||||
/// Don't forget to call Super::populate_from_parent when subclassing & overriding.
|
/// Don't forget to call Super::populate_from_parent when subclassing & overriding.
|
||||||
// TODO: consider including State in the params.
|
// TODO: consider including State in the params.
|
||||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) {
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||||
header.difficulty = parent.difficulty;
|
header.set_difficulty(parent.difficulty().clone());
|
||||||
header.gas_limit = parent.gas_limit;
|
header.set_gas_limit(parent.gas_limit().clone());
|
||||||
header.note_dirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle any potential consensus messages;
|
/// Handle any potential consensus messages;
|
||||||
|
@ -114,9 +114,9 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) {
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) {
|
||||||
header.difficulty = self.calculate_difficuty(header, parent);
|
let difficulty = self.calculate_difficulty(header, parent);
|
||||||
header.gas_limit = {
|
let gas_limit = {
|
||||||
let gas_limit = parent.gas_limit;
|
let gas_limit = parent.gas_limit().clone();
|
||||||
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
|
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||||
if gas_limit < gas_floor_target {
|
if gas_limit < gas_floor_target {
|
||||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||||
@ -126,21 +126,23 @@ impl Engine for Ethash {
|
|||||||
min(gas_ceil_target,
|
min(gas_ceil_target,
|
||||||
max(gas_floor_target,
|
max(gas_floor_target,
|
||||||
gas_limit - gas_limit / bound_divisor + 1.into() +
|
gas_limit - gas_limit / bound_divisor + 1.into() +
|
||||||
(header.gas_used * 6.into() / 5.into()) / bound_divisor))
|
(header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if header.number >= self.ethash_params.dao_hardfork_transition &&
|
header.set_difficulty(difficulty);
|
||||||
header.number <= self.ethash_params.dao_hardfork_transition + 9 {
|
header.set_gas_limit(gas_limit);
|
||||||
header.extra_data = b"dao-hard-fork"[..].to_owned();
|
if header.number() >= self.ethash_params.dao_hardfork_transition &&
|
||||||
|
header.number() <= self.ethash_params.dao_hardfork_transition + 9 {
|
||||||
|
header.set_extra_data(b"dao-hard-fork"[..].to_owned());
|
||||||
}
|
}
|
||||||
header.note_dirty();
|
header.note_dirty();
|
||||||
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number(), header.difficulty(), header.gas_limit());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_new_block(&self, block: &mut ExecutedBlock) {
|
fn on_new_block(&self, block: &mut ExecutedBlock) {
|
||||||
if block.fields().header.number == self.ethash_params.dao_hardfork_transition {
|
if block.fields().header.number() == self.ethash_params.dao_hardfork_transition {
|
||||||
// TODO: enable trigger function maybe?
|
// TODO: enable trigger function maybe?
|
||||||
// if block.fields().header.gas_limit <= 4_000_000.into() {
|
// if block.fields().header.gas_limit() <= 4_000_000.into() {
|
||||||
let mut state = block.fields_mut().state;
|
let mut state = block.fields_mut().state;
|
||||||
for child in &self.ethash_params.dao_hardfork_accounts {
|
for child in &self.ethash_params.dao_hardfork_accounts {
|
||||||
let b = state.balance(child);
|
let b = state.balance(child);
|
||||||
@ -157,7 +159,7 @@ impl Engine for Ethash {
|
|||||||
let fields = block.fields_mut();
|
let fields = block.fields_mut();
|
||||||
|
|
||||||
// Bestow block reward
|
// Bestow block reward
|
||||||
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
|
fields.state.add_balance(&fields.header.author(), &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
|
||||||
|
|
||||||
// Bestow uncle rewards
|
// Bestow uncle rewards
|
||||||
let current_number = fields.header.number();
|
let current_number = fields.header.number();
|
||||||
@ -171,18 +173,18 @@ impl Engine for Ethash {
|
|||||||
|
|
||||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
// check the seal fields.
|
// check the seal fields.
|
||||||
if header.seal.len() != self.seal_fields() {
|
if header.seal().len() != self.seal_fields() {
|
||||||
return Err(From::from(BlockError::InvalidSealArity(
|
return Err(From::from(BlockError::InvalidSealArity(
|
||||||
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
|
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
try!(UntrustedRlp::new(&header.seal[0]).as_val::<H256>());
|
try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H256>());
|
||||||
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
|
try!(UntrustedRlp::new(&header.seal()[1]).as_val::<H64>());
|
||||||
|
|
||||||
// TODO: consider removing these lines.
|
// TODO: consider removing these lines.
|
||||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||||
if header.difficulty < min_difficulty {
|
if header.difficulty() < &min_difficulty {
|
||||||
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty })))
|
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() })))
|
||||||
}
|
}
|
||||||
|
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
|
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
|
||||||
@ -190,37 +192,37 @@ impl Engine for Ethash {
|
|||||||
header.nonce().low_u64(),
|
header.nonce().low_u64(),
|
||||||
&Ethash::to_ethash(header.mix_hash())
|
&Ethash::to_ethash(header.mix_hash())
|
||||||
)));
|
)));
|
||||||
if difficulty < header.difficulty {
|
if &difficulty < header.difficulty() {
|
||||||
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
|
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
|
||||||
}
|
}
|
||||||
|
|
||||||
if header.number >= self.ethash_params.dao_hardfork_transition &&
|
if header.number() >= self.ethash_params.dao_hardfork_transition &&
|
||||||
header.number <= self.ethash_params.dao_hardfork_transition + 9 &&
|
header.number() <= self.ethash_params.dao_hardfork_transition + 9 &&
|
||||||
header.extra_data[..] != b"dao-hard-fork"[..] {
|
header.extra_data()[..] != b"dao-hard-fork"[..] {
|
||||||
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 })));
|
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 })));
|
||||||
}
|
}
|
||||||
|
|
||||||
if header.gas_limit > 0x7fffffffffffffffu64.into() {
|
if header.gas_limit() > &0x7fffffffffffffffu64.into() {
|
||||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit })));
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit().clone() })));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
if header.seal.len() != self.seal_fields() {
|
if header.seal().len() != self.seal_fields() {
|
||||||
return Err(From::from(BlockError::InvalidSealArity(
|
return Err(From::from(BlockError::InvalidSealArity(
|
||||||
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
|
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
|
let result = self.pow.compute_light(header.number() as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
|
||||||
let mix = Ethash::from_ethash(result.mix_hash);
|
let mix = Ethash::from_ethash(result.mix_hash);
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
|
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
|
||||||
if mix != header.mix_hash() {
|
if mix != header.mix_hash() {
|
||||||
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
|
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
|
||||||
}
|
}
|
||||||
if difficulty < header.difficulty {
|
if &difficulty < header.difficulty() {
|
||||||
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
|
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -232,15 +234,15 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check difficulty is correct given the two timestamps.
|
// Check difficulty is correct given the two timestamps.
|
||||||
let expected_difficulty = self.calculate_difficuty(header, parent);
|
let expected_difficulty = self.calculate_difficulty(header, parent);
|
||||||
if header.difficulty != expected_difficulty {
|
if header.difficulty() != &expected_difficulty {
|
||||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() })))
|
||||||
}
|
}
|
||||||
let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor;
|
let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||||
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
|
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||||
let max_gas = parent.gas_limit + parent.gas_limit / 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 {
|
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -259,9 +261,9 @@ impl Engine for Ethash {
|
|||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
|
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
|
||||||
impl Ethash {
|
impl Ethash {
|
||||||
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
|
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
|
||||||
const EXP_DIFF_PERIOD: u64 = 100000;
|
const EXP_DIFF_PERIOD: u64 = 100000;
|
||||||
if header.number == 0 {
|
if header.number() == 0 {
|
||||||
panic!("Can't calculate genesis block difficulty");
|
panic!("Can't calculate genesis block difficulty");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,25 +272,25 @@ impl Ethash {
|
|||||||
let duration_limit = self.ethash_params.duration_limit;
|
let duration_limit = self.ethash_params.duration_limit;
|
||||||
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
||||||
|
|
||||||
let mut target = if header.number < frontier_limit {
|
let mut target = if header.number() < frontier_limit {
|
||||||
if header.timestamp >= parent.timestamp + duration_limit {
|
if header.timestamp() >= parent.timestamp() + duration_limit {
|
||||||
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
|
parent.difficulty().clone() - (parent.difficulty().clone() / difficulty_bound_divisor)
|
||||||
} else {
|
} else {
|
||||||
parent.difficulty + (parent.difficulty / difficulty_bound_divisor)
|
parent.difficulty().clone() + (parent.difficulty().clone() / difficulty_bound_divisor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty, header.timestamp, parent.timestamp);
|
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp());
|
||||||
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
|
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
|
||||||
let diff_inc = (header.timestamp - parent.timestamp) / 10;
|
let diff_inc = (header.timestamp() - parent.timestamp()) / 10;
|
||||||
if diff_inc <= 1 {
|
if diff_inc <= 1 {
|
||||||
parent.difficulty + parent.difficulty / From::from(2048) * From::from(1 - diff_inc)
|
parent.difficulty().clone() + parent.difficulty().clone() / From::from(2048) * From::from(1 - diff_inc)
|
||||||
} else {
|
} else {
|
||||||
parent.difficulty - parent.difficulty / From::from(2048) * From::from(min(diff_inc - 1, 99))
|
parent.difficulty().clone() - parent.difficulty().clone() / From::from(2048) * From::from(min(diff_inc - 1, 99))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
target = max(min_difficulty, target);
|
target = max(min_difficulty, target);
|
||||||
let period = ((parent.number + 1) / EXP_DIFF_PERIOD) as usize;
|
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
|
||||||
if period > 1 {
|
if period > 1 {
|
||||||
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
|
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
|
||||||
}
|
}
|
||||||
@ -336,7 +338,7 @@ impl Header {
|
|||||||
|
|
||||||
/// Set the nonce and mix hash fields of the header.
|
/// Set the nonce and mix hash fields of the header.
|
||||||
pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) {
|
pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) {
|
||||||
self.seal = vec![encode(mix_hash).to_vec(), encode(nonce).to_vec()];
|
self.set_seal(vec![encode(mix_hash).to_vec(), encode(nonce).to_vec()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +376,7 @@ mod tests {
|
|||||||
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
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();
|
let mut uncle = Header::new();
|
||||||
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
|
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
|
||||||
uncle.author = uncle_author.clone();
|
uncle.set_author(uncle_author);
|
||||||
b.push_uncle(uncle).unwrap();
|
b.push_uncle(uncle).unwrap();
|
||||||
|
|
||||||
let b = b.close();
|
let b = b.close();
|
||||||
|
@ -68,7 +68,7 @@ mod tests {
|
|||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), 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(&"0000000000000000000000000000000000000001".into()), 1u64.into());
|
||||||
assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()), 1u64.into());
|
assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()), 1u64.into());
|
||||||
assert_eq!(s.balance(&"0000000000000000000000000000000000000003".into()), 1u64.into());
|
assert_eq!(s.balance(&"0000000000000000000000000000000000000003".into()), 1u64.into());
|
||||||
|
@ -33,43 +33,42 @@ pub type BlockNumber = u64;
|
|||||||
/// Doesn't do all that much on its own.
|
/// Doesn't do all that much on its own.
|
||||||
#[derive(Debug, Clone, Eq)]
|
#[derive(Debug, Clone, Eq)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
// TODO: make all private.
|
|
||||||
/// Parent hash.
|
/// Parent hash.
|
||||||
pub parent_hash: H256,
|
parent_hash: H256,
|
||||||
/// Block timestamp.
|
/// Block timestamp.
|
||||||
pub timestamp: u64,
|
timestamp: u64,
|
||||||
/// Block number.
|
/// Block number.
|
||||||
pub number: BlockNumber,
|
number: BlockNumber,
|
||||||
/// Block author.
|
/// Block author.
|
||||||
pub author: Address,
|
author: Address,
|
||||||
|
|
||||||
/// Transactions root.
|
/// Transactions root.
|
||||||
pub transactions_root: H256,
|
transactions_root: H256,
|
||||||
/// Block uncles hash.
|
/// Block uncles hash.
|
||||||
pub uncles_hash: H256,
|
uncles_hash: H256,
|
||||||
/// Block extra data.
|
/// Block extra data.
|
||||||
pub extra_data: Bytes,
|
extra_data: Bytes,
|
||||||
|
|
||||||
/// State root.
|
/// State root.
|
||||||
pub state_root: H256,
|
state_root: H256,
|
||||||
/// Block receipts root.
|
/// Block receipts root.
|
||||||
pub receipts_root: H256,
|
receipts_root: H256,
|
||||||
/// Block bloom.
|
/// Block bloom.
|
||||||
pub log_bloom: LogBloom,
|
log_bloom: LogBloom,
|
||||||
/// Gas used for contracts execution.
|
/// Gas used for contracts execution.
|
||||||
pub gas_used: U256,
|
gas_used: U256,
|
||||||
/// Block gas limit.
|
/// Block gas limit.
|
||||||
pub gas_limit: U256,
|
gas_limit: U256,
|
||||||
|
|
||||||
/// Block difficulty.
|
/// Block difficulty.
|
||||||
pub difficulty: U256,
|
difficulty: U256,
|
||||||
/// Vector of post-RLP-encoded fields.
|
/// Vector of post-RLP-encoded fields.
|
||||||
pub seal: Vec<Bytes>,
|
seal: Vec<Bytes>,
|
||||||
|
|
||||||
/// The memoized hash of the RLP representation *including* the seal fields.
|
/// The memoized hash of the RLP representation *including* the seal fields.
|
||||||
pub hash: RefCell<Option<H256>>,
|
hash: RefCell<Option<H256>>,
|
||||||
/// The memoized hash of the RLP representation *without* the seal fields.
|
/// The memoized hash of the RLP representation *without* the seal fields.
|
||||||
pub bare_hash: RefCell<Option<H256>>,
|
bare_hash: RefCell<Option<H256>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Header {
|
impl PartialEq for Header {
|
||||||
@ -134,15 +133,21 @@ impl Header {
|
|||||||
|
|
||||||
/// Get the extra data field of the header.
|
/// Get the extra data field of the header.
|
||||||
pub fn extra_data(&self) -> &Bytes { &self.extra_data }
|
pub fn extra_data(&self) -> &Bytes { &self.extra_data }
|
||||||
|
/// Get a mutable reference to extra_data
|
||||||
|
pub fn extra_data_mut(&mut self) -> &mut Bytes { self.note_dirty(); &mut self.extra_data }
|
||||||
|
|
||||||
/// Get the state root field of the header.
|
/// Get the state root field of the header.
|
||||||
pub fn state_root(&self) -> &H256 { &self.state_root }
|
pub fn state_root(&self) -> &H256 { &self.state_root }
|
||||||
/// Get the receipts root field of the header.
|
/// Get the receipts root field of the header.
|
||||||
pub fn receipts_root(&self) -> &H256 { &self.receipts_root }
|
pub fn receipts_root(&self) -> &H256 { &self.receipts_root }
|
||||||
|
/// Get the log bloom field of the header.
|
||||||
|
pub fn log_bloom(&self) -> &LogBloom { &self.log_bloom }
|
||||||
/// Get the transactions root field of the header.
|
/// Get the transactions root field of the header.
|
||||||
pub fn transactions_root(&self) -> &H256 { &self.transactions_root }
|
pub fn transactions_root(&self) -> &H256 { &self.transactions_root }
|
||||||
/// Get the uncles hash field of the header.
|
/// Get the uncles hash field of the header.
|
||||||
pub fn uncles_hash(&self) -> &H256 { &self.uncles_hash }
|
pub fn uncles_hash(&self) -> &H256 { &self.uncles_hash }
|
||||||
|
/// Get the gas used field of the header.
|
||||||
|
pub fn gas_used(&self) -> &U256 { &self.gas_used }
|
||||||
/// Get the gas limit field of the header.
|
/// Get the gas limit field of the header.
|
||||||
pub fn gas_limit(&self) -> &U256 { &self.gas_limit }
|
pub fn gas_limit(&self) -> &U256 { &self.gas_limit }
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ use spec::Spec;
|
|||||||
use error::*;
|
use error::*;
|
||||||
use client::{Client, ClientConfig, ChainNotify};
|
use client::{Client, ClientConfig, ChainNotify};
|
||||||
use miner::Miner;
|
use miner::Miner;
|
||||||
|
use snapshot::ManifestData;
|
||||||
use snapshot::service::Service as SnapshotService;
|
use snapshot::service::Service as SnapshotService;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
@ -39,6 +40,8 @@ pub enum ClientIoMessage {
|
|||||||
BlockVerified,
|
BlockVerified,
|
||||||
/// New transaction RLPs are ready to be imported
|
/// New transaction RLPs are ready to be imported
|
||||||
NewTransactions(Vec<Bytes>),
|
NewTransactions(Vec<Bytes>),
|
||||||
|
/// Begin snapshot restoration
|
||||||
|
BeginRestoration(ManifestData),
|
||||||
/// Feed a state chunk to the snapshot service
|
/// Feed a state chunk to the snapshot service
|
||||||
FeedStateChunk(H256, Bytes),
|
FeedStateChunk(H256, Bytes),
|
||||||
/// Feed a block chunk to the snapshot service
|
/// Feed a block chunk to the snapshot service
|
||||||
@ -164,6 +167,11 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
|
|||||||
match *net_message {
|
match *net_message {
|
||||||
ClientIoMessage::BlockVerified => { self.client.import_verified_blocks(); }
|
ClientIoMessage::BlockVerified => { self.client.import_verified_blocks(); }
|
||||||
ClientIoMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(transactions); }
|
ClientIoMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(transactions); }
|
||||||
|
ClientIoMessage::BeginRestoration(ref manifest) => {
|
||||||
|
if let Err(e) = self.snapshot.init_restore(manifest.clone()) {
|
||||||
|
warn!("Failed to initialize snapshot restoration: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => self.snapshot.feed_state_chunk(*hash, chunk),
|
ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => self.snapshot.feed_state_chunk(*hash, chunk),
|
||||||
ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => self.snapshot.feed_block_chunk(*hash, chunk),
|
ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => self.snapshot.feed_block_chunk(*hash, chunk),
|
||||||
_ => {} // ignore other messages
|
_ => {} // ignore other messages
|
||||||
|
@ -90,27 +90,26 @@ impl AbridgedBlock {
|
|||||||
let rlp = UntrustedRlp::new(&self.rlp).decompress(RlpType::Blocks);
|
let rlp = UntrustedRlp::new(&self.rlp).decompress(RlpType::Blocks);
|
||||||
let rlp = UntrustedRlp::new(&rlp);
|
let rlp = UntrustedRlp::new(&rlp);
|
||||||
|
|
||||||
let mut header = Header {
|
let mut header: Header = Default::default();
|
||||||
parent_hash: parent_hash,
|
header.set_parent_hash(parent_hash);
|
||||||
author: try!(rlp.val_at(0)),
|
header.set_author(try!(rlp.val_at(0)));
|
||||||
state_root: try!(rlp.val_at(1)),
|
header.set_state_root(try!(rlp.val_at(1)));
|
||||||
transactions_root: try!(rlp.val_at(2)),
|
header.set_transactions_root(try!(rlp.val_at(2)));
|
||||||
receipts_root: try!(rlp.val_at(3)),
|
header.set_receipts_root(try!(rlp.val_at(3)));
|
||||||
log_bloom: try!(rlp.val_at(4)),
|
header.set_log_bloom(try!(rlp.val_at(4)));
|
||||||
difficulty: try!(rlp.val_at(5)),
|
header.set_difficulty(try!(rlp.val_at(5)));
|
||||||
number: number,
|
header.set_number(number);
|
||||||
gas_limit: try!(rlp.val_at(6)),
|
header.set_gas_limit(try!(rlp.val_at(6)));
|
||||||
gas_used: try!(rlp.val_at(7)),
|
header.set_gas_used(try!(rlp.val_at(7)));
|
||||||
timestamp: try!(rlp.val_at(8)),
|
header.set_timestamp(try!(rlp.val_at(8)));
|
||||||
extra_data: try!(rlp.val_at(9)),
|
header.set_extra_data(try!(rlp.val_at(9)));
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let transactions = try!(rlp.val_at(10));
|
let transactions = try!(rlp.val_at(10));
|
||||||
let uncles: Vec<Header> = try!(rlp.val_at(11));
|
let uncles: Vec<Header> = try!(rlp.val_at(11));
|
||||||
|
|
||||||
let mut uncles_rlp = RlpStream::new();
|
let mut uncles_rlp = RlpStream::new();
|
||||||
uncles_rlp.append(&uncles);
|
uncles_rlp.append(&uncles);
|
||||||
header.uncles_hash = uncles_rlp.as_raw().sha3();
|
header.set_uncles_hash(uncles_rlp.as_raw().sha3());
|
||||||
|
|
||||||
let mut seal_fields = Vec::new();
|
let mut seal_fields = Vec::new();
|
||||||
for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count() {
|
for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count() {
|
||||||
|
@ -501,7 +501,7 @@ impl StateRebuilder {
|
|||||||
|
|
||||||
/// Check for accounts missing code. Once all chunks have been fed, there should
|
/// Check for accounts missing code. Once all chunks have been fed, there should
|
||||||
/// be none.
|
/// be none.
|
||||||
pub fn check_missing(&self) -> Result<(), Error> {
|
pub fn check_missing(self) -> Result<(), Error> {
|
||||||
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
|
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
|
||||||
match missing.is_empty() {
|
match missing.is_empty() {
|
||||||
true => Ok(()),
|
true => Ok(()),
|
||||||
@ -640,8 +640,8 @@ impl BlockRebuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Glue together any disconnected chunks. To be called at the end.
|
/// Glue together any disconnected chunks. To be called at the end.
|
||||||
pub fn glue_chunks(&mut self) {
|
pub fn glue_chunks(self) {
|
||||||
for &(ref first_num, ref first_hash) in &self.disconnected {
|
for (first_num, first_hash) in self.disconnected {
|
||||||
let parent_num = first_num - 1;
|
let parent_num = first_num - 1;
|
||||||
|
|
||||||
// check if the parent is even in the chain.
|
// check if the parent is even in the chain.
|
||||||
@ -649,7 +649,7 @@ impl BlockRebuilder {
|
|||||||
// the first block of the first chunks has nothing to connect to.
|
// the first block of the first chunks has nothing to connect to.
|
||||||
if let Some(parent_hash) = self.chain.block_hash(parent_num) {
|
if let Some(parent_hash) = self.chain.block_hash(parent_num) {
|
||||||
// if so, add the child to it.
|
// if so, add the child to it.
|
||||||
self.chain.add_child(parent_hash, *first_hash);
|
self.chain.add_child(parent_hash, first_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ use std::sync::Arc;
|
|||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use super::{ManifestData, StateRebuilder, BlockRebuilder};
|
use super::{ManifestData, StateRebuilder, BlockRebuilder};
|
||||||
use super::io::{SnapshotReader, LooseReader};
|
use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter};
|
||||||
|
|
||||||
use blockchain::BlockChain;
|
use blockchain::BlockChain;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
@ -34,7 +34,7 @@ use spec::Spec;
|
|||||||
|
|
||||||
use io::IoChannel;
|
use io::IoChannel;
|
||||||
|
|
||||||
use util::{Bytes, H256, Mutex, UtilError};
|
use util::{Bytes, H256, Mutex, RwLock, UtilError};
|
||||||
use util::journaldb::Algorithm;
|
use util::journaldb::Algorithm;
|
||||||
use util::kvdb::{Database, DatabaseConfig};
|
use util::kvdb::{Database, DatabaseConfig};
|
||||||
use util::snappy;
|
use util::snappy;
|
||||||
@ -50,8 +50,6 @@ pub enum RestorationStatus {
|
|||||||
Failed,
|
Failed,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restoration info.
|
|
||||||
|
|
||||||
/// The interface for a snapshot network service.
|
/// The interface for a snapshot network service.
|
||||||
/// This handles:
|
/// This handles:
|
||||||
/// - restoration of snapshots to temporary databases.
|
/// - restoration of snapshots to temporary databases.
|
||||||
@ -74,8 +72,10 @@ pub trait SnapshotService {
|
|||||||
/// Begin snapshot restoration.
|
/// Begin snapshot restoration.
|
||||||
/// If restoration in-progress, this will reset it.
|
/// If restoration in-progress, this will reset it.
|
||||||
/// From this point on, any previous snapshot may become unavailable.
|
/// From this point on, any previous snapshot may become unavailable.
|
||||||
/// Returns true if successful, false otherwise.
|
fn begin_restore(&self, manifest: ManifestData);
|
||||||
fn begin_restore(&self, manifest: ManifestData) -> bool;
|
|
||||||
|
/// Abort an in-progress restoration if there is one.
|
||||||
|
fn abort_restore(&self);
|
||||||
|
|
||||||
/// Feed a raw state chunk to the service to be processed asynchronously.
|
/// Feed a raw state chunk to the service to be processed asynchronously.
|
||||||
/// no-op if not currently restoring.
|
/// no-op if not currently restoring.
|
||||||
@ -88,51 +88,59 @@ pub trait SnapshotService {
|
|||||||
|
|
||||||
/// State restoration manager.
|
/// State restoration manager.
|
||||||
struct Restoration {
|
struct Restoration {
|
||||||
|
manifest: ManifestData,
|
||||||
state_chunks_left: HashSet<H256>,
|
state_chunks_left: HashSet<H256>,
|
||||||
block_chunks_left: HashSet<H256>,
|
block_chunks_left: HashSet<H256>,
|
||||||
state: StateRebuilder,
|
state: StateRebuilder,
|
||||||
blocks: BlockRebuilder,
|
blocks: BlockRebuilder,
|
||||||
|
writer: LooseWriter,
|
||||||
snappy_buffer: Bytes,
|
snappy_buffer: Bytes,
|
||||||
final_state_root: H256,
|
final_state_root: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RestorationParams<'a> {
|
||||||
|
manifest: ManifestData, // manifest to base restoration on.
|
||||||
|
pruning: Algorithm, // pruning algorithm for the database.
|
||||||
|
db_path: PathBuf, // database path
|
||||||
|
writer: LooseWriter, // writer for recovered snapshot.
|
||||||
|
genesis: &'a [u8], // genesis block of the chain.
|
||||||
|
}
|
||||||
|
|
||||||
impl Restoration {
|
impl Restoration {
|
||||||
// make a new restoration, building databases in the given path.
|
// make a new restoration using the given parameters.
|
||||||
fn new(manifest: &ManifestData, pruning: Algorithm, path: &Path, gb: &[u8]) -> Result<Self, Error> {
|
fn new(params: RestorationParams) -> Result<Self, Error> {
|
||||||
|
let manifest = params.manifest;
|
||||||
|
|
||||||
|
let state_chunks = manifest.state_hashes.iter().cloned().collect();
|
||||||
|
let block_chunks = manifest.block_hashes.iter().cloned().collect();
|
||||||
|
|
||||||
let cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
let cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||||
let raw_db = Arc::new(try!(Database::open(&cfg, &*path.to_string_lossy())
|
let raw_db = Arc::new(try!(Database::open(&cfg, &*params.db_path.to_string_lossy())
|
||||||
.map_err(UtilError::SimpleString)));
|
.map_err(UtilError::SimpleString)));
|
||||||
|
|
||||||
let chain = BlockChain::new(Default::default(), gb, raw_db.clone());
|
let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone());
|
||||||
let blocks = try!(BlockRebuilder::new(chain, manifest.block_number));
|
let blocks = try!(BlockRebuilder::new(chain, manifest.block_number));
|
||||||
|
|
||||||
|
let root = manifest.state_root.clone();
|
||||||
Ok(Restoration {
|
Ok(Restoration {
|
||||||
state_chunks_left: manifest.state_hashes.iter().cloned().collect(),
|
manifest: manifest,
|
||||||
block_chunks_left: manifest.block_hashes.iter().cloned().collect(),
|
state_chunks_left: state_chunks,
|
||||||
state: StateRebuilder::new(raw_db, pruning),
|
block_chunks_left: block_chunks,
|
||||||
|
state: StateRebuilder::new(raw_db, params.pruning),
|
||||||
blocks: blocks,
|
blocks: blocks,
|
||||||
|
writer: params.writer,
|
||||||
snappy_buffer: Vec::new(),
|
snappy_buffer: Vec::new(),
|
||||||
final_state_root: manifest.state_root,
|
final_state_root: root,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// feeds a state chunk
|
// feeds a state chunk
|
||||||
fn feed_state(&mut self, hash: H256, chunk: &[u8]) -> Result<(), Error> {
|
fn feed_state(&mut self, hash: H256, chunk: &[u8]) -> Result<(), Error> {
|
||||||
use util::trie::TrieError;
|
|
||||||
|
|
||||||
if self.state_chunks_left.remove(&hash) {
|
if self.state_chunks_left.remove(&hash) {
|
||||||
let len = try!(snappy::decompress_into(&chunk, &mut self.snappy_buffer));
|
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
|
||||||
|
|
||||||
try!(self.state.feed(&self.snappy_buffer[..len]));
|
try!(self.state.feed(&self.snappy_buffer[..len]));
|
||||||
|
try!(self.writer.write_state_chunk(hash, chunk));
|
||||||
if self.state_chunks_left.is_empty() {
|
|
||||||
try!(self.state.check_missing());
|
|
||||||
|
|
||||||
let root = self.state.state_root();
|
|
||||||
if root != self.final_state_root {
|
|
||||||
warn!("Final restored state has wrong state root: expected {:?}, got {:?}", root, self.final_state_root);
|
|
||||||
return Err(TrieError::InvalidStateRoot(root).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -141,14 +149,35 @@ impl Restoration {
|
|||||||
// feeds a block chunk
|
// feeds a block chunk
|
||||||
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine) -> Result<(), Error> {
|
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine) -> Result<(), Error> {
|
||||||
if self.block_chunks_left.remove(&hash) {
|
if self.block_chunks_left.remove(&hash) {
|
||||||
let len = try!(snappy::decompress_into(&chunk, &mut self.snappy_buffer));
|
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
|
||||||
try!(self.blocks.feed(&self.snappy_buffer[..len], engine));
|
|
||||||
|
try!(self.blocks.feed(&self.snappy_buffer[..len], engine));
|
||||||
|
try!(self.writer.write_block_chunk(hash, chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish up restoration.
|
||||||
|
fn finalize(self) -> Result<(), Error> {
|
||||||
|
use util::trie::TrieError;
|
||||||
|
|
||||||
|
if !self.is_done() { return Ok(()) }
|
||||||
|
|
||||||
|
// verify final state root.
|
||||||
|
let root = self.state.state_root();
|
||||||
|
if root != self.final_state_root {
|
||||||
|
warn!("Final restored state has wrong state root: expected {:?}, got {:?}", root, self.final_state_root);
|
||||||
|
return Err(TrieError::InvalidStateRoot(root).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for missing code.
|
||||||
|
try!(self.state.check_missing());
|
||||||
|
|
||||||
if self.block_chunks_left.is_empty() {
|
|
||||||
// connect out-of-order chunks.
|
// connect out-of-order chunks.
|
||||||
self.blocks.glue_chunks();
|
self.blocks.glue_chunks();
|
||||||
}
|
|
||||||
}
|
try!(self.writer.finish(self.manifest));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -174,7 +203,7 @@ pub struct Service {
|
|||||||
io_channel: Channel,
|
io_channel: Channel,
|
||||||
pruning: Algorithm,
|
pruning: Algorithm,
|
||||||
status: Mutex<RestorationStatus>,
|
status: Mutex<RestorationStatus>,
|
||||||
reader: Option<LooseReader>,
|
reader: RwLock<Option<LooseReader>>,
|
||||||
engine: Arc<Engine>,
|
engine: Arc<Engine>,
|
||||||
genesis_block: Bytes,
|
genesis_block: Bytes,
|
||||||
state_chunks: AtomicUsize,
|
state_chunks: AtomicUsize,
|
||||||
@ -190,6 +219,7 @@ impl Service {
|
|||||||
let reader = {
|
let reader = {
|
||||||
let mut snapshot_path = db_path.clone();
|
let mut snapshot_path = db_path.clone();
|
||||||
snapshot_path.push("snapshot");
|
snapshot_path.push("snapshot");
|
||||||
|
snapshot_path.push("current");
|
||||||
|
|
||||||
LooseReader::new(snapshot_path).ok()
|
LooseReader::new(snapshot_path).ok()
|
||||||
};
|
};
|
||||||
@ -201,15 +231,15 @@ impl Service {
|
|||||||
io_channel: io_channel,
|
io_channel: io_channel,
|
||||||
pruning: pruning,
|
pruning: pruning,
|
||||||
status: Mutex::new(RestorationStatus::Inactive),
|
status: Mutex::new(RestorationStatus::Inactive),
|
||||||
reader: reader,
|
reader: RwLock::new(reader),
|
||||||
engine: spec.engine.clone(),
|
engine: spec.engine.clone(),
|
||||||
genesis_block: spec.genesis_block(),
|
genesis_block: spec.genesis_block(),
|
||||||
state_chunks: AtomicUsize::new(0),
|
state_chunks: AtomicUsize::new(0),
|
||||||
block_chunks: AtomicUsize::new(0),
|
block_chunks: AtomicUsize::new(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the snapshot dir if it doesn't exist.
|
// create the root snapshot dir if it doesn't exist.
|
||||||
if let Err(e) = fs::create_dir_all(service.snapshot_dir()) {
|
if let Err(e) = fs::create_dir_all(service.root_dir()) {
|
||||||
if e.kind() != ErrorKind::AlreadyExists {
|
if e.kind() != ErrorKind::AlreadyExists {
|
||||||
return Err(e.into())
|
return Err(e.into())
|
||||||
}
|
}
|
||||||
@ -225,16 +255,23 @@ impl Service {
|
|||||||
Ok(service)
|
Ok(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the snapshot path.
|
// get the root path.
|
||||||
fn snapshot_dir(&self) -> PathBuf {
|
fn root_dir(&self) -> PathBuf {
|
||||||
let mut dir = self.db_path.clone();
|
let mut dir = self.db_path.clone();
|
||||||
dir.push("snapshot");
|
dir.push("snapshot");
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the current snapshot dir.
|
||||||
|
fn snapshot_dir(&self) -> PathBuf {
|
||||||
|
let mut dir = self.root_dir();
|
||||||
|
dir.push("current");
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
// get the restoration directory.
|
// get the restoration directory.
|
||||||
fn restoration_dir(&self) -> PathBuf {
|
fn restoration_dir(&self) -> PathBuf {
|
||||||
let mut dir = self.snapshot_dir();
|
let mut dir = self.root_dir();
|
||||||
dir.push("restoration");
|
dir.push("restoration");
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
@ -246,6 +283,13 @@ impl Service {
|
|||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// temporary snapshot recovery path.
|
||||||
|
fn temp_recovery_dir(&self) -> PathBuf {
|
||||||
|
let mut dir = self.restoration_dir();
|
||||||
|
dir.push("temp");
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
// replace one the client's database with our own.
|
// replace one the client's database with our own.
|
||||||
fn replace_client_db(&self) -> Result<(), Error> {
|
fn replace_client_db(&self) -> Result<(), Error> {
|
||||||
let our_db = self.restoration_db();
|
let our_db = self.restoration_db();
|
||||||
@ -284,6 +328,42 @@ impl Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize the restoration synchronously.
|
||||||
|
pub fn init_restore(&self, manifest: ManifestData) -> Result<(), Error> {
|
||||||
|
let rest_dir = self.restoration_dir();
|
||||||
|
|
||||||
|
let mut res = self.restoration.lock();
|
||||||
|
|
||||||
|
// tear down existing restoration.
|
||||||
|
*res = None;
|
||||||
|
|
||||||
|
// delete and restore the restoration dir.
|
||||||
|
if let Err(e) = fs::remove_dir_all(&rest_dir) {
|
||||||
|
match e.kind() {
|
||||||
|
ErrorKind::NotFound => {},
|
||||||
|
_ => return Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(fs::create_dir_all(&rest_dir));
|
||||||
|
|
||||||
|
// make new restoration.
|
||||||
|
let writer = try!(LooseWriter::new(self.temp_recovery_dir()));
|
||||||
|
|
||||||
|
let params = RestorationParams {
|
||||||
|
manifest: manifest,
|
||||||
|
pruning: self.pruning,
|
||||||
|
db_path: self.restoration_db(),
|
||||||
|
writer: writer,
|
||||||
|
genesis: &self.genesis_block,
|
||||||
|
};
|
||||||
|
|
||||||
|
*res = Some(try!(Restoration::new(params)));
|
||||||
|
|
||||||
|
*self.status.lock() = RestorationStatus::Ongoing;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// finalize the restoration. this accepts an already-locked
|
// finalize the restoration. this accepts an already-locked
|
||||||
// restoration as an argument -- so acquiring it again _will_
|
// restoration as an argument -- so acquiring it again _will_
|
||||||
// lead to deadlock.
|
// lead to deadlock.
|
||||||
@ -293,27 +373,52 @@ impl Service {
|
|||||||
self.state_chunks.store(0, Ordering::SeqCst);
|
self.state_chunks.store(0, Ordering::SeqCst);
|
||||||
self.block_chunks.store(0, Ordering::SeqCst);
|
self.block_chunks.store(0, Ordering::SeqCst);
|
||||||
|
|
||||||
// destroy the restoration before replacing databases.
|
// destroy the restoration before replacing databases and snapshot.
|
||||||
*rest = None;
|
try!(rest.take().map(Restoration::finalize).unwrap_or(Ok(())));
|
||||||
|
|
||||||
try!(self.replace_client_db());
|
try!(self.replace_client_db());
|
||||||
|
|
||||||
*self.status.lock() = RestorationStatus::Inactive;
|
let mut reader = self.reader.write();
|
||||||
|
*reader = None; // destroy the old reader if it existed.
|
||||||
|
|
||||||
|
let snapshot_dir = self.snapshot_dir();
|
||||||
|
|
||||||
|
trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy());
|
||||||
|
if let Err(e) = fs::remove_dir_all(&snapshot_dir) {
|
||||||
|
match e.kind() {
|
||||||
|
ErrorKind::NotFound => {}
|
||||||
|
_ => return Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(fs::create_dir(&snapshot_dir));
|
||||||
|
|
||||||
|
trace!(target: "snapshot", "copying restored snapshot files over");
|
||||||
|
for maybe_file in try!(fs::read_dir(self.temp_recovery_dir())) {
|
||||||
|
let path = try!(maybe_file).path();
|
||||||
|
if let Some(name) = path.file_name().map(|x| x.to_owned()) {
|
||||||
|
let mut new_path = snapshot_dir.clone();
|
||||||
|
new_path.push(name);
|
||||||
|
try!(fs::rename(path, new_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: take control of restored snapshot.
|
|
||||||
let _ = fs::remove_dir_all(self.restoration_dir());
|
let _ = fs::remove_dir_all(self.restoration_dir());
|
||||||
|
|
||||||
|
*reader = Some(try!(LooseReader::new(snapshot_dir)));
|
||||||
|
|
||||||
|
*self.status.lock() = RestorationStatus::Inactive;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Feed a chunk of either kind. no-op if no restoration or status is wrong.
|
/// Feed a chunk of either kind. no-op if no restoration or status is wrong.
|
||||||
fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> {
|
fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> {
|
||||||
match self.status() {
|
|
||||||
RestorationStatus::Inactive | RestorationStatus::Failed => Ok(()),
|
|
||||||
RestorationStatus::Ongoing => {
|
|
||||||
// TODO: be able to process block chunks and state chunks at same time?
|
// TODO: be able to process block chunks and state chunks at same time?
|
||||||
let mut restoration = self.restoration.lock();
|
let mut restoration = self.restoration.lock();
|
||||||
|
|
||||||
|
match self.status() {
|
||||||
|
RestorationStatus::Inactive | RestorationStatus::Failed => Ok(()),
|
||||||
|
RestorationStatus::Ongoing => {
|
||||||
let res = {
|
let res = {
|
||||||
let rest = match *restoration {
|
let rest = match *restoration {
|
||||||
Some(ref mut r) => r,
|
Some(ref mut r) => r,
|
||||||
@ -373,11 +478,11 @@ impl Service {
|
|||||||
|
|
||||||
impl SnapshotService for Service {
|
impl SnapshotService for Service {
|
||||||
fn manifest(&self) -> Option<ManifestData> {
|
fn manifest(&self) -> Option<ManifestData> {
|
||||||
self.reader.as_ref().map(|r| r.manifest().clone())
|
self.reader.read().as_ref().map(|r| r.manifest().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk(&self, hash: H256) -> Option<Bytes> {
|
fn chunk(&self, hash: H256) -> Option<Bytes> {
|
||||||
self.reader.as_ref().and_then(|r| r.chunk(hash).ok())
|
self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status(&self) -> RestorationStatus {
|
fn status(&self) -> RestorationStatus {
|
||||||
@ -388,39 +493,22 @@ impl SnapshotService for Service {
|
|||||||
(self.state_chunks.load(Ordering::Relaxed), self.block_chunks.load(Ordering::Relaxed))
|
(self.state_chunks.load(Ordering::Relaxed), self.block_chunks.load(Ordering::Relaxed))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_restore(&self, manifest: ManifestData) -> bool {
|
fn begin_restore(&self, manifest: ManifestData) {
|
||||||
let rest_dir = self.restoration_dir();
|
self.io_channel.send(ClientIoMessage::BeginRestoration(manifest))
|
||||||
|
.expect("snapshot service and io service are kept alive by client service; qed");
|
||||||
|
}
|
||||||
|
|
||||||
let mut res = self.restoration.lock();
|
fn abort_restore(&self) {
|
||||||
|
*self.restoration.lock() = None;
|
||||||
// tear down existing restoration.
|
*self.status.lock() = RestorationStatus::Inactive;
|
||||||
*res = None;
|
if let Err(e) = fs::remove_dir_all(&self.restoration_dir()) {
|
||||||
|
|
||||||
// delete and restore the restoration dir.
|
|
||||||
if let Err(e) = fs::remove_dir_all(&rest_dir).and_then(|_| fs::create_dir_all(&rest_dir)) {
|
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
ErrorKind::NotFound => {},
|
ErrorKind::NotFound => {},
|
||||||
_ => {
|
_ => warn!("encountered error {} while deleting snapshot restoration dir.", e),
|
||||||
warn!("encountered error {} while beginning snapshot restoration.", e);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make new restoration.
|
|
||||||
let db_path = self.restoration_db();
|
|
||||||
*res = match Restoration::new(&manifest, self.pruning, &db_path, &self.genesis_block) {
|
|
||||||
Ok(b) => Some(b),
|
|
||||||
Err(e) => {
|
|
||||||
warn!("encountered error {} while beginning snapshot restoration.", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
*self.status.lock() = RestorationStatus::Ongoing;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
|
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
|
||||||
self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk))
|
self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk))
|
||||||
.expect("snapshot service and io service are kept alive by client service; qed");
|
.expect("snapshot service and io service are kept alive by client service; qed");
|
||||||
|
@ -79,7 +79,6 @@ fn chunk_and_restore(amount: u64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rebuilder.glue_chunks();
|
rebuilder.glue_chunks();
|
||||||
drop(rebuilder);
|
|
||||||
|
|
||||||
// and test it.
|
// and test it.
|
||||||
let new_chain = BlockChain::new(Default::default(), &genesis, new_db);
|
let new_chain = BlockChain::new(Default::default(), &genesis, new_db);
|
||||||
|
@ -72,8 +72,9 @@ fn snap_and_restore() {
|
|||||||
rebuilder.feed(&chunk).unwrap();
|
rebuilder.feed(&chunk).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuilder.check_missing().unwrap();
|
|
||||||
assert_eq!(rebuilder.state_root(), state_root);
|
assert_eq!(rebuilder.state_root(), state_root);
|
||||||
|
rebuilder.check_missing().unwrap();
|
||||||
|
|
||||||
new_db
|
new_db
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@ use super::seal::Generic as GenericSeal;
|
|||||||
use ethereum;
|
use ethereum;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
/// Parameters common to all engines.
|
/// Parameters common to all engines.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
#[cfg_attr(test, derive(Default))]
|
#[cfg_attr(test, derive(Default))]
|
||||||
@ -162,21 +160,21 @@ impl Spec {
|
|||||||
|
|
||||||
/// Get the header of the genesis block.
|
/// Get the header of the genesis block.
|
||||||
pub fn genesis_header(&self) -> Header {
|
pub fn genesis_header(&self) -> Header {
|
||||||
Header {
|
let mut header: Header = Default::default();
|
||||||
parent_hash: self.parent_hash.clone(),
|
header.set_parent_hash(self.parent_hash.clone());
|
||||||
timestamp: self.timestamp,
|
header.set_timestamp(self.timestamp);
|
||||||
number: 0,
|
header.set_number(0);
|
||||||
author: self.author.clone(),
|
header.set_author(self.author.clone());
|
||||||
transactions_root: self.transactions_root.clone(),
|
header.set_transactions_root(self.transactions_root.clone());
|
||||||
uncles_hash: RlpStream::new_list(0).out().sha3(),
|
header.set_uncles_hash(RlpStream::new_list(0).out().sha3());
|
||||||
extra_data: self.extra_data.clone(),
|
header.set_extra_data(self.extra_data.clone());
|
||||||
state_root: self.state_root().clone(),
|
header.set_state_root(self.state_root().clone());
|
||||||
receipts_root: self.receipts_root.clone(),
|
header.set_receipts_root(self.receipts_root.clone());
|
||||||
log_bloom: H2048::new().clone(),
|
header.set_log_bloom(H2048::new().clone());
|
||||||
gas_used: self.gas_used.clone(),
|
header.set_gas_used(self.gas_used.clone());
|
||||||
gas_limit: self.gas_limit.clone(),
|
header.set_gas_limit(self.gas_limit.clone());
|
||||||
difficulty: self.difficulty.clone(),
|
header.set_difficulty(self.difficulty.clone());
|
||||||
seal: {
|
header.set_seal({
|
||||||
let seal = {
|
let seal = {
|
||||||
let mut s = RlpStream::new_list(self.seal_fields);
|
let mut s = RlpStream::new_list(self.seal_fields);
|
||||||
s.append_raw(&self.seal_rlp, self.seal_fields);
|
s.append_raw(&self.seal_rlp, self.seal_fields);
|
||||||
@ -184,10 +182,8 @@ impl Spec {
|
|||||||
};
|
};
|
||||||
let r = Rlp::new(&seal);
|
let r = Rlp::new(&seal);
|
||||||
(0..self.seal_fields).map(|i| r.at(i).as_raw().to_vec()).collect()
|
(0..self.seal_fields).map(|i| r.at(i).as_raw().to_vec()).collect()
|
||||||
},
|
});
|
||||||
hash: RefCell::new(None),
|
return header;
|
||||||
bare_hash: RefCell::new(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compose the genesis block for this chain.
|
/// Compose the genesis block for this chain.
|
||||||
|
@ -45,9 +45,9 @@ fn returns_state_root_basic() {
|
|||||||
let client_result = generate_dummy_client(6);
|
let client_result = generate_dummy_client(6);
|
||||||
let client = client_result.reference();
|
let client = client_result.reference();
|
||||||
let test_spec = get_test_spec();
|
let test_spec = get_test_spec();
|
||||||
let state_root = test_spec.genesis_header().state_root;
|
let genesis_header = test_spec.genesis_header();
|
||||||
|
|
||||||
assert!(client.state_data(&state_root).is_some());
|
assert!(client.state_data(genesis_header.state_root()).is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -84,26 +84,26 @@ pub fn create_test_block(header: &Header) -> Bytes {
|
|||||||
|
|
||||||
fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header {
|
fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header {
|
||||||
let mut header = Header::new();
|
let mut header = Header::new();
|
||||||
header.gas_limit = 0.into();
|
header.set_gas_limit(0.into());
|
||||||
header.difficulty = (order * 100).into();
|
header.set_difficulty((order * 100).into());
|
||||||
header.timestamp = (order * 10) as u64;
|
header.set_timestamp((order * 10) as u64);
|
||||||
header.number = order as u64;
|
header.set_number(order as u64);
|
||||||
header.parent_hash = parent_hash;
|
header.set_parent_hash(parent_hash);
|
||||||
header.state_root = H256::zero();
|
header.set_state_root(H256::zero());
|
||||||
|
|
||||||
header
|
header
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_unverifiable_block_with_extra(order: u32, parent_hash: H256, extra: Option<Bytes>) -> Bytes {
|
fn create_unverifiable_block_with_extra(order: u32, parent_hash: H256, extra: Option<Bytes>) -> Bytes {
|
||||||
let mut header = create_unverifiable_block_header(order, parent_hash);
|
let mut header = create_unverifiable_block_header(order, parent_hash);
|
||||||
header.extra_data = match extra {
|
header.set_extra_data(match extra {
|
||||||
Some(extra_data) => extra_data,
|
Some(extra_data) => extra_data,
|
||||||
None => {
|
None => {
|
||||||
let base = (order & 0x000000ff) as u8;
|
let base = (order & 0x000000ff) as u8;
|
||||||
let generated: Vec<u8> = vec![base + 1, base + 2, base + 3];
|
let generated: Vec<u8> = vec![base + 1, base + 2, base + 3];
|
||||||
generated
|
generated
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
create_test_block(&header)
|
create_test_block(&header)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
|
|||||||
let test_spec = get_test_spec();
|
let test_spec = get_test_spec();
|
||||||
let test_engine = &test_spec.engine;
|
let test_engine = &test_spec.engine;
|
||||||
//let test_engine = test_spec.to_engine().unwrap();
|
//let test_engine = test_spec.to_engine().unwrap();
|
||||||
let state_root = test_spec.genesis_header().state_root;
|
let state_root = test_spec.genesis_header().state_root().clone();
|
||||||
let mut rolling_hash = client.chain_info().best_block_hash;
|
let mut rolling_hash = client.chain_info().best_block_hash;
|
||||||
let mut rolling_block_number = starting_number as u64;
|
let mut rolling_block_number = starting_number as u64;
|
||||||
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10;
|
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10;
|
||||||
@ -212,12 +212,12 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
|
|||||||
for _ in 0..block_number {
|
for _ in 0..block_number {
|
||||||
let mut header = Header::new();
|
let mut header = Header::new();
|
||||||
|
|
||||||
header.gas_limit = test_engine.params().min_gas_limit;
|
header.set_gas_limit(test_engine.params().min_gas_limit);
|
||||||
header.difficulty = U256::from(0x20000);
|
header.set_difficulty(U256::from(0x20000));
|
||||||
header.timestamp = rolling_timestamp;
|
header.set_timestamp(rolling_timestamp);
|
||||||
header.number = rolling_block_number;
|
header.set_number(rolling_block_number);
|
||||||
header.parent_hash = rolling_hash;
|
header.set_parent_hash(rolling_hash);
|
||||||
header.state_root = state_root.clone();
|
header.set_state_root(state_root);
|
||||||
|
|
||||||
rolling_hash = header.hash();
|
rolling_hash = header.hash();
|
||||||
rolling_block_number = rolling_block_number + 1;
|
rolling_block_number = rolling_block_number + 1;
|
||||||
@ -345,12 +345,12 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h
|
|||||||
let mut r = Vec::new();
|
let mut r = Vec::new();
|
||||||
for i in start_number .. start_number + count + 1 {
|
for i in start_number .. start_number + count + 1 {
|
||||||
let mut block_header = Header::new();
|
let mut block_header = Header::new();
|
||||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
block_header.set_gas_limit(test_engine.params().min_gas_limit);
|
||||||
block_header.difficulty = U256::from(i).mul(U256([0, 1, 0, 0]));
|
block_header.set_difficulty(U256::from(i).mul(U256([0, 1, 0, 0])));
|
||||||
block_header.timestamp = rolling_timestamp;
|
block_header.set_timestamp(rolling_timestamp);
|
||||||
block_header.number = i as u64;
|
block_header.set_number(i as u64);
|
||||||
block_header.parent_hash = parent;
|
block_header.set_parent_hash(parent);
|
||||||
block_header.state_root = test_spec.genesis_header().state_root;
|
block_header.set_state_root(test_spec.genesis_header().state_root().clone());
|
||||||
|
|
||||||
parent = block_header.hash();
|
parent = block_header.hash();
|
||||||
rolling_timestamp = rolling_timestamp + 10;
|
rolling_timestamp = rolling_timestamp + 10;
|
||||||
@ -365,12 +365,12 @@ pub fn get_good_dummy_block() -> Bytes {
|
|||||||
let mut block_header = Header::new();
|
let mut block_header = Header::new();
|
||||||
let test_spec = get_test_spec();
|
let test_spec = get_test_spec();
|
||||||
let test_engine = &test_spec.engine;
|
let test_engine = &test_spec.engine;
|
||||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
block_header.set_gas_limit(test_engine.params().min_gas_limit);
|
||||||
block_header.difficulty = U256::from(0x20000);
|
block_header.set_difficulty(U256::from(0x20000));
|
||||||
block_header.timestamp = 40;
|
block_header.set_timestamp(40);
|
||||||
block_header.number = 1;
|
block_header.set_number(1);
|
||||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
block_header.set_parent_hash(test_spec.genesis_header().hash());
|
||||||
block_header.state_root = test_spec.genesis_header().state_root;
|
block_header.set_state_root(test_spec.genesis_header().state_root().clone());
|
||||||
|
|
||||||
create_test_block(&block_header)
|
create_test_block(&block_header)
|
||||||
}
|
}
|
||||||
@ -379,12 +379,12 @@ pub fn get_bad_state_dummy_block() -> Bytes {
|
|||||||
let mut block_header = Header::new();
|
let mut block_header = Header::new();
|
||||||
let test_spec = get_test_spec();
|
let test_spec = get_test_spec();
|
||||||
let test_engine = &test_spec.engine;
|
let test_engine = &test_spec.engine;
|
||||||
block_header.gas_limit = test_engine.params().min_gas_limit;
|
block_header.set_gas_limit(test_engine.params().min_gas_limit);
|
||||||
block_header.difficulty = U256::from(0x20000);
|
block_header.set_difficulty(U256::from(0x20000));
|
||||||
block_header.timestamp = 40;
|
block_header.set_timestamp(40);
|
||||||
block_header.number = 1;
|
block_header.set_number(1);
|
||||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
block_header.set_parent_hash(test_spec.genesis_header().hash());
|
||||||
block_header.state_root = 0xbad.into();
|
block_header.set_state_root(0xbad.into());
|
||||||
|
|
||||||
create_test_block(&block_header)
|
create_test_block(&block_header)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ pub struct PreverifiedBlock {
|
|||||||
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
|
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
|
||||||
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
||||||
try!(verify_header(&header, engine));
|
try!(verify_header(&header, engine));
|
||||||
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
try!(verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash()));
|
||||||
try!(engine.verify_block_basic(&header, Some(bytes)));
|
try!(engine.verify_block_basic(&header, Some(bytes)));
|
||||||
for u in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
for u in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
let u = try!(u);
|
let u = try!(u);
|
||||||
@ -81,7 +81,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) ->
|
|||||||
/// Phase 3 verification. Check block information against parent and uncles.
|
/// 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> {
|
pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
|
||||||
// TODO: verify timestamp
|
// TODO: verify timestamp
|
||||||
let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone()))));
|
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!(verify_parent(&header, &parent));
|
||||||
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
|
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
|||||||
|
|
||||||
let mut excluded = HashSet::new();
|
let mut excluded = HashSet::new();
|
||||||
excluded.insert(header.hash());
|
excluded.insert(header.hash());
|
||||||
let mut hash = header.parent_hash.clone();
|
let mut hash = header.parent_hash().clone();
|
||||||
excluded.insert(hash.clone());
|
excluded.insert(hash.clone());
|
||||||
for _ in 0..engine.maximum_uncle_age() {
|
for _ in 0..engine.maximum_uncle_age() {
|
||||||
match bc.block_details(&hash) {
|
match bc.block_details(&hash) {
|
||||||
@ -122,12 +122,12 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
|||||||
// 6 7
|
// 6 7
|
||||||
// (8 Invalid)
|
// (8 Invalid)
|
||||||
|
|
||||||
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
|
let depth = if header.number() > uncle.number() { header.number() - uncle.number() } else { 0 };
|
||||||
if depth > engine.maximum_uncle_age() as u64 {
|
if depth > engine.maximum_uncle_age() as u64 {
|
||||||
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
|
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() })));
|
||||||
}
|
}
|
||||||
else if depth < 1 {
|
else if depth < 1 {
|
||||||
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
|
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() })));
|
||||||
}
|
}
|
||||||
|
|
||||||
// cB
|
// cB
|
||||||
@ -139,8 +139,8 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
|||||||
// cB.p^6 -----------/ 6
|
// cB.p^6 -----------/ 6
|
||||||
// cB.p^7 -------------/
|
// cB.p^7 -------------/
|
||||||
// cB.p^8
|
// cB.p^8
|
||||||
let mut expected_uncle_parent = header.parent_hash.clone();
|
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 = try!(bc.block_header(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone()))));
|
||||||
for _ in 0..depth {
|
for _ in 0..depth {
|
||||||
match bc.block_details(&expected_uncle_parent) {
|
match bc.block_details(&expected_uncle_parent) {
|
||||||
Some(details) => {
|
Some(details) => {
|
||||||
@ -162,50 +162,50 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
|||||||
|
|
||||||
/// Phase 4 verification. Check block information against transaction enactment results,
|
/// Phase 4 verification. Check block information against transaction enactment results,
|
||||||
pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
|
pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
|
||||||
if expected.gas_used != got.gas_used {
|
if expected.gas_used() != got.gas_used() {
|
||||||
return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used, found: got.gas_used })))
|
return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used().clone(), found: got.gas_used().clone() })))
|
||||||
}
|
}
|
||||||
if expected.log_bloom != got.log_bloom {
|
if expected.log_bloom() != got.log_bloom() {
|
||||||
return Err(From::from(BlockError::InvalidLogBloom(Mismatch { expected: expected.log_bloom.clone(), found: got.log_bloom.clone() })))
|
return Err(From::from(BlockError::InvalidLogBloom(Mismatch { expected: expected.log_bloom().clone(), found: got.log_bloom().clone() })))
|
||||||
}
|
}
|
||||||
if expected.state_root != got.state_root {
|
if expected.state_root() != got.state_root() {
|
||||||
return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root.clone(), found: got.state_root.clone() })))
|
return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root().clone(), found: got.state_root().clone() })))
|
||||||
}
|
}
|
||||||
if expected.receipts_root != got.receipts_root {
|
if expected.receipts_root() != got.receipts_root() {
|
||||||
return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: expected.receipts_root.clone(), found: got.receipts_root.clone() })))
|
return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: expected.receipts_root().clone(), found: got.receipts_root().clone() })))
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check basic header parameters.
|
/// Check basic header parameters.
|
||||||
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
|
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
|
||||||
if header.number >= From::from(BlockNumber::max_value()) {
|
if header.number() >= From::from(BlockNumber::max_value()) {
|
||||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
|
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() })))
|
||||||
}
|
}
|
||||||
if header.gas_used > header.gas_limit {
|
if header.gas_used() > header.gas_limit() {
|
||||||
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
|
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit().clone()), min: None, found: header.gas_used().clone() })));
|
||||||
}
|
}
|
||||||
let min_gas_limit = engine.params().min_gas_limit;
|
let min_gas_limit = engine.params().min_gas_limit;
|
||||||
if header.gas_limit < min_gas_limit {
|
if header.gas_limit() < &min_gas_limit {
|
||||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })));
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit().clone() })));
|
||||||
}
|
}
|
||||||
let maximum_extra_data_size = engine.maximum_extra_data_size();
|
let maximum_extra_data_size = engine.maximum_extra_data_size();
|
||||||
if header.number != 0 && header.extra_data.len() > maximum_extra_data_size {
|
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
|
||||||
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() })));
|
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() })));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check header parameters agains parent header.
|
/// Check header parameters agains parent header.
|
||||||
fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
|
fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
|
||||||
if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash {
|
if !header.parent_hash().is_zero() && &parent.hash() != header.parent_hash() {
|
||||||
return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() })))
|
return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash().clone() })))
|
||||||
}
|
}
|
||||||
if header.timestamp <= parent.timestamp {
|
if header.timestamp() <= parent.timestamp() {
|
||||||
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
|
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp() + 1), found: header.timestamp() })))
|
||||||
}
|
}
|
||||||
if header.number != parent.number + 1 {
|
if header.number() != parent.number() + 1 {
|
||||||
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number })));
|
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() })));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -307,9 +307,9 @@ mod tests {
|
|||||||
self.blocks.get(hash).map(|bytes| {
|
self.blocks.get(hash).map(|bytes| {
|
||||||
let header = BlockView::new(bytes).header();
|
let header = BlockView::new(bytes).header();
|
||||||
BlockDetails {
|
BlockDetails {
|
||||||
number: header.number,
|
number: header.number(),
|
||||||
total_difficulty: header.difficulty,
|
total_difficulty: header.difficulty().clone(),
|
||||||
parent: header.parent_hash,
|
parent: header.parent_hash().clone(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -352,9 +352,9 @@ mod tests {
|
|||||||
let engine = &*spec.engine;
|
let engine = &*spec.engine;
|
||||||
|
|
||||||
let min_gas_limit = engine.params().min_gas_limit;
|
let min_gas_limit = engine.params().min_gas_limit;
|
||||||
good.gas_limit = min_gas_limit;
|
good.set_gas_limit(min_gas_limit);
|
||||||
good.timestamp = 40;
|
good.set_timestamp(40);
|
||||||
good.number = 10;
|
good.set_number(10);
|
||||||
|
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
|
|
||||||
@ -381,31 +381,31 @@ mod tests {
|
|||||||
let diff_inc = U256::from(0x40);
|
let diff_inc = U256::from(0x40);
|
||||||
|
|
||||||
let mut parent6 = good.clone();
|
let mut parent6 = good.clone();
|
||||||
parent6.number = 6;
|
parent6.set_number(6);
|
||||||
let mut parent7 = good.clone();
|
let mut parent7 = good.clone();
|
||||||
parent7.number = 7;
|
parent7.set_number(7);
|
||||||
parent7.parent_hash = parent6.hash();
|
parent7.set_parent_hash(parent6.hash());
|
||||||
parent7.difficulty = parent6.difficulty + diff_inc;
|
parent7.set_difficulty(parent6.difficulty().clone() + diff_inc);
|
||||||
parent7.timestamp = parent6.timestamp + 10;
|
parent7.set_timestamp(parent6.timestamp() + 10);
|
||||||
let mut parent8 = good.clone();
|
let mut parent8 = good.clone();
|
||||||
parent8.number = 8;
|
parent8.set_number(8);
|
||||||
parent8.parent_hash = parent7.hash();
|
parent8.set_parent_hash(parent7.hash());
|
||||||
parent8.difficulty = parent7.difficulty + diff_inc;
|
parent8.set_difficulty(parent7.difficulty().clone() + diff_inc);
|
||||||
parent8.timestamp = parent7.timestamp + 10;
|
parent8.set_timestamp(parent7.timestamp() + 10);
|
||||||
|
|
||||||
let mut good_uncle1 = good.clone();
|
let mut good_uncle1 = good.clone();
|
||||||
good_uncle1.number = 9;
|
good_uncle1.set_number(9);
|
||||||
good_uncle1.parent_hash = parent8.hash();
|
good_uncle1.set_parent_hash(parent8.hash());
|
||||||
good_uncle1.difficulty = parent8.difficulty + diff_inc;
|
good_uncle1.set_difficulty(parent8.difficulty().clone() + diff_inc);
|
||||||
good_uncle1.timestamp = parent8.timestamp + 10;
|
good_uncle1.set_timestamp(parent8.timestamp() + 10);
|
||||||
good_uncle1.extra_data.push(1u8);
|
good_uncle1.extra_data_mut().push(1u8);
|
||||||
|
|
||||||
let mut good_uncle2 = good.clone();
|
let mut good_uncle2 = good.clone();
|
||||||
good_uncle2.number = 8;
|
good_uncle2.set_number(8);
|
||||||
good_uncle2.parent_hash = parent7.hash();
|
good_uncle2.set_parent_hash(parent7.hash());
|
||||||
good_uncle2.difficulty = parent7.difficulty + diff_inc;
|
good_uncle2.set_difficulty(parent7.difficulty().clone() + diff_inc);
|
||||||
good_uncle2.timestamp = parent7.timestamp + 10;
|
good_uncle2.set_timestamp(parent7.timestamp() + 10);
|
||||||
good_uncle2.extra_data.push(2u8);
|
good_uncle2.extra_data_mut().push(2u8);
|
||||||
|
|
||||||
let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ];
|
let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ];
|
||||||
let mut uncles_rlp = RlpStream::new();
|
let mut uncles_rlp = RlpStream::new();
|
||||||
@ -414,14 +414,14 @@ mod tests {
|
|||||||
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| encode::<SignedTransaction>(t).to_vec()).collect());
|
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| encode::<SignedTransaction>(t).to_vec()).collect());
|
||||||
|
|
||||||
let mut parent = good.clone();
|
let mut parent = good.clone();
|
||||||
parent.number = 9;
|
parent.set_number(9);
|
||||||
parent.timestamp = parent8.timestamp + 10;
|
parent.set_timestamp(parent8.timestamp() + 10);
|
||||||
parent.parent_hash = parent8.hash();
|
parent.set_parent_hash(parent8.hash());
|
||||||
parent.difficulty = parent8.difficulty + diff_inc;
|
parent.set_difficulty(parent8.difficulty().clone() + diff_inc);
|
||||||
|
|
||||||
good.parent_hash = parent.hash();
|
good.set_parent_hash(parent.hash());
|
||||||
good.difficulty = parent.difficulty + diff_inc;
|
good.set_difficulty(parent.difficulty().clone() + diff_inc);
|
||||||
good.timestamp = parent.timestamp + 10;
|
good.set_timestamp(parent.timestamp() + 10);
|
||||||
|
|
||||||
let mut bc = TestBlockChain::new();
|
let mut bc = TestBlockChain::new();
|
||||||
bc.insert(create_test_block(&good));
|
bc.insert(create_test_block(&good));
|
||||||
@ -433,61 +433,62 @@ mod tests {
|
|||||||
check_ok(basic_test(&create_test_block(&good), engine));
|
check_ok(basic_test(&create_test_block(&good), engine));
|
||||||
|
|
||||||
let mut header = good.clone();
|
let mut header = good.clone();
|
||||||
header.transactions_root = good_transactions_root.clone();
|
header.set_transactions_root(good_transactions_root.clone());
|
||||||
header.uncles_hash = good_uncles_hash.clone();
|
header.set_uncles_hash(good_uncles_hash.clone());
|
||||||
check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine));
|
check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine));
|
||||||
|
|
||||||
header.gas_limit = min_gas_limit - From::from(1);
|
header.set_gas_limit(min_gas_limit - From::from(1));
|
||||||
check_fail(basic_test(&create_test_block(&header), engine),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }));
|
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit().clone() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.number = BlockNumber::max_value();
|
header.set_number(BlockNumber::max_value());
|
||||||
check_fail(basic_test(&create_test_block(&header), engine),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
|
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.gas_used = header.gas_limit + From::from(1);
|
let gas_used = header.gas_limit().clone() + 1.into();
|
||||||
|
header.set_gas_used(gas_used);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }));
|
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit().clone()), min: None, found: header.gas_used().clone() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
|
header.extra_data_mut().resize(engine.maximum_extra_data_size() + 1, 0u8);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
|
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data().len() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
|
header.extra_data_mut().resize(engine.maximum_extra_data_size() + 1, 0u8);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
|
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data().len() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.uncles_hash = good_uncles_hash.clone();
|
header.set_uncles_hash(good_uncles_hash.clone());
|
||||||
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
|
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
|
||||||
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root }));
|
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root().clone() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.transactions_root = good_transactions_root.clone();
|
header.set_transactions_root(good_transactions_root.clone());
|
||||||
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
|
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
|
||||||
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
|
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash().clone() }));
|
||||||
|
|
||||||
check_ok(family_test(&create_test_block(&good), engine, &bc));
|
check_ok(family_test(&create_test_block(&good), engine, &bc));
|
||||||
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine, &bc));
|
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine, &bc));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.parent_hash = H256::random();
|
header.set_parent_hash(H256::random());
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
||||||
UnknownParent(header.parent_hash));
|
UnknownParent(header.parent_hash().clone()));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.timestamp = 10;
|
header.set_timestamp(10);
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
||||||
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }));
|
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp() + 1), found: header.timestamp() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.number = 9;
|
header.set_number(9);
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
||||||
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
|
InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
let mut bad_uncles = good_uncles.clone();
|
let mut bad_uncles = good_uncles.clone();
|
||||||
|
@ -33,7 +33,7 @@ use service::{HypervisorService, IpcModuleId};
|
|||||||
use std::process::{Command,Child};
|
use std::process::{Command,Child};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub use service::{HypervisorServiceClient, CLIENT_MODULE_ID, SYNC_MODULE_ID};
|
pub use service::{HypervisorServiceClient, ControlService, CLIENT_MODULE_ID, SYNC_MODULE_ID};
|
||||||
|
|
||||||
pub type BinaryId = &'static str;
|
pub type BinaryId = &'static str;
|
||||||
|
|
||||||
@ -174,6 +174,10 @@ impl Hypervisor {
|
|||||||
self.service.unchecked_count() == 0
|
self.service.unchecked_count() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn modules_shutdown(&self) -> bool {
|
||||||
|
self.service.running_count() == 0
|
||||||
|
}
|
||||||
|
|
||||||
/// Waits for every required module to check in
|
/// Waits for every required module to check in
|
||||||
pub fn wait_for_startup(&self) {
|
pub fn wait_for_startup(&self) {
|
||||||
let mut worker = self.ipc_worker.write().unwrap();
|
let mut worker = self.ipc_worker.write().unwrap();
|
||||||
@ -182,21 +186,30 @@ impl Hypervisor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shutdown the ipc and all managed child processes
|
/// Waits for every required module to check in
|
||||||
pub fn shutdown(&self, wait_time: Option<std::time::Duration>) {
|
pub fn wait_for_shutdown(&self) {
|
||||||
if wait_time.is_some() { std::thread::sleep(wait_time.unwrap()) }
|
let mut worker = self.ipc_worker.write().unwrap();
|
||||||
|
while !self.modules_shutdown() {
|
||||||
let mut childs = self.processes.write().unwrap();
|
worker.poll()
|
||||||
for (ref mut module, ref mut child) in childs.iter_mut() {
|
|
||||||
trace!(target: "hypervisor", "Stopping process module: {}", module);
|
|
||||||
child.kill().unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shutdown the ipc and all managed child processes
|
||||||
|
pub fn shutdown(&self) {
|
||||||
|
let mut childs = self.processes.write().unwrap();
|
||||||
|
for (ref mut module, _) in childs.iter_mut() {
|
||||||
|
trace!(target: "hypervisor", "Stopping process module: {}", module);
|
||||||
|
self.service.send_shutdown(**module);
|
||||||
|
}
|
||||||
|
trace!(target: "hypervisor", "Waiting for shutdown...");
|
||||||
|
self.wait_for_shutdown();
|
||||||
|
trace!(target: "hypervisor", "All modules reported shutdown");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Hypervisor {
|
impl Drop for Hypervisor {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.shutdown(Some(std::time::Duration::new(1, 0)));
|
self.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
use std::sync::{RwLock,Arc};
|
use std::sync::{RwLock,Arc};
|
||||||
use ipc::IpcConfig;
|
use ipc::IpcConfig;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use nanoipc;
|
||||||
|
|
||||||
pub type IpcModuleId = u64;
|
pub type IpcModuleId = u64;
|
||||||
|
|
||||||
@ -28,15 +29,43 @@ pub const SYNC_MODULE_ID: IpcModuleId = 2100;
|
|||||||
|
|
||||||
/// IPC service that handles module management
|
/// IPC service that handles module management
|
||||||
pub struct HypervisorService {
|
pub struct HypervisorService {
|
||||||
check_list: RwLock<HashMap<IpcModuleId, bool>>,
|
modules: RwLock<HashMap<IpcModuleId, ModuleState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ModuleState {
|
||||||
|
started: bool,
|
||||||
|
control_url: String,
|
||||||
|
shutdown: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Ipc)]
|
||||||
|
pub trait ControlService {
|
||||||
|
fn shutdown(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Ipc)]
|
#[derive(Ipc)]
|
||||||
impl HypervisorService {
|
impl HypervisorService {
|
||||||
fn module_ready(&self, module_id: u64) -> bool {
|
// return type for making method synchronous
|
||||||
let mut check_list = self.check_list.write().unwrap();
|
fn module_ready(&self, module_id: u64, control_url: String) -> bool {
|
||||||
check_list.get_mut(&module_id).map(|mut status| *status = true);
|
let mut modules = self.modules.write().unwrap();
|
||||||
check_list.iter().any(|(_, status)| !status)
|
modules.get_mut(&module_id).map(|mut module| {
|
||||||
|
module.started = true;
|
||||||
|
module.control_url = control_url;
|
||||||
|
});
|
||||||
|
trace!(target: "hypervisor", "Module ready: {}", module_id);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// return type for making method synchronous
|
||||||
|
fn module_shutdown(&self, module_id: u64) -> bool {
|
||||||
|
let mut modules = self.modules.write().unwrap();
|
||||||
|
modules.get_mut(&module_id).map(|mut module| {
|
||||||
|
module.shutdown = true;
|
||||||
|
});
|
||||||
|
trace!(target: "hypervisor", "Module shutdown: {}", module_id);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,29 +77,46 @@ impl HypervisorService {
|
|||||||
|
|
||||||
/// New service with list of modules that will report for being ready
|
/// New service with list of modules that will report for being ready
|
||||||
pub fn with_modules(module_ids: Vec<IpcModuleId>) -> Arc<HypervisorService> {
|
pub fn with_modules(module_ids: Vec<IpcModuleId>) -> Arc<HypervisorService> {
|
||||||
let mut check_list = HashMap::new();
|
let mut modules = HashMap::new();
|
||||||
for module_id in module_ids {
|
for module_id in module_ids {
|
||||||
check_list.insert(module_id, false);
|
modules.insert(module_id, ModuleState::default());
|
||||||
}
|
}
|
||||||
Arc::new(HypervisorService {
|
Arc::new(HypervisorService {
|
||||||
check_list: RwLock::new(check_list),
|
modules: RwLock::new(modules),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the module to the check-list
|
/// Add the module to the check-list
|
||||||
pub fn add_module(&self, module_id: IpcModuleId) {
|
pub fn add_module(&self, module_id: IpcModuleId) {
|
||||||
self.check_list.write().unwrap().insert(module_id, false);
|
self.modules.write().unwrap().insert(module_id, ModuleState::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of modules still being waited for check-in
|
/// Number of modules still being waited for check-in
|
||||||
pub fn unchecked_count(&self) -> usize {
|
pub fn unchecked_count(&self) -> usize {
|
||||||
self.check_list.read().unwrap().iter().filter(|&(_, status)| !status).count()
|
self.modules.read().unwrap().iter().filter(|&(_, module)| !module.started).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List of all modules within this service
|
/// List of all modules within this service
|
||||||
pub fn module_ids(&self) -> Vec<IpcModuleId> {
|
pub fn module_ids(&self) -> Vec<IpcModuleId> {
|
||||||
self.check_list.read().unwrap().iter().map(|(module_id, _)| module_id).cloned().collect()
|
self.modules.read().unwrap().iter().map(|(module_id, _)| module_id).cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of modules started and running
|
||||||
|
pub fn running_count(&self) -> usize {
|
||||||
|
self.modules.read().unwrap().iter().filter(|&(_, module)| module.started && !module.shutdown).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_shutdown(&self, module_id: IpcModuleId) {
|
||||||
|
let modules = self.modules.read().unwrap();
|
||||||
|
modules.get(&module_id).map(|module| {
|
||||||
|
trace!(target: "hypervisor", "Sending shutdown to {}({})", module_id, &module.control_url);
|
||||||
|
let client = nanoipc::init_client::<ControlServiceClient<_>>(&module.control_url).unwrap();
|
||||||
|
client.shutdown();
|
||||||
|
trace!(target: "hypervisor", "Sent shutdown to {}", module_id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::ipc::IpcConfig for HypervisorService {}
|
impl ::ipc::IpcConfig for HypervisorService {}
|
||||||
|
|
||||||
|
impl ::ipc::IpcConfig for ControlService {}
|
||||||
|
@ -62,10 +62,10 @@ pub fn payload<B: ipc::BinaryConvertable>() -> Result<B, BootError> {
|
|||||||
.map_err(|binary_error| BootError::DecodeArgs(binary_error))
|
.map_err(|binary_error| BootError::DecodeArgs(binary_error))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(hv_url: &str, module_id: IpcModuleId) -> GuardedSocket<HypervisorServiceClient<NanoSocket>>{
|
pub fn register(hv_url: &str, control_url: &str, module_id: IpcModuleId) -> GuardedSocket<HypervisorServiceClient<NanoSocket>>{
|
||||||
let hypervisor_client = nanoipc::init_client::<HypervisorServiceClient<_>>(hv_url).unwrap();
|
let hypervisor_client = nanoipc::init_client::<HypervisorServiceClient<_>>(hv_url).unwrap();
|
||||||
hypervisor_client.handshake().unwrap();
|
hypervisor_client.handshake().unwrap();
|
||||||
hypervisor_client.module_ready(module_id);
|
hypervisor_client.module_ready(module_id, control_url.to_owned());
|
||||||
|
|
||||||
hypervisor_client
|
hypervisor_client
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,11 @@ API and Console Options:
|
|||||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||||
server, IP should be an interface's IP address,
|
server, IP should be an interface's IP address,
|
||||||
or local [default: local].
|
or local [default: local].
|
||||||
|
--dapps-hosts HOSTS List of allowed Host header values. This option will
|
||||||
|
validate the Host header sent by the browser, it
|
||||||
|
is additional security against some attack
|
||||||
|
vectors. Special options: "all", "none",
|
||||||
|
[default: none].
|
||||||
--dapps-user USERNAME Specify username for Dapps server. It will be
|
--dapps-user USERNAME Specify username for Dapps server. It will be
|
||||||
used in HTTP Basic Authentication Scheme.
|
used in HTTP Basic Authentication Scheme.
|
||||||
If --dapps-pass is not specified you will be
|
If --dapps-pass is not specified you will be
|
||||||
@ -346,6 +351,7 @@ pub struct Args {
|
|||||||
pub flag_no_dapps: bool,
|
pub flag_no_dapps: bool,
|
||||||
pub flag_dapps_port: u16,
|
pub flag_dapps_port: u16,
|
||||||
pub flag_dapps_interface: String,
|
pub flag_dapps_interface: String,
|
||||||
|
pub flag_dapps_hosts: String,
|
||||||
pub flag_dapps_user: Option<String>,
|
pub flag_dapps_user: Option<String>,
|
||||||
pub flag_dapps_pass: Option<String>,
|
pub flag_dapps_pass: Option<String>,
|
||||||
pub flag_dapps_path: String,
|
pub flag_dapps_path: String,
|
||||||
|
@ -356,6 +356,7 @@ impl Configuration {
|
|||||||
enabled: self.dapps_enabled(),
|
enabled: self.dapps_enabled(),
|
||||||
interface: self.dapps_interface(),
|
interface: self.dapps_interface(),
|
||||||
port: self.args.flag_dapps_port,
|
port: self.args.flag_dapps_port,
|
||||||
|
hosts: self.dapps_hosts(),
|
||||||
user: self.args.flag_dapps_user.clone(),
|
user: self.args.flag_dapps_user.clone(),
|
||||||
pass: self.args.flag_dapps_pass.clone(),
|
pass: self.args.flag_dapps_pass.clone(),
|
||||||
dapps_path: self.directories().dapps,
|
dapps_path: self.directories().dapps,
|
||||||
@ -485,6 +486,16 @@ impl Configuration {
|
|||||||
Some(hosts)
|
Some(hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dapps_hosts(&self) -> Option<Vec<String>> {
|
||||||
|
match self.args.flag_dapps_hosts.as_ref() {
|
||||||
|
"none" => return Some(Vec::new()),
|
||||||
|
"all" => return None,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let hosts = self.args.flag_dapps_hosts.split(',').map(|h| h.into()).collect();
|
||||||
|
Some(hosts)
|
||||||
|
}
|
||||||
|
|
||||||
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
||||||
let conf = IpcConfiguration {
|
let conf = IpcConfiguration {
|
||||||
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
||||||
@ -860,6 +871,23 @@ mod tests {
|
|||||||
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_dapps_hosts() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--dapps-hosts", "none"]);
|
||||||
|
let conf2 = parse(&["parity", "--dapps-hosts", "all"]);
|
||||||
|
let conf3 = parse(&["parity", "--dapps-hosts", "ethcore.io,something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.dapps_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf1.dapps_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf2.dapps_hosts(), None);
|
||||||
|
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_disable_signer_in_geth_compat() {
|
fn should_disable_signer_in_geth_compat() {
|
||||||
// given
|
// given
|
||||||
|
@ -25,6 +25,7 @@ pub struct Configuration {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub interface: String,
|
pub interface: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub hosts: Option<Vec<String>>,
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub pass: Option<String>,
|
pub pass: Option<String>,
|
||||||
pub dapps_path: String,
|
pub dapps_path: String,
|
||||||
@ -36,6 +37,7 @@ impl Default for Configuration {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
hosts: Some(Vec::new()),
|
||||||
user: None,
|
user: None,
|
||||||
pass: None,
|
pass: None,
|
||||||
dapps_path: replace_home("$HOME/.parity/dapps"),
|
dapps_path: replace_home("$HOME/.parity/dapps"),
|
||||||
@ -68,7 +70,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
|
|||||||
(username.to_owned(), password)
|
(username.to_owned(), password)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, auth))))
|
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, configuration.hosts, auth))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::server::WebappServer;
|
pub use self::server::WebappServer;
|
||||||
@ -84,6 +86,7 @@ mod server {
|
|||||||
_deps: Dependencies,
|
_deps: Dependencies,
|
||||||
_dapps_path: String,
|
_dapps_path: String,
|
||||||
_url: &SocketAddr,
|
_url: &SocketAddr,
|
||||||
|
_allowed_hosts: Option<Vec<String>>,
|
||||||
_auth: Option<(String, String)>,
|
_auth: Option<(String, String)>,
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||||
@ -109,6 +112,7 @@ mod server {
|
|||||||
deps: Dependencies,
|
deps: Dependencies,
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
|
allowed_hosts: Option<Vec<String>>,
|
||||||
auth: Option<(String, String)>
|
auth: Option<(String, String)>
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
use ethcore_dapps as dapps;
|
use ethcore_dapps as dapps;
|
||||||
@ -119,10 +123,10 @@ mod server {
|
|||||||
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
||||||
let start_result = match auth {
|
let start_result = match auth {
|
||||||
None => {
|
None => {
|
||||||
server.start_unsecure_http(url)
|
server.start_unsecured_http(url, allowed_hosts)
|
||||||
},
|
},
|
||||||
Some((username, password)) => {
|
Some((username, password)) => {
|
||||||
server.start_basic_auth_http(url, &username, &password)
|
server.start_basic_auth_http(url, allowed_hosts, &username, &password)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ impl ChainNotify for Informant {
|
|||||||
Colour::White.bold().paint(format!("#{}", header.number())),
|
Colour::White.bold().paint(format!("#{}", header.number())),
|
||||||
Colour::White.bold().paint(format!("{}", header.hash())),
|
Colour::White.bold().paint(format!("{}", header.hash())),
|
||||||
Colour::Yellow.bold().paint(format!("{}", tx_count)),
|
Colour::Yellow.bold().paint(format!("{}", tx_count)),
|
||||||
Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used.low_u64() as f32 / 1000000f32)),
|
Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)),
|
||||||
Colour::Purple.bold().paint(format!("{:.2}", duration as f32 / 1000000f32)),
|
Colour::Purple.bold().paint(format!("{:.2}", duration as f32 / 1000000f32)),
|
||||||
Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)),
|
Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)),
|
||||||
if skipped > 0 { format!(" + another {} block(s)", Colour::Red.bold().paint(format!("{}", skipped))) } else { String::new() }
|
if skipped > 0 { format!(" + another {} block(s)", Colour::Red.bold().paint(format!("{}", skipped))) } else { String::new() }
|
||||||
|
@ -32,6 +32,7 @@ pub mod service_urls {
|
|||||||
pub const SYNC: &'static str = "parity-sync.ipc";
|
pub const SYNC: &'static str = "parity-sync.ipc";
|
||||||
pub const SYNC_NOTIFY: &'static str = "parity-sync-notify.ipc";
|
pub const SYNC_NOTIFY: &'static str = "parity-sync-notify.ipc";
|
||||||
pub const NETWORK_MANAGER: &'static str = "parity-manage-net.ipc";
|
pub const NETWORK_MANAGER: &'static str = "parity-manage-net.ipc";
|
||||||
|
pub const SYNC_CONTROL: &'static str = "parity-sync-control.ipc";
|
||||||
#[cfg(feature="stratum")]
|
#[cfg(feature="stratum")]
|
||||||
pub const STRATUM: &'static str = "parity-stratum.ipc";
|
pub const STRATUM: &'static str = "parity-stratum.ipc";
|
||||||
#[cfg(feature="stratum")]
|
#[cfg(feature="stratum")]
|
||||||
|
@ -260,6 +260,10 @@ pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
|||||||
// Handle exit
|
// Handle exit
|
||||||
wait_for_exit(panic_handler, http_server, ipc_server, dapps_server, signer_server);
|
wait_for_exit(panic_handler, http_server, ipc_server, dapps_server, signer_server);
|
||||||
|
|
||||||
|
// hypervisor should be shutdown first while everything still works and can be
|
||||||
|
// terminated gracefully
|
||||||
|
drop(hypervisor);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +121,9 @@ impl SnapshotCommand {
|
|||||||
// drop the client so we don't restore while it has open DB handles.
|
// drop the client so we don't restore while it has open DB handles.
|
||||||
drop(service);
|
drop(service);
|
||||||
|
|
||||||
if !snapshot.begin_restore(manifest.clone()) {
|
try!(snapshot.init_restore(manifest.clone()).map_err(|e| {
|
||||||
return Err("Failed to begin restoration.".into());
|
format!("Failed to begin restoration: {}", e)
|
||||||
}
|
}));
|
||||||
|
|
||||||
let (num_state, num_blocks) = (manifest.state_hashes.len(), manifest.block_hashes.len());
|
let (num_state, num_blocks) = (manifest.state_hashes.len(), manifest.block_hashes.len());
|
||||||
|
|
||||||
|
@ -16,14 +16,26 @@
|
|||||||
|
|
||||||
//! Parity sync service
|
//! Parity sync service
|
||||||
|
|
||||||
use std;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use hypervisor::{SYNC_MODULE_ID, HYPERVISOR_IPC_URL};
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use hypervisor::{SYNC_MODULE_ID, HYPERVISOR_IPC_URL, ControlService};
|
||||||
use ethcore::client::{RemoteClient, ChainNotify};
|
use ethcore::client::{RemoteClient, ChainNotify};
|
||||||
use ethsync::{SyncProvider, EthSync, ManageNetwork, ServiceConfiguration};
|
use ethsync::{SyncProvider, EthSync, ManageNetwork, ServiceConfiguration};
|
||||||
use std::thread;
|
|
||||||
use modules::service_urls;
|
use modules::service_urls;
|
||||||
use boot;
|
use boot;
|
||||||
|
use nanoipc;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct SyncControlService {
|
||||||
|
pub stop: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlService for SyncControlService {
|
||||||
|
fn shutdown(&self) {
|
||||||
|
trace!(target: "hypervisor", "Received shutdown from control service");
|
||||||
|
self.stop.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
boot::setup_cli_logger("sync");
|
boot::setup_cli_logger("sync");
|
||||||
@ -33,31 +45,45 @@ pub fn main() {
|
|||||||
|
|
||||||
let remote_client = dependency!(RemoteClient, &service_urls::with_base(&service_config.io_path, service_urls::CLIENT));
|
let remote_client = dependency!(RemoteClient, &service_urls::with_base(&service_config.io_path, service_urls::CLIENT));
|
||||||
|
|
||||||
let stop = boot::main_thread();
|
|
||||||
let sync = EthSync::new(service_config.sync, remote_client.service().clone(), service_config.net).unwrap();
|
let sync = EthSync::new(service_config.sync, remote_client.service().clone(), service_config.net).unwrap();
|
||||||
|
|
||||||
let _ = boot::register(
|
let _ = boot::main_thread();
|
||||||
|
let service_stop = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let hypervisor = boot::register(
|
||||||
&service_urls::with_base(&service_config.io_path, HYPERVISOR_IPC_URL),
|
&service_urls::with_base(&service_config.io_path, HYPERVISOR_IPC_URL),
|
||||||
|
&service_urls::with_base(&service_config.io_path, service_urls::SYNC_CONTROL),
|
||||||
SYNC_MODULE_ID
|
SYNC_MODULE_ID
|
||||||
);
|
);
|
||||||
|
|
||||||
boot::host_service(
|
boot::host_service(
|
||||||
&service_urls::with_base(&service_config.io_path, service_urls::SYNC),
|
&service_urls::with_base(&service_config.io_path, service_urls::SYNC),
|
||||||
stop.clone(),
|
service_stop.clone(),
|
||||||
sync.clone() as Arc<SyncProvider>
|
sync.clone() as Arc<SyncProvider>
|
||||||
);
|
);
|
||||||
boot::host_service(
|
boot::host_service(
|
||||||
&service_urls::with_base(&service_config.io_path, service_urls::NETWORK_MANAGER),
|
&service_urls::with_base(&service_config.io_path, service_urls::NETWORK_MANAGER),
|
||||||
stop.clone(),
|
service_stop.clone(),
|
||||||
sync.clone() as Arc<ManageNetwork>
|
sync.clone() as Arc<ManageNetwork>
|
||||||
);
|
);
|
||||||
boot::host_service(
|
boot::host_service(
|
||||||
&service_urls::with_base(&service_config.io_path, service_urls::SYNC_NOTIFY),
|
&service_urls::with_base(&service_config.io_path, service_urls::SYNC_NOTIFY),
|
||||||
stop.clone(),
|
service_stop.clone(),
|
||||||
sync.clone() as Arc<ChainNotify>
|
sync.clone() as Arc<ChainNotify>
|
||||||
);
|
);
|
||||||
|
|
||||||
while !stop.load(::std::sync::atomic::Ordering::Relaxed) {
|
let control_service = Arc::new(SyncControlService::default());
|
||||||
thread::park_timeout(std::time::Duration::from_millis(1000));
|
let as_control = control_service.clone() as Arc<ControlService>;
|
||||||
|
let mut worker = nanoipc::Worker::<ControlService>::new(&as_control);
|
||||||
|
worker.add_reqrep(
|
||||||
|
&service_urls::with_base(&service_config.io_path, service_urls::SYNC_CONTROL)
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
while !control_service.stop.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
worker.poll();
|
||||||
}
|
}
|
||||||
|
service_stop.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
hypervisor.module_shutdown(SYNC_MODULE_ID);
|
||||||
|
trace!(target: "hypervisor", "Sync process terminated gracefully");
|
||||||
}
|
}
|
||||||
|
@ -158,22 +158,22 @@ impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
|||||||
let block = Block {
|
let block = Block {
|
||||||
hash: Some(uncle.hash().into()),
|
hash: Some(uncle.hash().into()),
|
||||||
size: None,
|
size: None,
|
||||||
parent_hash: uncle.parent_hash.into(),
|
parent_hash: uncle.parent_hash().clone().into(),
|
||||||
uncles_hash: uncle.uncles_hash.into(),
|
uncles_hash: uncle.uncles_hash().clone().into(),
|
||||||
author: uncle.author.into(),
|
author: uncle.author().clone().into(),
|
||||||
miner: uncle.author.into(),
|
miner: uncle.author().clone().into(),
|
||||||
state_root: uncle.state_root.into(),
|
state_root: uncle.state_root().clone().into(),
|
||||||
transactions_root: uncle.transactions_root.into(),
|
transactions_root: uncle.transactions_root().clone().into(),
|
||||||
number: Some(uncle.number.into()),
|
number: Some(uncle.number().into()),
|
||||||
gas_used: uncle.gas_used.into(),
|
gas_used: uncle.gas_used().clone().into(),
|
||||||
gas_limit: uncle.gas_limit.into(),
|
gas_limit: uncle.gas_limit().clone().into(),
|
||||||
logs_bloom: uncle.log_bloom.into(),
|
logs_bloom: uncle.log_bloom().clone().into(),
|
||||||
timestamp: uncle.timestamp.into(),
|
timestamp: uncle.timestamp().into(),
|
||||||
difficulty: uncle.difficulty.into(),
|
difficulty: uncle.difficulty().clone().into(),
|
||||||
total_difficulty: (uncle.difficulty + parent_difficulty).into(),
|
total_difficulty: (uncle.difficulty().clone() + parent_difficulty).into(),
|
||||||
receipts_root: uncle.receipts_root.into(),
|
receipts_root: uncle.receipts_root().clone().into(),
|
||||||
extra_data: uncle.extra_data.into(),
|
extra_data: uncle.extra_data().clone().into(),
|
||||||
seal_fields: uncle.seal.into_iter().map(|f| decode(&f)).map(Bytes::new).collect(),
|
seal_fields: uncle.seal().clone().into_iter().map(|f| decode(&f)).map(Bytes::new).collect(),
|
||||||
uncles: vec![],
|
uncles: vec![],
|
||||||
transactions: BlockTransactions::Hashes(vec![]),
|
transactions: BlockTransactions::Hashes(vec![]),
|
||||||
};
|
};
|
||||||
|
11
scripts/parity.service
Normal file
11
scripts/parity.service
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Parity Daemon
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=%h/.parity/parity.conf
|
||||||
|
ExecStart=/usr/bin/parity $ARGS
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
||||||
|
|
@ -270,7 +270,7 @@ impl BlockCollection {
|
|||||||
match self.head {
|
match self.head {
|
||||||
None if hash == self.heads[0] => {
|
None if hash == self.heads[0] => {
|
||||||
trace!("New head {}", hash);
|
trace!("New head {}", hash);
|
||||||
self.head = Some(info.parent_hash);
|
self.head = Some(info.parent_hash().clone());
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
@ -280,8 +280,8 @@ impl BlockCollection {
|
|||||||
body: None,
|
body: None,
|
||||||
};
|
};
|
||||||
let header_id = HeaderId {
|
let header_id = HeaderId {
|
||||||
transactions_root: info.transactions_root,
|
transactions_root: info.transactions_root().clone(),
|
||||||
uncles: info.uncles_hash
|
uncles: info.uncles_hash().clone(),
|
||||||
};
|
};
|
||||||
if header_id.transactions_root == rlp::SHA3_NULL_RLP && header_id.uncles == rlp::SHA3_EMPTY_LIST_RLP {
|
if header_id.transactions_root == rlp::SHA3_NULL_RLP && header_id.uncles == rlp::SHA3_EMPTY_LIST_RLP {
|
||||||
// empty body, just mark as downloaded
|
// empty body, just mark as downloaded
|
||||||
@ -294,7 +294,7 @@ impl BlockCollection {
|
|||||||
self.header_ids.insert(header_id, hash.clone());
|
self.header_ids.insert(header_id, hash.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.parents.insert(info.parent_hash.clone(), hash.clone());
|
self.parents.insert(info.parent_hash().clone(), hash.clone());
|
||||||
self.blocks.insert(hash.clone(), block);
|
self.blocks.insert(hash.clone(), block);
|
||||||
trace!(target: "sync", "New header: {}", hash.hex());
|
trace!(target: "sync", "New header: {}", hash.hex());
|
||||||
Ok(hash)
|
Ok(hash)
|
||||||
|
@ -503,7 +503,7 @@ impl ChainSync {
|
|||||||
let mut valid_response = item_count == 0; //empty response is valid
|
let mut valid_response = item_count == 0; //empty response is valid
|
||||||
for i in 0..item_count {
|
for i in 0..item_count {
|
||||||
let info: BlockHeader = try!(r.val_at(i));
|
let info: BlockHeader = try!(r.val_at(i));
|
||||||
let number = BlockNumber::from(info.number);
|
let number = BlockNumber::from(info.number());
|
||||||
// Check if any of the headers matches the hash we requested
|
// Check if any of the headers matches the hash we requested
|
||||||
if !valid_response {
|
if !valid_response {
|
||||||
if let Some(expected) = expected_hash {
|
if let Some(expected) = expected_hash {
|
||||||
@ -645,11 +645,11 @@ impl ChainSync {
|
|||||||
trace!(target: "sync", "New block already queued {:?}", h);
|
trace!(target: "sync", "New block already queued {:?}", h);
|
||||||
},
|
},
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if header.number == self.last_imported_block + 1 {
|
if header.number() == self.last_imported_block + 1 {
|
||||||
self.last_imported_block = header.number;
|
self.last_imported_block = header.number();
|
||||||
self.last_imported_hash = header.hash();
|
self.last_imported_hash = header.hash();
|
||||||
}
|
}
|
||||||
trace!(target: "sync", "New block queued {:?} ({})", h, header.number);
|
trace!(target: "sync", "New block queued {:?} ({})", h, header.number());
|
||||||
},
|
},
|
||||||
Err(BlockImportError::Block(BlockError::UnknownParent(p))) => {
|
Err(BlockImportError::Block(BlockError::UnknownParent(p))) => {
|
||||||
unknown = true;
|
unknown = true;
|
||||||
@ -1539,12 +1539,12 @@ mod tests {
|
|||||||
|
|
||||||
fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes {
|
fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes {
|
||||||
let mut header = Header::new();
|
let mut header = Header::new();
|
||||||
header.gas_limit = 0.into();
|
header.set_gas_limit(0.into());
|
||||||
header.difficulty = (order * 100).into();
|
header.set_difficulty((order * 100).into());
|
||||||
header.timestamp = (order * 10) as u64;
|
header.set_timestamp((order * 10) as u64);
|
||||||
header.number = order as u64;
|
header.set_number(order as u64);
|
||||||
header.parent_hash = parent_hash;
|
header.set_parent_hash(parent_hash);
|
||||||
header.state_root = H256::zero();
|
header.set_state_root(H256::zero());
|
||||||
|
|
||||||
let mut rlp = RlpStream::new_list(3);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(&header);
|
rlp.append(&header);
|
||||||
|
Loading…
Reference in New Issue
Block a user