Merge branch 'master' into tx-rpc-expose
This commit is contained in:
commit
663eec8a76
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -15,6 +15,7 @@ dependencies = [
|
|||||||
"fdlimit 0.1.0",
|
"fdlimit 0.1.0",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -236,8 +237,8 @@ dependencies = [
|
|||||||
"ethcore 0.9.99",
|
"ethcore 0.9.99",
|
||||||
"ethcore-util 0.9.99",
|
"ethcore-util 0.9.99",
|
||||||
"ethsync 0.9.99",
|
"ethsync 0.9.99",
|
||||||
"jsonrpc-core 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-http-server 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -409,7 +410,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-core"
|
name = "jsonrpc-core"
|
||||||
version = "1.2.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -420,11 +421,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "2.1.0"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -699,6 +700,17 @@ dependencies = [
|
|||||||
"librocksdb-sys 0.2.1 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
"librocksdb-sys 0.2.1 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpassword"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-crypto"
|
name = "rust-crypto"
|
||||||
version = "0.2.34"
|
version = "0.2.34"
|
||||||
@ -832,6 +844,14 @@ dependencies = [
|
|||||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termios"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.34"
|
version = "0.1.34"
|
||||||
|
@ -21,6 +21,7 @@ fdlimit = { path = "util/fdlimit" }
|
|||||||
daemonize = "0.2"
|
daemonize = "0.2"
|
||||||
ethcore-devtools = { path = "devtools" }
|
ethcore-devtools = { path = "devtools" }
|
||||||
number_prefix = "0.2"
|
number_prefix = "0.2"
|
||||||
|
rpassword = "0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rpc"]
|
default = ["rpc"]
|
||||||
|
@ -385,7 +385,7 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
{
|
{
|
||||||
let mut verification = self.verification.lock().unwrap();
|
let mut verification = self.verification.lock().unwrap();
|
||||||
verification.unverified.shrink_to_fit();
|
verification.unverified.shrink_to_fit();
|
||||||
|
@ -87,6 +87,8 @@ pub struct ClientConfig {
|
|||||||
pub blockchain: BlockChainConfig,
|
pub blockchain: BlockChainConfig,
|
||||||
/// Prefer journal rather than archive.
|
/// Prefer journal rather than archive.
|
||||||
pub prefer_journal: bool,
|
pub prefer_journal: bool,
|
||||||
|
/// The name of the client instance.
|
||||||
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientConfig {
|
impl Default for ClientConfig {
|
||||||
@ -95,6 +97,7 @@ impl Default for ClientConfig {
|
|||||||
queue: Default::default(),
|
queue: Default::default(),
|
||||||
blockchain: Default::default(),
|
blockchain: Default::default(),
|
||||||
prefer_journal: false,
|
prefer_journal: false,
|
||||||
|
name: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,6 +196,8 @@ pub struct ClientReport {
|
|||||||
pub transactions_applied: usize,
|
pub transactions_applied: usize,
|
||||||
/// How much gas has been processed so far.
|
/// How much gas has been processed so far.
|
||||||
pub gas_processed: U256,
|
pub gas_processed: U256,
|
||||||
|
/// Memory used by state DB
|
||||||
|
pub state_db_mem: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientReport {
|
impl ClientReport {
|
||||||
@ -225,7 +230,7 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HISTORY: u64 = 1000;
|
const HISTORY: u64 = 1000;
|
||||||
const CLIENT_DB_VER_STR: &'static str = "4.0";
|
const CLIENT_DB_VER_STR: &'static str = "5.1";
|
||||||
|
|
||||||
impl Client<CanonVerifier> {
|
impl Client<CanonVerifier> {
|
||||||
/// Create a new client with given spec and DB path.
|
/// Create a new client with given spec and DB path.
|
||||||
@ -437,7 +442,9 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
/// Get the report.
|
/// Get the report.
|
||||||
pub fn report(&self) -> ClientReport {
|
pub fn report(&self) -> ClientReport {
|
||||||
self.report.read().unwrap().clone()
|
let mut report = self.report.read().unwrap().clone();
|
||||||
|
report.state_db_mem = self.state_db.lock().unwrap().mem_used();
|
||||||
|
report
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tick the client.
|
/// Tick the client.
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
pub mod verification;
|
pub mod verification;
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
mod canon_verifier;
|
mod canon_verifier;
|
||||||
|
#[cfg(test)]
|
||||||
mod noop_verifier;
|
mod noop_verifier;
|
||||||
|
|
||||||
pub use self::verification::*;
|
pub use self::verification::*;
|
||||||
pub use self::verifier::Verifier;
|
pub use self::verifier::Verifier;
|
||||||
pub use self::canon_verifier::CanonVerifier;
|
pub use self::canon_verifier::CanonVerifier;
|
||||||
|
#[cfg(test)]
|
||||||
pub use self::noop_verifier::NoopVerifier;
|
pub use self::noop_verifier::NoopVerifier;
|
||||||
|
199
parity/main.rs
199
parity/main.rs
@ -32,6 +32,7 @@ extern crate fdlimit;
|
|||||||
extern crate daemonize;
|
extern crate daemonize;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate number_prefix;
|
extern crate number_prefix;
|
||||||
|
extern crate rpassword;
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
extern crate ethcore_rpc as rpc;
|
extern crate ethcore_rpc as rpc;
|
||||||
@ -43,7 +44,7 @@ use std::path::PathBuf;
|
|||||||
use env_logger::LogBuilder;
|
use env_logger::LogBuilder;
|
||||||
use ctrlc::CtrlC;
|
use ctrlc::CtrlC;
|
||||||
use util::*;
|
use util::*;
|
||||||
use util::panics::MayPanic;
|
use util::panics::{MayPanic, ForwardPanic, PanicHandler};
|
||||||
use ethcore::spec::*;
|
use ethcore::spec::*;
|
||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use ethcore::service::{ClientService, NetSyncMessage};
|
use ethcore::service::{ClientService, NetSyncMessage};
|
||||||
@ -53,6 +54,16 @@ use docopt::Docopt;
|
|||||||
use daemonize::Daemonize;
|
use daemonize::Daemonize;
|
||||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||||
|
|
||||||
|
fn die_with_message(msg: &str) -> ! {
|
||||||
|
println!("ERROR: {}", msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! die {
|
||||||
|
($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*))));
|
||||||
|
}
|
||||||
|
|
||||||
const USAGE: &'static str = r#"
|
const USAGE: &'static str = r#"
|
||||||
Parity. Ethereum Client.
|
Parity. Ethereum Client.
|
||||||
By Wood/Paronyan/Kotewicz/Drwięga/Volf.
|
By Wood/Paronyan/Kotewicz/Drwięga/Volf.
|
||||||
@ -60,15 +71,20 @@ Parity. Ethereum Client.
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
parity daemon <pid-file> [options] [ --no-bootstrap | <enode>... ]
|
parity daemon <pid-file> [options] [ --no-bootstrap | <enode>... ]
|
||||||
|
parity account (new | list)
|
||||||
parity [options] [ --no-bootstrap | <enode>... ]
|
parity [options] [ --no-bootstrap | <enode>... ]
|
||||||
|
|
||||||
Options:
|
Protocol Options:
|
||||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
||||||
or frontier, mainnet, morden, or testnet [default: frontier].
|
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
||||||
|
--testnet Equivalent to --chain testnet (geth-compatible).
|
||||||
|
--networkid INDEX Override the network identifier from the chain we are on.
|
||||||
--archive Client should not prune the state/storage trie.
|
--archive Client should not prune the state/storage trie.
|
||||||
-d --db-path PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
||||||
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
||||||
|
--identity NAME Specify your node's name.
|
||||||
|
|
||||||
|
Networking Options:
|
||||||
--no-bootstrap Don't bother trying to connect to any nodes initially.
|
--no-bootstrap Don't bother trying to connect to any nodes initially.
|
||||||
--listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304].
|
--listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304].
|
||||||
--public-address URL Specify the IP/port on which peers may connect.
|
--public-address URL Specify the IP/port on which peers may connect.
|
||||||
@ -78,18 +94,32 @@ Options:
|
|||||||
--no-upnp Disable trying to figure out the correct public adderss over UPnP.
|
--no-upnp Disable trying to figure out the correct public adderss over UPnP.
|
||||||
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
||||||
|
|
||||||
|
API and Console Options:
|
||||||
|
-j --jsonrpc Enable the JSON-RPC API sever.
|
||||||
|
--jsonrpc-addr HOST Specify the hostname portion of the JSONRPC API server [default: 127.0.0.1].
|
||||||
|
--jsonrpc-port PORT Specify the port portion of the JSONRPC API server [default: 8545].
|
||||||
|
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
||||||
|
--jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited
|
||||||
|
list of API name. Possible name are web3, eth and net. [default: web3,eth,net].
|
||||||
|
--rpc Equivalent to --jsonrpc (geth-compatible).
|
||||||
|
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST (geth-compatible).
|
||||||
|
--rpcport PORT Equivalent to --jsonrpc-port PORT (geth-compatible).
|
||||||
|
--rpcapi APIS Equivalent to --jsonrpc-apis APIS (geth-compatible).
|
||||||
|
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL (geth-compatible).
|
||||||
|
|
||||||
|
Sealing/Mining Options:
|
||||||
|
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
||||||
|
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
||||||
|
--extradata STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
||||||
|
|
||||||
|
Memory Footprint Options:
|
||||||
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
||||||
--cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144].
|
--cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144].
|
||||||
--queue-max-size BYTES Specify the maximum size of memory to use for block queue [default: 52428800].
|
--queue-max-size BYTES Specify the maximum size of memory to use for block queue [default: 52428800].
|
||||||
|
--cache MEGABYTES Set total amount of cache to use for the entire system, mutually exclusive with
|
||||||
|
other cache options (geth-compatible).
|
||||||
|
|
||||||
-j --jsonrpc Enable the JSON-RPC API sever.
|
Miscellaneous Options:
|
||||||
--jsonrpc-url URL Specify URL for JSON-RPC API server [default: 127.0.0.1:8545].
|
|
||||||
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
|
||||||
|
|
||||||
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
|
||||||
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
|
||||||
--extra-data STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
|
||||||
|
|
||||||
-l --logging LOGGING Specify the logging level.
|
-l --logging LOGGING Specify the logging level.
|
||||||
-v --version Show information about version.
|
-v --version Show information about version.
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
@ -98,17 +128,24 @@ Options:
|
|||||||
#[derive(Debug, RustcDecodable)]
|
#[derive(Debug, RustcDecodable)]
|
||||||
struct Args {
|
struct Args {
|
||||||
cmd_daemon: bool,
|
cmd_daemon: bool,
|
||||||
|
cmd_account: bool,
|
||||||
|
cmd_new: bool,
|
||||||
|
cmd_list: bool,
|
||||||
arg_pid_file: String,
|
arg_pid_file: String,
|
||||||
arg_enode: Vec<String>,
|
arg_enode: Vec<String>,
|
||||||
flag_chain: String,
|
flag_chain: String,
|
||||||
flag_db_path: String,
|
flag_testnet: bool,
|
||||||
|
flag_datadir: String,
|
||||||
|
flag_networkid: Option<String>,
|
||||||
|
flag_identity: String,
|
||||||
|
flag_cache: Option<usize>,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
flag_archive: bool,
|
flag_archive: bool,
|
||||||
flag_no_bootstrap: bool,
|
flag_no_bootstrap: bool,
|
||||||
flag_listen_address: String,
|
flag_listen_address: String,
|
||||||
flag_public_address: Option<String>,
|
flag_public_address: Option<String>,
|
||||||
flag_address: Option<String>,
|
flag_address: Option<String>,
|
||||||
flag_peers: u32,
|
flag_peers: usize,
|
||||||
flag_no_discovery: bool,
|
flag_no_discovery: bool,
|
||||||
flag_no_upnp: bool,
|
flag_no_upnp: bool,
|
||||||
flag_node_key: Option<String>,
|
flag_node_key: Option<String>,
|
||||||
@ -116,8 +153,15 @@ struct Args {
|
|||||||
flag_cache_max_size: usize,
|
flag_cache_max_size: usize,
|
||||||
flag_queue_max_size: usize,
|
flag_queue_max_size: usize,
|
||||||
flag_jsonrpc: bool,
|
flag_jsonrpc: bool,
|
||||||
flag_jsonrpc_url: String,
|
flag_jsonrpc_addr: String,
|
||||||
|
flag_jsonrpc_port: u16,
|
||||||
flag_jsonrpc_cors: String,
|
flag_jsonrpc_cors: String,
|
||||||
|
flag_jsonrpc_apis: String,
|
||||||
|
flag_rpc: bool,
|
||||||
|
flag_rpcaddr: Option<String>,
|
||||||
|
flag_rpcport: Option<u16>,
|
||||||
|
flag_rpccorsdomain: Option<String>,
|
||||||
|
flag_rpcapi: Option<String>,
|
||||||
flag_logging: Option<String>,
|
flag_logging: Option<String>,
|
||||||
flag_version: bool,
|
flag_version: bool,
|
||||||
flag_author: String,
|
flag_author: String,
|
||||||
@ -151,19 +195,29 @@ fn setup_log(init: &Option<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_domain: &str) {
|
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option<Arc<PanicHandler>> {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
let mut server = rpc::HttpServer::new(1);
|
let server = rpc::RpcServer::new();
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
for api in apis.into_iter() {
|
||||||
server.add_delegate(EthClient::new(&client, &sync).to_delegate());
|
match api {
|
||||||
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
||||||
server.add_delegate(NetClient::new(&sync).to_delegate());
|
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
|
||||||
server.start_async(url, cors_domain);
|
"eth" => {
|
||||||
|
server.add_delegate(EthClient::new(&client, &sync).to_delegate());
|
||||||
|
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
die!("{}: Invalid API name to be enabled.", api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(server.start_http(url, cors_domain, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "rpc"))]
|
#[cfg(not(feature = "rpc"))]
|
||||||
fn setup_rpc_server(_client: Arc<Client>, _sync: Arc<EthSync>, _url: &str) {
|
fn setup_rpc_server(_client: Arc<Client>, _sync: Arc<EthSync>, _url: &str) -> Option<Arc<PanicHandler>> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_version() {
|
fn print_version() {
|
||||||
@ -179,16 +233,6 @@ By Wood/Paronyan/Kotewicz/Drwięga/Volf.\
|
|||||||
", version());
|
", version());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn die_with_message(msg: &str) -> ! {
|
|
||||||
println!("ERROR: {}", msg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! die {
|
|
||||||
($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*))));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
args: Args
|
args: Args
|
||||||
}
|
}
|
||||||
@ -201,7 +245,7 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> String {
|
fn path(&self) -> String {
|
||||||
self.args.flag_db_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
self.args.flag_datadir.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn author(&self) -> Address {
|
fn author(&self) -> Address {
|
||||||
@ -221,8 +265,11 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spec(&self) -> Spec {
|
fn spec(&self) -> Spec {
|
||||||
|
if self.args.flag_testnet {
|
||||||
|
return ethereum::new_morden();
|
||||||
|
}
|
||||||
match self.args.flag_chain.as_ref() {
|
match self.args.flag_chain.as_ref() {
|
||||||
"frontier" | "mainnet" => ethereum::new_frontier(),
|
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
||||||
"morden" | "testnet" => ethereum::new_morden(),
|
"morden" | "testnet" => ethereum::new_morden(),
|
||||||
"olympic" => ethereum::new_olympic(),
|
"olympic" => ethereum::new_olympic(),
|
||||||
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| die!("{}: Couldn't read chain specification file. Sure it exists?", f)).as_ref()),
|
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| die!("{}: Couldn't read chain specification file. Sure it exists?", f)).as_ref()),
|
||||||
@ -276,7 +323,7 @@ impl Configuration {
|
|||||||
ret.public_address = public;
|
ret.public_address = public;
|
||||||
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
|
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
|
||||||
ret.discovery_enabled = !self.args.flag_no_discovery;
|
ret.discovery_enabled = !self.args.flag_no_discovery;
|
||||||
ret.ideal_peers = self.args.flag_peers;
|
ret.ideal_peers = self.args.flag_peers as u32;
|
||||||
let mut net_path = PathBuf::from(&self.path());
|
let mut net_path = PathBuf::from(&self.path());
|
||||||
net_path.push("network");
|
net_path.push("network");
|
||||||
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
||||||
@ -295,10 +342,44 @@ impl Configuration {
|
|||||||
.start()
|
.start()
|
||||||
.unwrap_or_else(|e| die!("Couldn't daemonize; {}", e));
|
.unwrap_or_else(|e| die!("Couldn't daemonize; {}", e));
|
||||||
}
|
}
|
||||||
|
if self.args.cmd_account {
|
||||||
|
self.execute_account_cli();
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.execute_client();
|
self.execute_client();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_account_cli(&self) {
|
||||||
|
use util::keys::store::SecretStore;
|
||||||
|
use rpassword::read_password;
|
||||||
|
let mut secret_store = SecretStore::new();
|
||||||
|
if self.args.cmd_new {
|
||||||
|
println!("Please note that password is NOT RECOVERABLE.");
|
||||||
|
println!("Type password: ");
|
||||||
|
let password = read_password().unwrap();
|
||||||
|
println!("Repeat password: ");
|
||||||
|
let password_repeat = read_password().unwrap();
|
||||||
|
if password != password_repeat {
|
||||||
|
println!("Passwords do not match!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("New account address:");
|
||||||
|
let new_address = secret_store.new_account(&password).unwrap();
|
||||||
|
println!("{:?}", new_address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.args.cmd_list {
|
||||||
|
println!("Known addresses:");
|
||||||
|
for &(addr, _) in secret_store.accounts().unwrap().iter() {
|
||||||
|
println!("{:?}", addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_client(&self) {
|
fn execute_client(&self) {
|
||||||
|
// Setup panic handler
|
||||||
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
|
||||||
// Setup logging
|
// Setup logging
|
||||||
setup_log(&self.args.flag_logging);
|
setup_log(&self.args.flag_logging);
|
||||||
// Raise fdlimit
|
// Raise fdlimit
|
||||||
@ -307,15 +388,25 @@ impl Configuration {
|
|||||||
let spec = self.spec();
|
let spec = self.spec();
|
||||||
let net_settings = self.net_settings(&spec);
|
let net_settings = self.net_settings(&spec);
|
||||||
let mut sync_config = SyncConfig::default();
|
let mut sync_config = SyncConfig::default();
|
||||||
sync_config.network_id = spec.network_id();
|
sync_config.network_id = self.args.flag_networkid.as_ref().map(|id| U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --networkid", id))).unwrap_or(spec.network_id());
|
||||||
|
|
||||||
// Build client
|
// Build client
|
||||||
let mut client_config = ClientConfig::default();
|
let mut client_config = ClientConfig::default();
|
||||||
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
match self.args.flag_cache {
|
||||||
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
Some(mb) => {
|
||||||
|
client_config.blockchain.max_cache_size = mb * 1024 * 1024;
|
||||||
|
client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size / 2;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
||||||
|
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
client_config.prefer_journal = !self.args.flag_archive;
|
client_config.prefer_journal = !self.args.flag_archive;
|
||||||
|
client_config.name = self.args.flag_identity.clone();
|
||||||
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
||||||
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
|
panic_handler.forward_from(&service);
|
||||||
let client = service.client().clone();
|
let client = service.client().clone();
|
||||||
client.set_author(self.author());
|
client.set_author(self.author());
|
||||||
client.set_extra_data(self.extra_data());
|
client.set_extra_data(self.extra_data());
|
||||||
@ -324,32 +415,45 @@ impl Configuration {
|
|||||||
let sync = EthSync::register(service.network(), sync_config, client);
|
let sync = EthSync::register(service.network(), sync_config, client);
|
||||||
|
|
||||||
// Setup rpc
|
// Setup rpc
|
||||||
if self.args.flag_jsonrpc {
|
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
||||||
setup_rpc_server(service.client(), sync.clone(), &self.args.flag_jsonrpc_url, &self.args.flag_jsonrpc_cors);
|
let url = format!("{}:{}",
|
||||||
SocketAddr::from_str(&self.args.flag_jsonrpc_url).unwrap_or_else(|_|die!("{}: Invalid JSONRPC listen address given with --jsonrpc-url. Should be of the form 'IP:port'.", self.args.flag_jsonrpc_url));
|
self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_addr),
|
||||||
|
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
|
||||||
|
);
|
||||||
|
SocketAddr::from_str(&url).unwrap_or_else(|_|die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||||
|
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||||
|
// TODO: use this as the API list.
|
||||||
|
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||||
|
let server_handler = setup_rpc_server(service.client(), sync.clone(), &url, cors, apis.split(",").collect());
|
||||||
|
if let Some(handler) = server_handler {
|
||||||
|
panic_handler.forward_from(handler.deref());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register IO handler
|
// Register IO handler
|
||||||
let io_handler = Arc::new(ClientIoHandler {
|
let io_handler = Arc::new(ClientIoHandler {
|
||||||
client: service.client(),
|
client: service.client(),
|
||||||
info: Default::default(),
|
info: Default::default(),
|
||||||
sync: sync
|
sync: sync.clone(),
|
||||||
});
|
});
|
||||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||||
|
|
||||||
// Handle exit
|
// Handle exit
|
||||||
wait_for_exit(&service);
|
wait_for_exit(panic_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_exit(client_service: &ClientService) {
|
fn wait_for_exit(panic_handler: Arc<PanicHandler>) {
|
||||||
let exit = Arc::new(Condvar::new());
|
let exit = Arc::new(Condvar::new());
|
||||||
|
|
||||||
// Handle possible exits
|
// Handle possible exits
|
||||||
let e = exit.clone();
|
let e = exit.clone();
|
||||||
CtrlC::set_handler(move || { e.notify_all(); });
|
CtrlC::set_handler(move || { e.notify_all(); });
|
||||||
|
|
||||||
|
// Handle panics
|
||||||
let e = exit.clone();
|
let e = exit.clone();
|
||||||
client_service.on_panic(move |_reason| { e.notify_all(); });
|
panic_handler.on_panic(move |_reason| { e.notify_all(); });
|
||||||
|
|
||||||
// Wait for signal
|
// Wait for signal
|
||||||
let mutex = Mutex::new(());
|
let mutex = Mutex::new(());
|
||||||
@ -395,7 +499,7 @@ impl Informant {
|
|||||||
let sync_info = sync.status();
|
let sync_info = sync.status();
|
||||||
|
|
||||||
if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
|
if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
|
||||||
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} chain, {} queue, {} sync ]",
|
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]",
|
||||||
chain_info.best_block_number,
|
chain_info.best_block_number,
|
||||||
chain_info.best_block_hash,
|
chain_info.best_block_hash,
|
||||||
(report.blocks_imported - last_report.blocks_imported) / dur,
|
(report.blocks_imported - last_report.blocks_imported) / dur,
|
||||||
@ -408,6 +512,7 @@ impl Informant {
|
|||||||
queue_info.unverified_queue_size,
|
queue_info.unverified_queue_size,
|
||||||
queue_info.verified_queue_size,
|
queue_info.verified_queue_size,
|
||||||
|
|
||||||
|
Informant::format_bytes(report.state_db_mem),
|
||||||
Informant::format_bytes(cache_info.total()),
|
Informant::format_bytes(cache_info.total()),
|
||||||
Informant::format_bytes(queue_info.mem_used),
|
Informant::format_bytes(queue_info.mem_used),
|
||||||
Informant::format_bytes(sync_info.mem_used),
|
Informant::format_bytes(sync_info.mem_used),
|
||||||
|
@ -12,8 +12,8 @@ build = "build.rs"
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
serde_json = "0.7.0"
|
serde_json = "0.7.0"
|
||||||
jsonrpc-core = "1.2"
|
jsonrpc-core = "2.0"
|
||||||
jsonrpc-http-server = "2.1"
|
jsonrpc-http-server = "3.0"
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
ethash = { path = "../ethash" }
|
ethash = { path = "../ethash" }
|
||||||
|
@ -29,33 +29,43 @@ extern crate ethcore;
|
|||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
extern crate transient_hashmap;
|
extern crate transient_hashmap;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use util::panics::PanicHandler;
|
||||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
|
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
|
|
||||||
/// Http server.
|
/// Http server.
|
||||||
pub struct HttpServer {
|
pub struct RpcServer {
|
||||||
handler: IoHandler,
|
handler: Arc<IoHandler>,
|
||||||
threads: usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpServer {
|
impl RpcServer {
|
||||||
/// Construct new http server object with given number of threads.
|
/// Construct new http server object with given number of threads.
|
||||||
pub fn new(threads: usize) -> HttpServer {
|
pub fn new() -> RpcServer {
|
||||||
HttpServer {
|
RpcServer {
|
||||||
handler: IoHandler::new(),
|
handler: Arc::new(IoHandler::new()),
|
||||||
threads: threads
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add io delegate.
|
/// Add io delegate.
|
||||||
pub fn add_delegate<D>(&mut self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||||
self.handler.add_delegate(delegate);
|
self.handler.add_delegate(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start server asynchronously in new thread
|
/// Start server asynchronously in new thread and returns panic handler.
|
||||||
pub fn start_async(self, addr: &str, cors_domain: &str) {
|
pub fn start_http(&self, addr: &str, cors_domain: &str, threads: usize) -> Arc<PanicHandler> {
|
||||||
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
|
let addr = addr.to_owned();
|
||||||
server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned()))
|
let cors_domain = cors_domain.to_owned();
|
||||||
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
let ph = panic_handler.clone();
|
||||||
|
let server = jsonrpc_http_server::Server::new(self.handler.clone());
|
||||||
|
thread::Builder::new().name("jsonrpc_http".to_string()).spawn(move || {
|
||||||
|
ph.catch_panic(move || {
|
||||||
|
server.start(addr.as_ref(), jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain), threads);
|
||||||
|
}).unwrap()
|
||||||
|
}).expect("Error while creating jsonrpc http thread");
|
||||||
|
panic_handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,8 @@ impl EthFilter for EthFilterClient {
|
|||||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
Some(info) => match info.filter {
|
Some(info) => match info.filter {
|
||||||
PollFilter::Block => {
|
PollFilter::Block => {
|
||||||
let current_number = client.chain_info().best_block_number;
|
// + 1, cause we want to return hashes including current block hash.
|
||||||
|
let current_number = client.chain_info().best_block_number + 1;
|
||||||
let hashes = (info.block_number..current_number).into_iter()
|
let hashes = (info.block_number..current_number).into_iter()
|
||||||
.map(BlockId::Number)
|
.map(BlockId::Number)
|
||||||
.filter_map(|id| client.block_hash(id))
|
.filter_map(|id| client.block_hash(id))
|
||||||
|
@ -907,9 +907,8 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
match sync.send(peer_id, packet_id, packet) {
|
match sync.send(peer_id, packet_id, packet) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(target:"sync", "Error sending request: {:?}", e);
|
debug!(target:"sync", "Error sending request: {:?}", e);
|
||||||
sync.disable_peer(peer_id);
|
sync.disable_peer(peer_id);
|
||||||
self.on_peer_aborting(sync, peer_id);
|
|
||||||
}
|
}
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let mut peer = self.peers.get_mut(&peer_id).unwrap();
|
let mut peer = self.peers.get_mut(&peer_id).unwrap();
|
||||||
@ -922,9 +921,8 @@ impl ChainSync {
|
|||||||
/// Generic packet sender
|
/// Generic packet sender
|
||||||
fn send_packet(&mut self, sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
fn send_packet(&mut self, sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
||||||
if let Err(e) = sync.send(peer_id, packet_id, packet) {
|
if let Err(e) = sync.send(peer_id, packet_id, packet) {
|
||||||
warn!(target:"sync", "Error sending packet: {:?}", e);
|
debug!(target:"sync", "Error sending packet: {:?}", e);
|
||||||
sync.disable_peer(peer_id);
|
sync.disable_peer(peer_id);
|
||||||
self.on_peer_aborting(sync, peer_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Called when peer sends us new transactions
|
/// Called when peer sends us new transactions
|
||||||
|
@ -25,10 +25,7 @@ use kvdb::{Database, DBTransaction, DatabaseConfig};
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay
|
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay
|
||||||
/// and, possibly, latent-removal semantics.
|
/// and latent-removal semantics.
|
||||||
///
|
|
||||||
/// If `counters` is `None`, then it behaves exactly like OverlayDB. If not it behaves
|
|
||||||
/// differently:
|
|
||||||
///
|
///
|
||||||
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
||||||
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
||||||
@ -60,7 +57,6 @@ const DB_VERSION_NO_JOURNAL : u32 = 3 + 256;
|
|||||||
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
||||||
|
|
||||||
impl JournalDB {
|
impl JournalDB {
|
||||||
|
|
||||||
/// Create a new instance from file
|
/// Create a new instance from file
|
||||||
pub fn new(path: &str) -> JournalDB {
|
pub fn new(path: &str) -> JournalDB {
|
||||||
Self::from_prefs(path, true)
|
Self::from_prefs(path, true)
|
||||||
@ -149,6 +145,87 @@ impl JournalDB {
|
|||||||
Ok(ret as u32)
|
Ok(ret as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn morph_key(key: &H256, index: u8) -> Bytes {
|
||||||
|
let mut ret = key.bytes().to_owned();
|
||||||
|
ret.push(index);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next three are valid only as long as there is an insert operation of `key` in the journal.
|
||||||
|
fn set_already_in(batch: &DBTransaction, key: &H256) { batch.put(&Self::morph_key(key, 0), &[1u8]).expect("Low-level database error. Some issue with your hard disk?"); }
|
||||||
|
fn reset_already_in(batch: &DBTransaction, key: &H256) { batch.delete(&Self::morph_key(key, 0)).expect("Low-level database error. Some issue with your hard disk?"); }
|
||||||
|
fn is_already_in(backing: &Database, key: &H256) -> bool {
|
||||||
|
backing.get(&Self::morph_key(key, 0)).expect("Low-level database error. Some issue with your hard disk?").is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_keys(inserts: &Vec<(H256, Bytes)>, backing: &Database, counters: &mut HashMap<H256, i32>, batch: &DBTransaction) {
|
||||||
|
for &(ref h, ref d) in inserts {
|
||||||
|
if let Some(c) = counters.get_mut(h) {
|
||||||
|
// already counting. increment.
|
||||||
|
*c += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the first entry for this node in the journal.
|
||||||
|
if backing.get(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?").is_some() {
|
||||||
|
// already in the backing DB. start counting, and remember it was already in.
|
||||||
|
Self::set_already_in(batch, &h);
|
||||||
|
counters.insert(h.clone(), 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets removed when a key leaves the journal, so should never be set when we're placing a new key.
|
||||||
|
//Self::reset_already_in(&h);
|
||||||
|
assert!(!Self::is_already_in(backing, &h));
|
||||||
|
batch.put(&h.bytes(), d).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replay_keys(inserts: &Vec<H256>, backing: &Database, counters: &mut HashMap<H256, i32>) {
|
||||||
|
trace!("replay_keys: inserts={:?}, counters={:?}", inserts, counters);
|
||||||
|
for h in inserts {
|
||||||
|
if let Some(c) = counters.get_mut(h) {
|
||||||
|
// already counting. increment.
|
||||||
|
*c += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the first entry for this node in the journal.
|
||||||
|
// it is initialised to 1 if it was already in.
|
||||||
|
if Self::is_already_in(backing, h) {
|
||||||
|
trace!("replace_keys: Key {} was already in!", h);
|
||||||
|
counters.insert(h.clone(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("replay_keys: (end) counters={:?}", counters);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_keys(deletes: Vec<H256>, counters: &mut HashMap<H256, i32>, batch: &DBTransaction) {
|
||||||
|
for h in deletes.into_iter() {
|
||||||
|
let mut n: Option<i32> = None;
|
||||||
|
if let Some(c) = counters.get_mut(&h) {
|
||||||
|
if *c > 1 {
|
||||||
|
*c -= 1;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
n = Some(*c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &n {
|
||||||
|
&Some(i) if i == 1 => {
|
||||||
|
counters.remove(&h);
|
||||||
|
Self::reset_already_in(batch, &h);
|
||||||
|
}
|
||||||
|
&None => {
|
||||||
|
// Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs.
|
||||||
|
//assert!(!Self::is_already_in(db, &h));
|
||||||
|
batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid value in counters: {:?}", n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Commit all recent insert operations and historical removals from the old era
|
/// Commit all recent insert operations and historical removals from the old era
|
||||||
/// to the backing database.
|
/// to the backing database.
|
||||||
fn commit_with_counters(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
fn commit_with_counters(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
@ -159,60 +236,85 @@ impl JournalDB {
|
|||||||
|
|
||||||
// TODO: store reclaim_period.
|
// TODO: store reclaim_period.
|
||||||
|
|
||||||
// when we make a new commit, we journal the inserts and removes.
|
// When we make a new commit, we make a journal of all blocks in the recent history and record
|
||||||
// for each end_era that we journaled that we are no passing by,
|
// all keys that were inserted and deleted. The journal is ordered by era; multiple commits can
|
||||||
// we remove all of its removes assuming it is canonical and all
|
// share the same era. This forms a data structure similar to a queue but whose items are tuples.
|
||||||
// of its inserts otherwise.
|
// By the time comes to remove a tuple from the queue (i.e. then the era passes from recent history
|
||||||
|
// into ancient history) then only one commit from the tuple is considered canonical. This commit
|
||||||
|
// is kept in the main backing database, whereas any others from the same era are reverted.
|
||||||
|
//
|
||||||
|
// It is possible that a key, properly available in the backing database be deleted and re-inserted
|
||||||
|
// in the recent history queue, yet have both operations in commits that are eventually non-canonical.
|
||||||
|
// To avoid the original, and still required, key from being deleted, we maintain a reference count
|
||||||
|
// which includes an original key, if any.
|
||||||
|
//
|
||||||
|
// The semantics of the `counter` are:
|
||||||
|
// insert key k:
|
||||||
|
// counter already contains k: count += 1
|
||||||
|
// counter doesn't contain k:
|
||||||
|
// backing db contains k: count = 1
|
||||||
|
// backing db doesn't contain k: insert into backing db, count = 0
|
||||||
|
// delete key k:
|
||||||
|
// counter contains k (count is asserted to be non-zero):
|
||||||
|
// count > 1: counter -= 1
|
||||||
|
// count == 1: remove counter
|
||||||
|
// count == 0: remove key from backing db
|
||||||
|
// counter doesn't contain k: remove key from backing db
|
||||||
//
|
//
|
||||||
// We also keep reference counters for each key inserted in the journal to handle
|
// Practically, this means that for each commit block turning from recent to ancient we do the
|
||||||
// the following cases where key K must not be deleted from the DB when processing removals :
|
// following:
|
||||||
// Given H is the journal size in eras, 0 <= C <= H.
|
// is_canonical:
|
||||||
// Key K is removed in era A(N) and re-inserted in canonical era B(N + C).
|
// inserts: Ignored (left alone in the backing database).
|
||||||
// Key K is removed in era A(N) and re-inserted in non-canonical era B`(N + C).
|
// deletes: Enacted; however, recent history queue is checked for ongoing references. This is
|
||||||
// Key K is added in non-canonical era A'(N) canonical B(N + C).
|
// reduced as a preference to deletion from the backing database.
|
||||||
|
// !is_canonical:
|
||||||
|
// inserts: Reverted; however, recent history queue is checked for ongoing references. This is
|
||||||
|
// reduced as a preference to deletion from the backing database.
|
||||||
|
// deletes: Ignored (they were never inserted).
|
||||||
//
|
//
|
||||||
// The counter is encreased each time a key is inserted in the journal in the commit. The list of insertions
|
|
||||||
// is saved with the era record. When the era becomes end_era and goes out of journal the counter is decreased
|
|
||||||
// and the key is safe to delete.
|
|
||||||
|
|
||||||
// record new commit's details.
|
// record new commit's details.
|
||||||
trace!("commit: #{} ({}), end era: {:?}", now, id, end);
|
trace!("commit: #{} ({}), end era: {:?}", now, id, end);
|
||||||
let mut counters = self.counters.as_ref().unwrap().write().unwrap();
|
let mut counters = self.counters.as_ref().unwrap().write().unwrap();
|
||||||
let batch = DBTransaction::new();
|
let batch = DBTransaction::new();
|
||||||
{
|
{
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
let mut last;
|
let mut last;
|
||||||
|
|
||||||
while {
|
while try!(self.backing.get({
|
||||||
let record = try!(self.backing.get({
|
let mut r = RlpStream::new_list(3);
|
||||||
let mut r = RlpStream::new_list(3);
|
r.append(&now);
|
||||||
r.append(&now);
|
r.append(&index);
|
||||||
r.append(&index);
|
r.append(&&PADDING[..]);
|
||||||
r.append(&&PADDING[..]);
|
last = r.drain();
|
||||||
last = r.drain();
|
&last
|
||||||
&last
|
})).is_some() {
|
||||||
}));
|
|
||||||
match record {
|
|
||||||
Some(r) => {
|
|
||||||
assert!(&Rlp::new(&r).val_at::<H256>(0) != id);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let drained = self.overlay.drain();
|
||||||
|
let removes: Vec<H256> = drained
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(ref k, &(_, ref c))| if *c < 0 {Some(k.clone())} else {None}).cloned()
|
||||||
|
.collect();
|
||||||
|
let inserts: Vec<(H256, Bytes)> = drained
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(k, (v, r))| if r > 0 { assert!(r == 1); Some((k, v)) } else { assert!(r >= -1); None })
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut r = RlpStream::new_list(3);
|
let mut r = RlpStream::new_list(3);
|
||||||
let inserts: Vec<H256> = self.overlay.keys().iter().filter(|&(_, &c)| c > 0).map(|(key, _)| key.clone()).collect();
|
|
||||||
// Increase counter for each inserted key no matter if the block is canonical or not.
|
|
||||||
for i in &inserts {
|
|
||||||
*counters.entry(i.clone()).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
let removes: Vec<H256> = self.overlay.keys().iter().filter(|&(_, &c)| c < 0).map(|(key, _)| key.clone()).collect();
|
|
||||||
r.append(id);
|
r.append(id);
|
||||||
r.append(&inserts);
|
|
||||||
|
// Process the new inserts.
|
||||||
|
// We use the inserts for three things. For each:
|
||||||
|
// - we place into the backing DB or increment the counter if already in;
|
||||||
|
// - we note in the backing db that it was already in;
|
||||||
|
// - we write the key into our journal for this block;
|
||||||
|
|
||||||
|
r.begin_list(inserts.len());
|
||||||
|
inserts.iter().foreach(|&(k, _)| {r.append(&k);});
|
||||||
r.append(&removes);
|
r.append(&removes);
|
||||||
|
Self::insert_keys(&inserts, &self.backing, &mut counters, &batch);
|
||||||
try!(batch.put(&last, r.as_raw()));
|
try!(batch.put(&last, r.as_raw()));
|
||||||
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
||||||
}
|
}
|
||||||
@ -221,8 +323,6 @@ impl JournalDB {
|
|||||||
if let Some((end_era, canon_id)) = end {
|
if let Some((end_era, canon_id)) = end {
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
let mut last;
|
let mut last;
|
||||||
let mut to_remove: Vec<H256> = Vec::new();
|
|
||||||
let mut canon_inserts: Vec<H256> = Vec::new();
|
|
||||||
while let Some(rlp_data) = try!(self.backing.get({
|
while let Some(rlp_data) = try!(self.backing.get({
|
||||||
let mut r = RlpStream::new_list(3);
|
let mut r = RlpStream::new_list(3);
|
||||||
r.append(&end_era);
|
r.append(&end_era);
|
||||||
@ -232,54 +332,19 @@ impl JournalDB {
|
|||||||
&last
|
&last
|
||||||
})) {
|
})) {
|
||||||
let rlp = Rlp::new(&rlp_data);
|
let rlp = Rlp::new(&rlp_data);
|
||||||
let mut inserts: Vec<H256> = rlp.val_at(1);
|
let inserts: Vec<H256> = rlp.val_at(1);
|
||||||
JournalDB::decrease_counters(&inserts, &mut counters);
|
let deletes: Vec<H256> = rlp.val_at(2);
|
||||||
// Collect keys to be removed. These are removed keys for canonical block, inserted for non-canonical
|
// Collect keys to be removed. These are removed keys for canonical block, inserted for non-canonical
|
||||||
if canon_id == rlp.val_at(0) {
|
Self::kill_keys(if canon_id == rlp.val_at(0) {deletes} else {inserts}, &mut counters, &batch);
|
||||||
let mut canon_deletes: Vec<H256> = rlp.val_at(2);
|
|
||||||
trace!("Purging nodes deleted from canon: {:?}", canon_deletes);
|
|
||||||
to_remove.append(&mut canon_deletes);
|
|
||||||
canon_inserts = inserts;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trace!("Purging nodes inserted in non-canon: {:?}", inserts);
|
|
||||||
to_remove.append(&mut inserts);
|
|
||||||
}
|
|
||||||
trace!("commit: Delete journal for time #{}.{}: {}, (canon was {}): {} entries", end_era, index, rlp.val_at::<H256>(0), canon_id, to_remove.len());
|
|
||||||
try!(batch.delete(&last));
|
try!(batch.delete(&last));
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
trace!("JournalDB: delete journal for time #{}.{}, (canon was {})", end_era, index, canon_id);
|
||||||
let canon_inserts = canon_inserts.drain(..).collect::<HashSet<_>>();
|
|
||||||
// Purge removed keys if they are not referenced and not re-inserted in the canon commit
|
|
||||||
let mut deletes = 0;
|
|
||||||
trace!("Purging filtered nodes: {:?}", to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)).collect::<Vec<_>>());
|
|
||||||
for h in to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)) {
|
|
||||||
try!(batch.delete(&h));
|
|
||||||
deletes += 1;
|
|
||||||
}
|
|
||||||
trace!("Total nodes purged: {}", deletes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit overlay insertions
|
|
||||||
let ret = Self::batch_overlay_insertions(&mut self.overlay, &batch);
|
|
||||||
try!(self.backing.write(batch));
|
try!(self.backing.write(batch));
|
||||||
Ok(ret as u32)
|
// trace!("JournalDB::commit() deleted {} nodes", deletes);
|
||||||
}
|
Ok(0)
|
||||||
|
|
||||||
|
|
||||||
// Decrease counters for given keys. Deletes obsolete counters
|
|
||||||
fn decrease_counters(keys: &[H256], counters: &mut HashMap<H256, i32>) {
|
|
||||||
for i in keys.iter() {
|
|
||||||
let delete_counter = {
|
|
||||||
let cnt = counters.get_mut(i).expect("Missing key counter");
|
|
||||||
*cnt -= 1;
|
|
||||||
*cnt == 0
|
|
||||||
};
|
|
||||||
if delete_counter {
|
|
||||||
counters.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payload(&self, key: &H256) -> Option<Bytes> {
|
fn payload(&self, key: &H256) -> Option<Bytes> {
|
||||||
@ -287,7 +352,7 @@ impl JournalDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_counters(db: &Database) -> HashMap<H256, i32> {
|
fn read_counters(db: &Database) -> HashMap<H256, i32> {
|
||||||
let mut res = HashMap::new();
|
let mut counters = HashMap::new();
|
||||||
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
||||||
let mut era = decode::<u64>(&val);
|
let mut era = decode::<u64>(&val);
|
||||||
loop {
|
loop {
|
||||||
@ -299,11 +364,10 @@ impl JournalDB {
|
|||||||
r.append(&&PADDING[..]);
|
r.append(&&PADDING[..]);
|
||||||
&r.drain()
|
&r.drain()
|
||||||
}).expect("Low-level database error.") {
|
}).expect("Low-level database error.") {
|
||||||
|
trace!("read_counters: era={}, index={}", era, index);
|
||||||
let rlp = Rlp::new(&rlp_data);
|
let rlp = Rlp::new(&rlp_data);
|
||||||
let to_add: Vec<H256> = rlp.val_at(1);
|
let inserts: Vec<H256> = rlp.val_at(1);
|
||||||
for h in to_add {
|
Self::replay_keys(&inserts, db, &mut counters);
|
||||||
*res.entry(h).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
index += 1;
|
index += 1;
|
||||||
};
|
};
|
||||||
if index == 0 || era == 0 {
|
if index == 0 || era == 0 {
|
||||||
@ -312,10 +376,15 @@ impl JournalDB {
|
|||||||
era -= 1;
|
era -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace!("Recovered {} counters", res.len());
|
trace!("Recovered {} counters", counters.len());
|
||||||
res
|
counters
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// Returns heap memory size used
|
||||||
|
pub fn mem_used(&self) -> usize {
|
||||||
|
self.overlay.mem_used() + match &self.counters { &Some(ref c) => c.read().unwrap().heap_size_of_children(), &None => 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HashDB for JournalDB {
|
impl HashDB for JournalDB {
|
||||||
fn keys(&self) -> HashMap<H256, i32> {
|
fn keys(&self) -> HashMap<H256, i32> {
|
||||||
@ -368,6 +437,28 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_same_in_fork() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = JournalDB::new_temp();
|
||||||
|
|
||||||
|
let x = jdb.insert(b"X");
|
||||||
|
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
||||||
|
jdb.commit(2, &b"2".sha3(), None).unwrap();
|
||||||
|
jdb.commit(3, &b"1002a".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
jdb.commit(4, &b"1003a".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&x);
|
||||||
|
jdb.commit(3, &b"1002b".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
let x = jdb.insert(b"X");
|
||||||
|
jdb.commit(4, &b"1003b".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.commit(5, &b"1004a".sha3(), Some((3, b"1002a".sha3()))).unwrap();
|
||||||
|
jdb.commit(6, &b"1005a".sha3(), Some((4, b"1003a".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&x));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn long_history() {
|
fn long_history() {
|
||||||
// history is 3
|
// history is 3
|
||||||
@ -488,15 +579,18 @@ mod tests {
|
|||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reopen() {
|
fn reopen() {
|
||||||
let mut dir = ::std::env::temp_dir();
|
let mut dir = ::std::env::temp_dir();
|
||||||
dir.push(H32::random().hex());
|
dir.push(H32::random().hex());
|
||||||
|
let bar = H256::random();
|
||||||
|
|
||||||
let foo = {
|
let foo = {
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
// history is 1
|
// history is 1
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.emplace(bar.clone(), b"bar".to_vec());
|
||||||
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
foo
|
foo
|
||||||
};
|
};
|
||||||
@ -510,8 +604,68 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
assert!(!jdb.exists(&foo));
|
assert!(!jdb.exists(&foo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reopen_remove() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
let bar = H256::random();
|
||||||
|
|
||||||
|
let foo = {
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
// foo is ancient history.
|
||||||
|
|
||||||
|
jdb.insert(b"foo");
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
foo
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((3, b"3".sha3()))).unwrap();
|
||||||
|
jdb.commit(5, &b"5".sha3(), Some((4, b"4".sha3()))).unwrap();
|
||||||
|
assert!(!jdb.exists(&foo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn reopen_fork() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
let (foo, bar, baz) = {
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
jdb.remove(&foo);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1a".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(1, &b"1b".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
(foo, bar, baz)
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(!jdb.exists(&baz));
|
||||||
|
assert!(!jdb.exists(&bar));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ impl SecretStore {
|
|||||||
let mut path = ::std::env::home_dir().expect("Failed to get home dir");
|
let mut path = ::std::env::home_dir().expect("Failed to get home dir");
|
||||||
path.push(".parity");
|
path.push(".parity");
|
||||||
path.push("keys");
|
path.push("keys");
|
||||||
|
::std::fs::create_dir_all(&path).expect("Should panic since it is critical to be able to access home dir");
|
||||||
Self::new_in(&path)
|
Self::new_in(&path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ use bytes::*;
|
|||||||
use rlp::*;
|
use rlp::*;
|
||||||
use sha3::*;
|
use sha3::*;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
|
use heapsize::*;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -143,6 +144,11 @@ impl MemoryDB {
|
|||||||
}
|
}
|
||||||
self.raw(key).unwrap()
|
self.raw(key).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the size of allocated heap memory
|
||||||
|
pub fn mem_used(&self) -> usize {
|
||||||
|
self.data.heap_size_of_children()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
|
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
|
||||||
|
@ -190,25 +190,25 @@ impl Connection {
|
|||||||
|
|
||||||
/// Register this connection with the IO event loop.
|
/// Register this connection with the IO event loop.
|
||||||
pub fn register_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
pub fn register_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
||||||
trace!(target: "net", "connection register; token={:?}", reg);
|
trace!(target: "network", "connection register; token={:?}", reg);
|
||||||
if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()) {
|
if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()) {
|
||||||
debug!("Failed to register {:?}, {:?}", reg, e);
|
trace!(target: "network", "Failed to register {:?}, {:?}", reg, e);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update connection registration. Should be called at the end of the IO handler.
|
/// Update connection registration. Should be called at the end of the IO handler.
|
||||||
pub fn update_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
pub fn update_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
||||||
trace!(target: "net", "connection reregister; token={:?}", reg);
|
trace!(target: "network", "connection reregister; token={:?}", reg);
|
||||||
event_loop.reregister( &self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()).or_else(|e| {
|
event_loop.reregister( &self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()).or_else(|e| {
|
||||||
debug!("Failed to reregister {:?}, {:?}", reg, e);
|
trace!(target: "network", "Failed to reregister {:?}, {:?}", reg, e);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete connection registration. Should be called at the end of the IO handler.
|
/// Delete connection registration. Should be called at the end of the IO handler.
|
||||||
pub fn deregister_socket<Host: Handler>(&self, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
pub fn deregister_socket<Host: Handler>(&self, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
||||||
trace!(target: "net", "connection deregister; token={:?}", self.token);
|
trace!(target: "network", "connection deregister; token={:?}", self.token);
|
||||||
event_loop.deregister(&self.socket).ok(); // ignore errors here
|
event_loop.deregister(&self.socket).ok(); // ignore errors here
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Parse, validate and confirm auth message
|
/// Parse, validate and confirm auth message
|
||||||
fn read_auth(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_auth(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received handshake auth from {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received handshake auth from {:?}", self.connection.socket.peer_addr());
|
||||||
if data.len() != V4_AUTH_PACKET_SIZE {
|
if data.len() != V4_AUTH_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong auth packet size");
|
debug!(target:"net", "Wrong auth packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
@ -253,7 +253,7 @@ impl Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_auth_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_auth_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
||||||
self.auth_cipher.extend_from_slice(data);
|
self.auth_cipher.extend_from_slice(data);
|
||||||
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
||||||
let rlp = UntrustedRlp::new(&auth);
|
let rlp = UntrustedRlp::new(&auth);
|
||||||
@ -268,7 +268,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Parse and validate ack message
|
/// Parse and validate ack message
|
||||||
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received handshake auth to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||||
if data.len() != V4_ACK_PACKET_SIZE {
|
if data.len() != V4_ACK_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong ack packet size");
|
debug!(target:"net", "Wrong ack packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
@ -296,7 +296,7 @@ impl Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
||||||
self.ack_cipher.extend_from_slice(data);
|
self.ack_cipher.extend_from_slice(data);
|
||||||
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
|
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
|
||||||
let rlp = UntrustedRlp::new(&ack);
|
let rlp = UntrustedRlp::new(&ack);
|
||||||
@ -309,7 +309,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends auth message
|
/// Sends auth message
|
||||||
fn write_auth(&mut self, secret: &Secret, public: &Public) -> Result<(), UtilError> {
|
fn write_auth(&mut self, secret: &Secret, public: &Public) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Sending handshake auth to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Sending handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||||
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
{
|
{
|
||||||
@ -336,7 +336,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends ack message
|
/// Sends ack message
|
||||||
fn write_ack(&mut self) -> Result<(), UtilError> {
|
fn write_ack(&mut self) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Sending handshake ack to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Sending handshake ack to {:?}", self.connection.socket.peer_addr());
|
||||||
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
{
|
{
|
||||||
@ -355,7 +355,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends EIP8 ack message
|
/// Sends EIP8 ack message
|
||||||
fn write_ack_eip8(&mut self) -> Result<(), UtilError> {
|
fn write_ack_eip8(&mut self) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Sending EIP8 handshake ack to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Sending EIP8 handshake ack to {:?}", self.connection.socket.peer_addr());
|
||||||
let mut rlp = RlpStream::new_list(3);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(self.ecdhe.public());
|
rlp.append(self.ecdhe.public());
|
||||||
rlp.append(&self.nonce);
|
rlp.append(&self.nonce);
|
||||||
|
@ -170,29 +170,37 @@ pub struct NetworkContext<'s, Message> where Message: Send + Sync + Clone + 'sta
|
|||||||
io: &'s IoContext<NetworkIoMessage<Message>>,
|
io: &'s IoContext<NetworkIoMessage<Message>>,
|
||||||
protocol: ProtocolId,
|
protocol: ProtocolId,
|
||||||
sessions: Arc<RwLock<Slab<SharedSession>>>,
|
sessions: Arc<RwLock<Slab<SharedSession>>>,
|
||||||
session: Option<StreamToken>,
|
session: Option<SharedSession>,
|
||||||
|
session_id: Option<StreamToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone + 'static, {
|
impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone + 'static, {
|
||||||
/// Create a new network IO access point. Takes references to all the data that can be updated within the IO handler.
|
/// Create a new network IO access point. Takes references to all the data that can be updated within the IO handler.
|
||||||
fn new(io: &'s IoContext<NetworkIoMessage<Message>>,
|
fn new(io: &'s IoContext<NetworkIoMessage<Message>>,
|
||||||
protocol: ProtocolId,
|
protocol: ProtocolId,
|
||||||
session: Option<StreamToken>, sessions: Arc<RwLock<Slab<SharedSession>>>) -> NetworkContext<'s, Message> {
|
session: Option<SharedSession>, sessions: Arc<RwLock<Slab<SharedSession>>>) -> NetworkContext<'s, Message> {
|
||||||
|
let id = session.as_ref().map(|s| s.lock().unwrap().token());
|
||||||
NetworkContext {
|
NetworkContext {
|
||||||
io: io,
|
io: io,
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
|
session_id: id,
|
||||||
session: session,
|
session: session,
|
||||||
sessions: sessions,
|
sessions: sessions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_session(&self, peer: PeerId) -> Option<SharedSession> {
|
||||||
|
match self.session_id {
|
||||||
|
Some(id) if id == peer => self.session.clone(),
|
||||||
|
_ => self.sessions.read().unwrap().get(peer).cloned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a packet over the network to another peer.
|
/// Send a packet over the network to another peer.
|
||||||
pub fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
pub fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
||||||
let session = { self.sessions.read().unwrap().get(peer).cloned() };
|
let session = self.resolve_session(peer);
|
||||||
if let Some(session) = session {
|
if let Some(session) = session {
|
||||||
session.lock().unwrap().deref_mut().send_packet(self.protocol, packet_id as u8, &data).unwrap_or_else(|e| {
|
try!(session.lock().unwrap().deref_mut().send_packet(self.protocol, packet_id as u8, &data));
|
||||||
warn!(target: "network", "Send error: {:?}", e);
|
|
||||||
}); //TODO: don't copy vector data
|
|
||||||
try!(self.io.update_registration(peer));
|
try!(self.io.update_registration(peer));
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "network", "Send: Peer no longer exist")
|
trace!(target: "network", "Send: Peer no longer exist")
|
||||||
@ -200,14 +208,10 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Respond to a current network message. Panics if no there is no packet in the context.
|
/// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing.
|
||||||
pub fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
pub fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
||||||
match self.session {
|
assert!(self.session.is_some(), "Respond called without network context");
|
||||||
Some(session) => self.send(session, packet_id, data),
|
self.send(self.session_id.unwrap(), packet_id, data)
|
||||||
None => {
|
|
||||||
panic!("Respond: Session does not exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send an IO message
|
/// Send an IO message
|
||||||
@ -215,7 +219,6 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
|
|||||||
self.io.message(NetworkIoMessage::User(msg));
|
self.io.message(NetworkIoMessage::User(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected.
|
/// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected.
|
||||||
pub fn disable_peer(&self, peer: PeerId) {
|
pub fn disable_peer(&self, peer: PeerId) {
|
||||||
//TODO: remove capability, disconnect if no capabilities left
|
//TODO: remove capability, disconnect if no capabilities left
|
||||||
@ -239,7 +242,7 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
|
|||||||
|
|
||||||
/// Returns peer identification string
|
/// Returns peer identification string
|
||||||
pub fn peer_info(&self, peer: PeerId) -> String {
|
pub fn peer_info(&self, peer: PeerId) -> String {
|
||||||
let session = { self.sessions.read().unwrap().get(peer).cloned() };
|
let session = self.resolve_session(peer);
|
||||||
if let Some(session) = session {
|
if let Some(session) = session {
|
||||||
return session.lock().unwrap().info.client_version.clone()
|
return session.lock().unwrap().info.client_version.clone()
|
||||||
}
|
}
|
||||||
@ -624,7 +627,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
let mut packet_data: Option<(ProtocolId, PacketId, Vec<u8>)> = None;
|
let mut packet_data: Option<(ProtocolId, PacketId, Vec<u8>)> = None;
|
||||||
let mut kill = false;
|
let mut kill = false;
|
||||||
let session = { self.sessions.read().unwrap().get(token).cloned() };
|
let session = { self.sessions.read().unwrap().get(token).cloned() };
|
||||||
if let Some(session) = session {
|
if let Some(session) = session.clone() {
|
||||||
let mut s = session.lock().unwrap();
|
let mut s = session.lock().unwrap();
|
||||||
match s.readable(io, &self.info.read().unwrap()) {
|
match s.readable(io, &self.info.read().unwrap()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -656,11 +659,11 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
}
|
}
|
||||||
for p in ready_data {
|
for p in ready_data {
|
||||||
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
||||||
h.connected(&NetworkContext::new(io, p, Some(token), self.sessions.clone()), &token);
|
h.connected(&NetworkContext::new(io, p, session.clone(), self.sessions.clone()), &token);
|
||||||
}
|
}
|
||||||
if let Some((p, packet_id, data)) = packet_data {
|
if let Some((p, packet_id, data)) = packet_data {
|
||||||
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
||||||
h.read(&NetworkContext::new(io, p, Some(token), self.sessions.clone()), &token, packet_id, &data[1..]);
|
h.read(&NetworkContext::new(io, p, session.clone(), self.sessions.clone()), &token, packet_id, &data[1..]);
|
||||||
}
|
}
|
||||||
io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e));
|
io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e));
|
||||||
}
|
}
|
||||||
@ -718,6 +721,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
let mut to_disconnect: Vec<ProtocolId> = Vec::new();
|
let mut to_disconnect: Vec<ProtocolId> = Vec::new();
|
||||||
let mut failure_id = None;
|
let mut failure_id = None;
|
||||||
let mut deregister = false;
|
let mut deregister = false;
|
||||||
|
let mut expired_session = None;
|
||||||
match token {
|
match token {
|
||||||
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
|
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
|
||||||
let handshakes = self.handshakes.write().unwrap();
|
let handshakes = self.handshakes.write().unwrap();
|
||||||
@ -733,6 +737,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
FIRST_SESSION ... LAST_SESSION => {
|
FIRST_SESSION ... LAST_SESSION => {
|
||||||
let sessions = self.sessions.write().unwrap();
|
let sessions = self.sessions.write().unwrap();
|
||||||
if let Some(session) = sessions.get(token).cloned() {
|
if let Some(session) = sessions.get(token).cloned() {
|
||||||
|
expired_session = Some(session.clone());
|
||||||
let mut s = session.lock().unwrap();
|
let mut s = session.lock().unwrap();
|
||||||
if !s.expired() {
|
if !s.expired() {
|
||||||
if s.is_ready() {
|
if s.is_ready() {
|
||||||
@ -757,7 +762,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
}
|
}
|
||||||
for p in to_disconnect {
|
for p in to_disconnect {
|
||||||
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
||||||
h.disconnected(&NetworkContext::new(io, p, Some(token), self.sessions.clone()), &token);
|
h.disconnected(&NetworkContext::new(io, p, expired_session.clone(), self.sessions.clone()), &token);
|
||||||
}
|
}
|
||||||
if deregister {
|
if deregister {
|
||||||
io.deregister_stream(token).expect("Error deregistering stream");
|
io.deregister_stream(token).expect("Error deregistering stream");
|
||||||
|
@ -213,6 +213,9 @@ impl Session {
|
|||||||
|
|
||||||
/// Send a protocol packet to peer.
|
/// Send a protocol packet to peer.
|
||||||
pub fn send_packet(&mut self, protocol: &str, packet_id: u8, data: &[u8]) -> Result<(), UtilError> {
|
pub fn send_packet(&mut self, protocol: &str, packet_id: u8, data: &[u8]) -> Result<(), UtilError> {
|
||||||
|
if self.expired() {
|
||||||
|
return Err(From::from(NetworkError::Expired));
|
||||||
|
}
|
||||||
let mut i = 0usize;
|
let mut i = 0usize;
|
||||||
while protocol != self.info.capabilities[i].protocol {
|
while protocol != self.info.capabilities[i].protocol {
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -351,15 +354,15 @@ impl Session {
|
|||||||
offset += caps[i].packet_count;
|
offset += caps[i].packet_count;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
trace!(target: "net", "Hello: {} v{} {} {:?}", client_version, protocol, id, caps);
|
trace!(target: "network", "Hello: {} v{} {} {:?}", client_version, protocol, id, caps);
|
||||||
self.info.client_version = client_version;
|
self.info.client_version = client_version;
|
||||||
self.info.capabilities = caps;
|
self.info.capabilities = caps;
|
||||||
if self.info.capabilities.is_empty() {
|
if self.info.capabilities.is_empty() {
|
||||||
trace!("No common capabilities with peer.");
|
trace!(target: "network", "No common capabilities with peer.");
|
||||||
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
||||||
}
|
}
|
||||||
if protocol != host.protocol_version {
|
if protocol != host.protocol_version {
|
||||||
trace!("Peer protocol version mismatch: {}", protocol);
|
trace!(target: "network", "Peer protocol version mismatch: {}", protocol);
|
||||||
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
||||||
}
|
}
|
||||||
self.had_hello = true;
|
self.had_hello = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user