Simple signing queue, confirmation APIs exposed in signer WebSockets. (#1182)
* Splitting methods requiring signing into separate trait * Single place where RPC apis are created. * Separating eth_filter * Separating eth_signing * Stubs for Personal Signer methods * Test for EthSigningQueueClient * TransactionConfirmation API * Exposing PersonalSigner API * Defining ApiSets dependent on context * Removing types * Fixing default impl * Fixing un-mocked tests * Update signing_queue.rs [ci skip] * Removing unused import [ci skip]
This commit is contained in:
parent
b9b0ce8d65
commit
99e26b8480
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -358,12 +358,7 @@ dependencies = [
|
|||||||
"ethcore-util 1.2.0",
|
"ethcore-util 1.2.0",
|
||||||
"jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.4 (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)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_codegen 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"ws 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ws 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ extern crate serde_json;
|
|||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate parity_dapps;
|
extern crate parity_dapps;
|
||||||
|
extern crate ethcore_rpc;
|
||||||
|
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod apps;
|
mod apps;
|
||||||
@ -66,6 +67,7 @@ use std::net::SocketAddr;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use jsonrpc_core::{IoHandler, IoDelegate};
|
use jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||||
|
use ethcore_rpc::Extendable;
|
||||||
|
|
||||||
static DAPPS_DOMAIN : &'static str = ".parity";
|
static DAPPS_DOMAIN : &'static str = ".parity";
|
||||||
|
|
||||||
@ -74,6 +76,12 @@ pub struct ServerBuilder {
|
|||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Extendable for ServerBuilder {
|
||||||
|
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||||
|
self.handler.add_delegate(delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ServerBuilder {
|
impl ServerBuilder {
|
||||||
/// Construct new dapps server
|
/// Construct new dapps server
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -82,11 +90,6 @@ impl ServerBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add io delegate.
|
|
||||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
|
||||||
self.handler.add_delegate(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
||||||
|
@ -17,14 +17,9 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use ethcore::client::Client;
|
|
||||||
use ethsync::EthSync;
|
|
||||||
use ethminer::{Miner, ExternalMiner};
|
|
||||||
use util::RotatingLogger;
|
|
||||||
use util::panics::PanicHandler;
|
use util::panics::PanicHandler;
|
||||||
use util::keys::store::AccountService;
|
|
||||||
use util::network_settings::NetworkSettings;
|
|
||||||
use die::*;
|
use die::*;
|
||||||
|
use rpc_apis;
|
||||||
|
|
||||||
#[cfg(feature = "dapps")]
|
#[cfg(feature = "dapps")]
|
||||||
pub use ethcore_dapps::Server as WebappServer;
|
pub use ethcore_dapps::Server as WebappServer;
|
||||||
@ -41,13 +36,7 @@ pub struct Configuration {
|
|||||||
|
|
||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
pub panic_handler: Arc<PanicHandler>,
|
pub panic_handler: Arc<PanicHandler>,
|
||||||
pub client: Arc<Client>,
|
pub apis: Arc<rpc_apis::Dependencies>,
|
||||||
pub sync: Arc<EthSync>,
|
|
||||||
pub secret_store: Arc<AccountService>,
|
|
||||||
pub miner: Arc<Miner>,
|
|
||||||
pub external_miner: Arc<ExternalMiner>,
|
|
||||||
pub logger: Arc<RotatingLogger>,
|
|
||||||
pub settings: Arc<NetworkSettings>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappServer> {
|
pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappServer> {
|
||||||
@ -92,17 +81,10 @@ pub fn setup_dapps_server(
|
|||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
auth: Option<(String, String)>
|
auth: Option<(String, String)>
|
||||||
) -> WebappServer {
|
) -> WebappServer {
|
||||||
use ethcore_rpc::v1::*;
|
|
||||||
use ethcore_dapps as dapps;
|
use ethcore_dapps as dapps;
|
||||||
|
|
||||||
let server = dapps::ServerBuilder::new();
|
let server = dapps::ServerBuilder::new();
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
||||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
|
||||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
|
||||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
|
||||||
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
|
|
||||||
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate());
|
|
||||||
|
|
||||||
let start_result = match auth {
|
let start_result = match auth {
|
||||||
None => {
|
None => {
|
||||||
server.start_unsecure_http(url)
|
server.start_unsecure_http(url)
|
||||||
|
@ -67,6 +67,7 @@ mod cli;
|
|||||||
mod configuration;
|
mod configuration;
|
||||||
mod migration;
|
mod migration;
|
||||||
mod signer;
|
mod signer;
|
||||||
|
mod rpc_apis;
|
||||||
|
|
||||||
use std::io::{Write, Read, BufReader, BufRead};
|
use std::io::{Write, Read, BufReader, BufRead};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@ -195,8 +196,9 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
|||||||
// Sync
|
// Sync
|
||||||
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
||||||
|
|
||||||
let dependencies = Arc::new(rpc::Dependencies {
|
let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
|
||||||
panic_handler: panic_handler.clone(),
|
signer_enabled: conf.args.flag_signer,
|
||||||
|
signer_queue: Arc::new(rpc_apis::ConfirmationsQueue::default()),
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
sync: sync.clone(),
|
sync: sync.clone(),
|
||||||
secret_store: account_service.clone(),
|
secret_store: account_service.clone(),
|
||||||
@ -206,6 +208,11 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
|||||||
settings: network_settings.clone(),
|
settings: network_settings.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let dependencies = rpc::Dependencies {
|
||||||
|
panic_handler: panic_handler.clone(),
|
||||||
|
apis: deps_for_rpc_apis.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
// Setup http rpc
|
// Setup http rpc
|
||||||
let rpc_server = rpc::new_http(rpc::HttpConfiguration {
|
let rpc_server = rpc::new_http(rpc::HttpConfiguration {
|
||||||
enabled: network_settings.rpc_enabled,
|
enabled: network_settings.rpc_enabled,
|
||||||
@ -227,26 +234,16 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
|||||||
pass: conf.args.flag_dapps_pass.clone(),
|
pass: conf.args.flag_dapps_pass.clone(),
|
||||||
}, dapps::Dependencies {
|
}, dapps::Dependencies {
|
||||||
panic_handler: panic_handler.clone(),
|
panic_handler: panic_handler.clone(),
|
||||||
client: client.clone(),
|
apis: deps_for_rpc_apis.clone(),
|
||||||
sync: sync.clone(),
|
|
||||||
secret_store: account_service.clone(),
|
|
||||||
miner: miner.clone(),
|
|
||||||
external_miner: external_miner.clone(),
|
|
||||||
logger: logger.clone(),
|
|
||||||
settings: network_settings.clone(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up a signer
|
// Set up a signer
|
||||||
let signer_server = signer::start(signer::Configuration {
|
let signer_server = signer::start(signer::Configuration {
|
||||||
enabled: conf.args.flag_signer,
|
enabled: deps_for_rpc_apis.signer_enabled,
|
||||||
port: conf.args.flag_signer_port,
|
port: conf.args.flag_signer_port,
|
||||||
}, signer::Dependencies {
|
}, signer::Dependencies {
|
||||||
panic_handler: panic_handler.clone(),
|
panic_handler: panic_handler.clone(),
|
||||||
client: client.clone(),
|
apis: deps_for_rpc_apis.clone(),
|
||||||
sync: sync.clone(),
|
|
||||||
secret_store: account_service.clone(),
|
|
||||||
miner: miner.clone(),
|
|
||||||
external_miner: external_miner.clone(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register IO handler
|
// Register IO handler
|
||||||
|
@ -15,19 +15,13 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use ethcore::client::Client;
|
|
||||||
use ethsync::EthSync;
|
|
||||||
use ethminer::{Miner, ExternalMiner};
|
|
||||||
use util::RotatingLogger;
|
|
||||||
use util::panics::PanicHandler;
|
use util::panics::PanicHandler;
|
||||||
use util::keys::store::AccountService;
|
|
||||||
use util::network_settings::NetworkSettings;
|
|
||||||
use die::*;
|
use die::*;
|
||||||
use jsonipc;
|
use jsonipc;
|
||||||
|
use rpc_apis;
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
pub use ethcore_rpc::Server as RpcServer;
|
pub use ethcore_rpc::Server as RpcServer;
|
||||||
@ -52,16 +46,10 @@ pub struct IpcConfiguration {
|
|||||||
|
|
||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
pub panic_handler: Arc<PanicHandler>,
|
pub panic_handler: Arc<PanicHandler>,
|
||||||
pub client: Arc<Client>,
|
pub apis: Arc<rpc_apis::Dependencies>,
|
||||||
pub sync: Arc<EthSync>,
|
|
||||||
pub secret_store: Arc<AccountService>,
|
|
||||||
pub miner: Arc<Miner>,
|
|
||||||
pub external_miner: Arc<ExternalMiner>,
|
|
||||||
pub logger: Arc<RotatingLogger>,
|
|
||||||
pub settings: Arc<NetworkSettings>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_http(conf: HttpConfiguration, deps: &Arc<Dependencies>) -> Option<RpcServer> {
|
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Option<RpcServer> {
|
||||||
if !conf.enabled {
|
if !conf.enabled {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -78,58 +66,23 @@ pub fn new_http(conf: HttpConfiguration, deps: &Arc<Dependencies>) -> Option<Rpc
|
|||||||
Some(setup_http_rpc_server(deps, &addr, conf.cors, apis))
|
Some(setup_http_rpc_server(deps, &addr, conf.cors, apis))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_ipc(conf: IpcConfiguration, deps: &Arc<Dependencies>) -> Option<jsonipc::Server> {
|
pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Option<jsonipc::Server> {
|
||||||
if !conf.enabled { return None; }
|
if !conf.enabled { return None; }
|
||||||
let apis = conf.apis.split(',').collect();
|
let apis = conf.apis.split(',').collect();
|
||||||
Some(setup_ipc_rpc_server(deps, &conf.socket_addr, apis))
|
Some(setup_ipc_rpc_server(deps, &conf.socket_addr, apis))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_rpc_server(apis: Vec<&str>, deps: &Arc<Dependencies>) -> Server {
|
fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server {
|
||||||
use ethcore_rpc::v1::*;
|
let apis = rpc_apis::from_str(apis);
|
||||||
|
|
||||||
let server = Server::new();
|
let server = Server::new();
|
||||||
let mut modules = BTreeMap::new();
|
rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::List(apis))
|
||||||
for api in apis.into_iter() {
|
|
||||||
match api {
|
|
||||||
"web3" => {
|
|
||||||
modules.insert("web3".to_owned(), "1.0".to_owned());
|
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
|
||||||
},
|
|
||||||
"net" => {
|
|
||||||
modules.insert("net".to_owned(), "1.0".to_owned());
|
|
||||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
|
||||||
},
|
|
||||||
"eth" => {
|
|
||||||
modules.insert("eth".to_owned(), "1.0".to_owned());
|
|
||||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
|
||||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
|
||||||
},
|
|
||||||
"personal" => {
|
|
||||||
modules.insert("personal".to_owned(), "1.0".to_owned());
|
|
||||||
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate())
|
|
||||||
},
|
|
||||||
"ethcore" => {
|
|
||||||
modules.insert("ethcore".to_owned(), "1.0".to_owned());
|
|
||||||
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
|
|
||||||
},
|
|
||||||
"traces" => {
|
|
||||||
modules.insert("traces".to_owned(), "1.0".to_owned());
|
|
||||||
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
die!("{}: Invalid API name to be enabled.", api);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.add_delegate(RpcClient::new(modules).to_delegate());
|
|
||||||
server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "rpc"))]
|
#[cfg(not(feature = "rpc"))]
|
||||||
pub fn setup_http_rpc_server(
|
pub fn setup_http_rpc_server(
|
||||||
_deps: Dependencies,
|
_deps: &Dependencies,
|
||||||
_url: &SocketAddr,
|
_url: &SocketAddr,
|
||||||
_cors_domain: Option<String>,
|
_cors_domain: Vec<String>,
|
||||||
_apis: Vec<&str>,
|
_apis: Vec<&str>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||||
@ -137,27 +90,31 @@ pub fn setup_http_rpc_server(
|
|||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
pub fn setup_http_rpc_server(
|
pub fn setup_http_rpc_server(
|
||||||
dependencies: &Arc<Dependencies>,
|
dependencies: &Dependencies,
|
||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
cors_domains: Vec<String>,
|
cors_domains: Vec<String>,
|
||||||
apis: Vec<&str>,
|
apis: Vec<&str>,
|
||||||
) -> RpcServer {
|
) -> RpcServer {
|
||||||
let server = setup_rpc_server(apis, dependencies);
|
let server = setup_rpc_server(apis, dependencies);
|
||||||
let start_result = server.start_http(url, cors_domains);
|
let start_result = server.start_http(url, cors_domains);
|
||||||
let deps = dependencies.clone();
|
let ph = dependencies.panic_handler.clone();
|
||||||
match start_result {
|
match start_result {
|
||||||
Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err),
|
Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err),
|
||||||
Err(e) => die!("RPC: {:?}", e),
|
Err(e) => die!("RPC: {:?}", e),
|
||||||
Ok(server) => {
|
Ok(server) => {
|
||||||
server.set_panic_handler(move || {
|
server.set_panic_handler(move || {
|
||||||
deps.panic_handler.notify_all("Panic in RPC thread.".to_owned());
|
ph.notify_all("Panic in RPC thread.".to_owned());
|
||||||
});
|
});
|
||||||
server
|
server
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "rpc"))]
|
||||||
pub fn setup_ipc_rpc_server(dependencies: &Arc<Dependencies>, addr: &str, apis: Vec<&str>) -> jsonipc::Server {
|
pub fn setup_ipc_rpc_server(_dependencies: &Dependencies, _addr: &str, _apis: Vec<&str>) -> ! {
|
||||||
|
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||||
|
}
|
||||||
|
#[cfg(feature = "rpc")]
|
||||||
|
pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: Vec<&str>) -> jsonipc::Server {
|
||||||
let server = setup_rpc_server(apis, dependencies);
|
let server = setup_rpc_server(apis, dependencies);
|
||||||
match server.start_ipc(addr) {
|
match server.start_ipc(addr) {
|
||||||
Err(jsonipc::Error::Io(io_error)) => die_with_io_error("RPC", io_error),
|
Err(jsonipc::Error::Io(io_error)) => die_with_io_error("RPC", io_error),
|
||||||
|
168
parity/rpc_apis.rs
Normal file
168
parity/rpc_apis.rs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// 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::collections::BTreeMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use die::*;
|
||||||
|
use ethsync::EthSync;
|
||||||
|
use ethminer::{Miner, ExternalMiner};
|
||||||
|
use ethcore::client::Client;
|
||||||
|
use util::RotatingLogger;
|
||||||
|
use util::keys::store::AccountService;
|
||||||
|
use util::network_settings::NetworkSettings;
|
||||||
|
|
||||||
|
#[cfg(feature="rpc")]
|
||||||
|
pub use ethcore_rpc::ConfirmationsQueue;
|
||||||
|
#[cfg(not(feature="rpc"))]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ConfirmationsQueue;
|
||||||
|
|
||||||
|
#[cfg(feature="rpc")]
|
||||||
|
use ethcore_rpc::Extendable;
|
||||||
|
|
||||||
|
pub enum Api {
|
||||||
|
Web3,
|
||||||
|
Net,
|
||||||
|
Eth,
|
||||||
|
Personal,
|
||||||
|
Ethcore,
|
||||||
|
Traces,
|
||||||
|
Rpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ApiError {
|
||||||
|
UnknownApi(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ApiSet {
|
||||||
|
SafeContext,
|
||||||
|
UnsafeContext,
|
||||||
|
List(Vec<Api>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Api {
|
||||||
|
type Err = ApiError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
use self::Api::*;
|
||||||
|
|
||||||
|
match s {
|
||||||
|
"web3" => Ok(Web3),
|
||||||
|
"net" => Ok(Net),
|
||||||
|
"eth" => Ok(Eth),
|
||||||
|
"personal" => Ok(Personal),
|
||||||
|
"ethcore" => Ok(Ethcore),
|
||||||
|
"traces" => Ok(Traces),
|
||||||
|
"rpc" => Ok(Rpc),
|
||||||
|
e => Err(ApiError::UnknownApi(e.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Dependencies {
|
||||||
|
pub signer_enabled: bool,
|
||||||
|
pub signer_queue: Arc<ConfirmationsQueue>,
|
||||||
|
pub client: Arc<Client>,
|
||||||
|
pub sync: Arc<EthSync>,
|
||||||
|
pub secret_store: Arc<AccountService>,
|
||||||
|
pub miner: Arc<Miner>,
|
||||||
|
pub external_miner: Arc<ExternalMiner>,
|
||||||
|
pub logger: Arc<RotatingLogger>,
|
||||||
|
pub settings: Arc<NetworkSettings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
|
||||||
|
let mut modules = BTreeMap::new();
|
||||||
|
for api in apis {
|
||||||
|
let (name, version) = match *api {
|
||||||
|
Api::Web3 => ("web3", "1.0"),
|
||||||
|
Api::Net => ("net", "1.0"),
|
||||||
|
Api::Eth => ("eth", "1.0"),
|
||||||
|
Api::Personal => ("personal", "1.0"),
|
||||||
|
Api::Ethcore => ("ethcore", "1.0"),
|
||||||
|
Api::Traces => ("traces", "1.0"),
|
||||||
|
Api::Rpc => ("rpc", "1.0"),
|
||||||
|
};
|
||||||
|
modules.insert(name.into(), version.into());
|
||||||
|
}
|
||||||
|
modules
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str(apis: Vec<&str>) -> Vec<Api> {
|
||||||
|
apis.into_iter()
|
||||||
|
.map(Api::from_str)
|
||||||
|
.collect::<Result<Vec<Api>, ApiError>>()
|
||||||
|
.unwrap_or_else(|e| match e {
|
||||||
|
ApiError::UnknownApi(s) => die!("Unknown RPC API specified: {}", s),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_apis(apis: ApiSet, signer_enabled: bool) -> Vec<Api> {
|
||||||
|
match apis {
|
||||||
|
ApiSet::List(apis) => apis,
|
||||||
|
ApiSet::UnsafeContext if signer_enabled => {
|
||||||
|
vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc]
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Ethcore, Api::Traces, Api::Rpc]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet) -> T {
|
||||||
|
use ethcore_rpc::v1::*;
|
||||||
|
|
||||||
|
let apis = list_apis(apis, deps.signer_enabled);
|
||||||
|
for api in &apis {
|
||||||
|
match *api {
|
||||||
|
Api::Web3 => {
|
||||||
|
server.add_delegate(Web3Client::new().to_delegate());
|
||||||
|
},
|
||||||
|
Api::Net => {
|
||||||
|
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
||||||
|
},
|
||||||
|
Api::Eth => {
|
||||||
|
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
||||||
|
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
||||||
|
|
||||||
|
if deps.signer_enabled {
|
||||||
|
server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue).to_delegate());
|
||||||
|
} else {
|
||||||
|
server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Api::Personal => {
|
||||||
|
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
|
||||||
|
if deps.signer_enabled {
|
||||||
|
server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_queue).to_delegate());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Api::Ethcore => {
|
||||||
|
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
|
||||||
|
},
|
||||||
|
Api::Traces => {
|
||||||
|
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
|
||||||
|
},
|
||||||
|
Api::Rpc => {
|
||||||
|
let modules = to_modules(&apis);
|
||||||
|
server.add_delegate(RpcClient::new(modules).to_delegate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server
|
||||||
|
}
|
@ -15,12 +15,9 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ethcore::client::Client;
|
|
||||||
use ethsync::EthSync;
|
|
||||||
use ethminer::{Miner, ExternalMiner};
|
|
||||||
use util::keys::store::AccountService;
|
|
||||||
use util::panics::{PanicHandler, ForwardPanic};
|
use util::panics::{PanicHandler, ForwardPanic};
|
||||||
use die::*;
|
use die::*;
|
||||||
|
use rpc_apis;
|
||||||
|
|
||||||
#[cfg(feature = "ethcore-signer")]
|
#[cfg(feature = "ethcore-signer")]
|
||||||
use ethcore_signer as signer;
|
use ethcore_signer as signer;
|
||||||
@ -36,11 +33,7 @@ pub struct Configuration {
|
|||||||
|
|
||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
pub panic_handler: Arc<PanicHandler>,
|
pub panic_handler: Arc<PanicHandler>,
|
||||||
pub client: Arc<Client>,
|
pub apis: Arc<rpc_apis::Dependencies>,
|
||||||
pub sync: Arc<EthSync>,
|
|
||||||
pub secret_store: Arc<AccountService>,
|
|
||||||
pub miner: Arc<Miner>,
|
|
||||||
pub external_miner: Arc<ExternalMiner>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(conf: Configuration, deps: Dependencies) -> Option<SignerServer> {
|
pub fn start(conf: Configuration, deps: Dependencies) -> Option<SignerServer> {
|
||||||
@ -58,13 +51,8 @@ fn do_start(conf: Configuration, deps: Dependencies) -> SignerServer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let start_result = {
|
let start_result = {
|
||||||
use ethcore_rpc::v1::*;
|
|
||||||
let server = signer::ServerBuilder::new();
|
let server = signer::ServerBuilder::new();
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
let server = rpc_apis::setup_rpc(server, deps.apis, rpc_apis::ApiSet::SafeContext);
|
||||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
|
||||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
|
||||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
|
||||||
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
|
|
||||||
server.start(addr)
|
server.start(addr)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,12 +44,26 @@ use self::jsonrpc_core::{IoHandler, IoDelegate};
|
|||||||
|
|
||||||
pub use jsonrpc_http_server::{Server, RpcServerError};
|
pub use jsonrpc_http_server::{Server, RpcServerError};
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
|
pub use v1::{SigningQueue, ConfirmationsQueue};
|
||||||
|
|
||||||
|
/// An object that can be extended with `IoDelegates`
|
||||||
|
pub trait Extendable {
|
||||||
|
/// Add `Delegate` to this object.
|
||||||
|
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>);
|
||||||
|
}
|
||||||
|
|
||||||
/// Http server.
|
/// Http server.
|
||||||
pub struct RpcServer {
|
pub struct RpcServer {
|
||||||
handler: Arc<jsonrpc_core::io::IoHandler>,
|
handler: Arc<jsonrpc_core::io::IoHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Extendable for RpcServer {
|
||||||
|
/// Add io delegate.
|
||||||
|
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||||
|
self.handler.add_delegate(delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RpcServer {
|
impl RpcServer {
|
||||||
/// Construct new http server object.
|
/// Construct new http server object.
|
||||||
pub fn new() -> RpcServer {
|
pub fn new() -> RpcServer {
|
||||||
@ -58,11 +72,6 @@ impl RpcServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add io delegate.
|
|
||||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
|
||||||
self.handler.add_delegate(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||||
pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> {
|
pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> {
|
||||||
let cors_domains = cors_domains.into_iter()
|
let cors_domains = cors_domains.into_iter()
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
mod poll_manager;
|
mod poll_manager;
|
||||||
mod poll_filter;
|
mod poll_filter;
|
||||||
|
mod signing_queue;
|
||||||
|
|
||||||
pub use self::poll_manager::PollManager;
|
pub use self::poll_manager::PollManager;
|
||||||
pub use self::poll_filter::PollFilter;
|
pub use self::poll_filter::PollFilter;
|
||||||
|
pub use self::signing_queue::{ConfirmationsQueue, SigningQueue};
|
||||||
|
108
rpc/src/v1/helpers/signing_queue.rs
Normal file
108
rpc/src/v1/helpers/signing_queue.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// 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::sync::Mutex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use v1::types::{TransactionRequest, TransactionConfirmation};
|
||||||
|
use util::U256;
|
||||||
|
|
||||||
|
/// A queue of transactions awaiting to be confirmed and signed.
|
||||||
|
pub trait SigningQueue: Send + Sync {
|
||||||
|
/// Add new request to the queue.
|
||||||
|
fn add_request(&self, transaction: TransactionRequest) -> U256;
|
||||||
|
|
||||||
|
/// Remove request from the queue.
|
||||||
|
fn remove_request(&self, id: U256) -> Option<TransactionConfirmation>;
|
||||||
|
|
||||||
|
/// Return copy of all the requests in the queue.
|
||||||
|
fn requests(&self) -> Vec<TransactionConfirmation>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queue for all unconfirmed transactions.
|
||||||
|
pub struct ConfirmationsQueue {
|
||||||
|
id: Mutex<U256>,
|
||||||
|
queue: Mutex<HashMap<U256, TransactionConfirmation>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConfirmationsQueue {
|
||||||
|
fn default() -> Self {
|
||||||
|
ConfirmationsQueue {
|
||||||
|
id: Mutex::new(U256::from(0)),
|
||||||
|
queue: Mutex::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningQueue for ConfirmationsQueue {
|
||||||
|
fn add_request(&self, transaction: TransactionRequest) -> U256 {
|
||||||
|
// Increment id
|
||||||
|
let id = {
|
||||||
|
let mut last_id = self.id.lock().unwrap();
|
||||||
|
*last_id = *last_id + U256::from(1);
|
||||||
|
*last_id
|
||||||
|
};
|
||||||
|
let mut queue = self.queue.lock().unwrap();
|
||||||
|
queue.insert(id, TransactionConfirmation {
|
||||||
|
id: id,
|
||||||
|
transaction: transaction,
|
||||||
|
});
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_request(&self, id: U256) -> Option<TransactionConfirmation> {
|
||||||
|
self.queue.lock().unwrap().remove(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requests(&self) -> Vec<TransactionConfirmation> {
|
||||||
|
let queue = self.queue.lock().unwrap();
|
||||||
|
queue.values().cloned().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use util::hash::Address;
|
||||||
|
use util::numbers::U256;
|
||||||
|
use v1::types::TransactionRequest;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_work_for_hashset() {
|
||||||
|
// given
|
||||||
|
let queue = ConfirmationsQueue::default();
|
||||||
|
|
||||||
|
let request = TransactionRequest {
|
||||||
|
from: Address::from(1),
|
||||||
|
to: Some(Address::from(2)),
|
||||||
|
gas_price: None,
|
||||||
|
gas: None,
|
||||||
|
value: Some(U256::from(10_000_000)),
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
queue.add_request(request.clone());
|
||||||
|
let all = queue.requests();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(all.len(), 1);
|
||||||
|
let el = all.get(0).unwrap();
|
||||||
|
assert_eq!(el.id, U256::from(1));
|
||||||
|
assert_eq!(el.transaction, request);
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
extern crate ethash;
|
extern crate ethash;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::sync::{Arc, Weak, Mutex};
|
use std::sync::{Arc, Weak, Mutex};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use ethsync::{SyncProvider, SyncState};
|
use ethsync::{SyncProvider, SyncState};
|
||||||
@ -36,10 +35,9 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act
|
|||||||
use ethcore::log_entry::LogEntry;
|
use ethcore::log_entry::LogEntry;
|
||||||
use ethcore::filter::Filter as EthcoreFilter;
|
use ethcore::filter::Filter as EthcoreFilter;
|
||||||
use self::ethash::SeedHashCompute;
|
use self::ethash::SeedHashCompute;
|
||||||
use v1::traits::{Eth, EthFilter};
|
use v1::traits::Eth;
|
||||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
||||||
use v1::helpers::{PollFilter, PollManager};
|
use v1::impls::dispatch_transaction;
|
||||||
use v1::impls::{dispatch_transaction, sign_and_dispatch};
|
|
||||||
use serde;
|
use serde;
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
@ -170,6 +168,25 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||||
|
let receipts = miner.pending_receipts();
|
||||||
|
|
||||||
|
let pending_logs = receipts.into_iter()
|
||||||
|
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
||||||
|
.collect::<Vec<(H256, LogEntry)>>();
|
||||||
|
|
||||||
|
let result = pending_logs.into_iter()
|
||||||
|
.filter(|pair| filter.matches(&pair.1))
|
||||||
|
.map(|pair| {
|
||||||
|
let mut log = Log::from(pair.1);
|
||||||
|
log.transaction_hash = Some(pair.0);
|
||||||
|
log
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
|
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
|
||||||
|
|
||||||
fn params_len(params: &Params) -> usize {
|
fn params_len(params: &Params) -> usize {
|
||||||
@ -193,25 +210,6 @@ fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNum
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
|
||||||
let receipts = miner.pending_receipts();
|
|
||||||
|
|
||||||
let pending_logs = receipts.into_iter()
|
|
||||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
|
||||||
.collect::<Vec<(H256, LogEntry)>>();
|
|
||||||
|
|
||||||
let result = pending_logs.into_iter()
|
|
||||||
.filter(|pair| filter.matches(&pair.1))
|
|
||||||
.map(|pair| {
|
|
||||||
let mut log = Log::from(pair.1);
|
|
||||||
log.transaction_hash = Some(pair.0);
|
|
||||||
log
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be in range [-32099, -32000]
|
// must be in range [-32099, -32000]
|
||||||
const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
|
const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
|
||||||
|
|
||||||
@ -496,23 +494,6 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
|
|
||||||
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
from_params::<(TransactionRequest, )>(params)
|
|
||||||
.and_then(|(request, )| {
|
|
||||||
let accounts = take_weak!(self.accounts);
|
|
||||||
match accounts.account_secret(&request.from) {
|
|
||||||
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
|
|
||||||
Err(_) => to_value(&H256::zero())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
|
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Bytes, )>(params)
|
from_params::<(Bytes, )>(params)
|
||||||
.and_then(|(raw_transaction, )| {
|
.and_then(|(raw_transaction, )| {
|
||||||
@ -563,186 +544,3 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
|||||||
rpc_unimplemented!()
|
rpc_unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
|
||||||
pub struct EthFilterClient<C, M> where
|
|
||||||
C: BlockChainClient,
|
|
||||||
M: MinerService {
|
|
||||||
|
|
||||||
client: Weak<C>,
|
|
||||||
miner: Weak<M>,
|
|
||||||
polls: Mutex<PollManager<PollFilter>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C, M> EthFilterClient<C, M> where
|
|
||||||
C: BlockChainClient,
|
|
||||||
M: MinerService {
|
|
||||||
|
|
||||||
/// Creates new Eth filter client.
|
|
||||||
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
|
||||||
EthFilterClient {
|
|
||||||
client: Arc::downgrade(client),
|
|
||||||
miner: Arc::downgrade(miner),
|
|
||||||
polls: Mutex::new(PollManager::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C, M> EthFilter for EthFilterClient<C, M> where
|
|
||||||
C: BlockChainClient + 'static,
|
|
||||||
M: MinerService + 'static {
|
|
||||||
|
|
||||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
from_params::<(Filter,)>(params)
|
|
||||||
.and_then(|(filter,)| {
|
|
||||||
let mut polls = self.polls.lock().unwrap();
|
|
||||||
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
|
||||||
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
|
|
||||||
to_value(&U256::from(id))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
match params {
|
|
||||||
Params::None => {
|
|
||||||
let mut polls = self.polls.lock().unwrap();
|
|
||||||
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
|
||||||
to_value(&U256::from(id))
|
|
||||||
},
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
match params {
|
|
||||||
Params::None => {
|
|
||||||
let mut polls = self.polls.lock().unwrap();
|
|
||||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
|
||||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
|
||||||
|
|
||||||
to_value(&U256::from(id))
|
|
||||||
},
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
let client = take_weak!(self.client);
|
|
||||||
from_params::<(Index,)>(params)
|
|
||||||
.and_then(|(index,)| {
|
|
||||||
let mut polls = self.polls.lock().unwrap();
|
|
||||||
match polls.poll_mut(&index.value()) {
|
|
||||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
|
||||||
Some(filter) => match *filter {
|
|
||||||
PollFilter::Block(ref mut 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 = (*block_number..current_number).into_iter()
|
|
||||||
.map(BlockID::Number)
|
|
||||||
.filter_map(|id| client.block_hash(id))
|
|
||||||
.collect::<Vec<H256>>();
|
|
||||||
|
|
||||||
*block_number = current_number;
|
|
||||||
|
|
||||||
to_value(&hashes)
|
|
||||||
},
|
|
||||||
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
|
||||||
// get hashes of pending transactions
|
|
||||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
|
||||||
|
|
||||||
let new_hashes =
|
|
||||||
{
|
|
||||||
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
|
|
||||||
|
|
||||||
// find all new hashes
|
|
||||||
current_hashes
|
|
||||||
.iter()
|
|
||||||
.filter(|hash| !previous_hashes_set.contains(hash))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<H256>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
// save all hashes of pending transactions
|
|
||||||
*previous_hashes = current_hashes;
|
|
||||||
|
|
||||||
// return new hashes
|
|
||||||
to_value(&new_hashes)
|
|
||||||
},
|
|
||||||
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
|
|
||||||
// retrive the current block number
|
|
||||||
let current_number = client.chain_info().best_block_number;
|
|
||||||
|
|
||||||
// check if we need to check pending hashes
|
|
||||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
|
||||||
|
|
||||||
// build appropriate filter
|
|
||||||
let mut filter: EthcoreFilter = filter.clone().into();
|
|
||||||
filter.from_block = BlockID::Number(*block_number);
|
|
||||||
filter.to_block = BlockID::Latest;
|
|
||||||
|
|
||||||
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
|
|
||||||
let mut logs = client.logs(filter.clone())
|
|
||||||
.into_iter()
|
|
||||||
.map(From::from)
|
|
||||||
.collect::<Vec<Log>>();
|
|
||||||
|
|
||||||
// additionally retrieve pending logs
|
|
||||||
if include_pending {
|
|
||||||
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
|
||||||
|
|
||||||
// remove logs about which client was already notified about
|
|
||||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
|
||||||
.filter(|p| !previous_logs.contains(p))
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// save all logs retrieved by client
|
|
||||||
*previous_logs = pending_logs.into_iter().collect();
|
|
||||||
|
|
||||||
// append logs array with new pending logs
|
|
||||||
logs.extend(new_pending_logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save current block number as next from block number
|
|
||||||
*block_number = current_number;
|
|
||||||
|
|
||||||
to_value(&logs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
from_params::<(Index,)>(params)
|
|
||||||
.and_then(|(index,)| {
|
|
||||||
let mut polls = self.polls.lock().unwrap();
|
|
||||||
match polls.poll(&index.value()) {
|
|
||||||
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
|
|
||||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
|
||||||
let filter: EthcoreFilter = filter.clone().into();
|
|
||||||
let mut logs = take_weak!(self.client).logs(filter.clone())
|
|
||||||
.into_iter()
|
|
||||||
.map(From::from)
|
|
||||||
.collect::<Vec<Log>>();
|
|
||||||
|
|
||||||
if include_pending {
|
|
||||||
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
to_value(&logs)
|
|
||||||
},
|
|
||||||
// just empty array
|
|
||||||
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
from_params::<(Index,)>(params)
|
|
||||||
.and_then(|(index,)| {
|
|
||||||
self.polls.lock().unwrap().remove_poll(&index.value());
|
|
||||||
to_value(&true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
214
rpc/src/v1/impls/eth_filter.rs
Normal file
214
rpc/src/v1/impls/eth_filter.rs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Eth Filter RPC implementation
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::{Arc, Weak, Mutex};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use util::numbers::*;
|
||||||
|
use ethminer::MinerService;
|
||||||
|
use ethcore::filter::Filter as EthcoreFilter;
|
||||||
|
use ethcore::client::{BlockChainClient, BlockID};
|
||||||
|
use v1::traits::EthFilter;
|
||||||
|
use v1::types::{BlockNumber, Index, Filter, Log};
|
||||||
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
|
use v1::impls::eth::pending_logs;
|
||||||
|
|
||||||
|
|
||||||
|
/// Eth filter rpc implementation.
|
||||||
|
pub struct EthFilterClient<C, M> where
|
||||||
|
C: BlockChainClient,
|
||||||
|
M: MinerService {
|
||||||
|
|
||||||
|
client: Weak<C>,
|
||||||
|
miner: Weak<M>,
|
||||||
|
polls: Mutex<PollManager<PollFilter>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, M> EthFilterClient<C, M> where
|
||||||
|
C: BlockChainClient,
|
||||||
|
M: MinerService {
|
||||||
|
|
||||||
|
/// Creates new Eth filter client.
|
||||||
|
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||||
|
EthFilterClient {
|
||||||
|
client: Arc::downgrade(client),
|
||||||
|
miner: Arc::downgrade(miner),
|
||||||
|
polls: Mutex::new(PollManager::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||||
|
C: BlockChainClient + 'static,
|
||||||
|
M: MinerService + 'static {
|
||||||
|
|
||||||
|
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Filter,)>(params)
|
||||||
|
.and_then(|(filter,)| {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
||||||
|
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
|
||||||
|
to_value(&U256::from(id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
||||||
|
to_value(&U256::from(id))
|
||||||
|
},
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||||
|
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||||
|
|
||||||
|
to_value(&U256::from(id))
|
||||||
|
},
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
let client = take_weak!(self.client);
|
||||||
|
from_params::<(Index,)>(params)
|
||||||
|
.and_then(|(index,)| {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
match polls.poll_mut(&index.value()) {
|
||||||
|
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
|
Some(filter) => match *filter {
|
||||||
|
PollFilter::Block(ref mut 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 = (*block_number..current_number).into_iter()
|
||||||
|
.map(BlockID::Number)
|
||||||
|
.filter_map(|id| client.block_hash(id))
|
||||||
|
.collect::<Vec<H256>>();
|
||||||
|
|
||||||
|
*block_number = current_number;
|
||||||
|
|
||||||
|
to_value(&hashes)
|
||||||
|
},
|
||||||
|
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||||
|
// get hashes of pending transactions
|
||||||
|
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||||
|
|
||||||
|
let new_hashes =
|
||||||
|
{
|
||||||
|
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
// find all new hashes
|
||||||
|
current_hashes
|
||||||
|
.iter()
|
||||||
|
.filter(|hash| !previous_hashes_set.contains(hash))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<H256>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
// save all hashes of pending transactions
|
||||||
|
*previous_hashes = current_hashes;
|
||||||
|
|
||||||
|
// return new hashes
|
||||||
|
to_value(&new_hashes)
|
||||||
|
},
|
||||||
|
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
|
||||||
|
// retrive the current block number
|
||||||
|
let current_number = client.chain_info().best_block_number;
|
||||||
|
|
||||||
|
// check if we need to check pending hashes
|
||||||
|
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||||
|
|
||||||
|
// build appropriate filter
|
||||||
|
let mut filter: EthcoreFilter = filter.clone().into();
|
||||||
|
filter.from_block = BlockID::Number(*block_number);
|
||||||
|
filter.to_block = BlockID::Latest;
|
||||||
|
|
||||||
|
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
|
||||||
|
let mut logs = client.logs(filter.clone())
|
||||||
|
.into_iter()
|
||||||
|
.map(From::from)
|
||||||
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
|
// additionally retrieve pending logs
|
||||||
|
if include_pending {
|
||||||
|
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
||||||
|
|
||||||
|
// remove logs about which client was already notified about
|
||||||
|
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||||
|
.filter(|p| !previous_logs.contains(p))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// save all logs retrieved by client
|
||||||
|
*previous_logs = pending_logs.into_iter().collect();
|
||||||
|
|
||||||
|
// append logs array with new pending logs
|
||||||
|
logs.extend(new_pending_logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save current block number as next from block number
|
||||||
|
*block_number = current_number;
|
||||||
|
|
||||||
|
to_value(&logs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Index,)>(params)
|
||||||
|
.and_then(|(index,)| {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
match polls.poll(&index.value()) {
|
||||||
|
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
|
||||||
|
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||||
|
let filter: EthcoreFilter = filter.clone().into();
|
||||||
|
let mut logs = take_weak!(self.client).logs(filter.clone())
|
||||||
|
.into_iter()
|
||||||
|
.map(From::from)
|
||||||
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
|
if include_pending {
|
||||||
|
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
to_value(&logs)
|
||||||
|
},
|
||||||
|
// just empty array
|
||||||
|
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Index,)>(params)
|
||||||
|
.and_then(|(index,)| {
|
||||||
|
self.polls.lock().unwrap().remove_poll(&index.value());
|
||||||
|
to_value(&true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
111
rpc/src/v1/impls/eth_signing.rs
Normal file
111
rpc/src/v1/impls/eth_signing.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Eth Signing RPC implementation.
|
||||||
|
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use ethminer::MinerService;
|
||||||
|
use ethcore::client::BlockChainClient;
|
||||||
|
use util::numbers::*;
|
||||||
|
use util::keys::store::AccountProvider;
|
||||||
|
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||||
|
use v1::traits::EthSigning;
|
||||||
|
use v1::types::TransactionRequest;
|
||||||
|
use v1::impls::sign_and_dispatch;
|
||||||
|
|
||||||
|
|
||||||
|
/// Implementation of functions that require signing when no trusted signer is used.
|
||||||
|
pub struct EthSigningQueueClient {
|
||||||
|
queue: Weak<ConfirmationsQueue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthSigningQueueClient {
|
||||||
|
/// Creates a new signing queue client given shared signing queue.
|
||||||
|
pub fn new(queue: &Arc<ConfirmationsQueue>) -> Self {
|
||||||
|
EthSigningQueueClient {
|
||||||
|
queue: Arc::downgrade(queue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthSigning for EthSigningQueueClient {
|
||||||
|
|
||||||
|
fn sign(&self, _params: Params) -> Result<Value, Error> {
|
||||||
|
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
|
||||||
|
rpc_unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(TransactionRequest, )>(params)
|
||||||
|
.and_then(|(request, )| {
|
||||||
|
let queue = take_weak!(self.queue);
|
||||||
|
queue.add_request(request);
|
||||||
|
// TODO [ToDr] Block and wait for confirmation?
|
||||||
|
to_value(&H256::zero())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of functions that require signing when no trusted signer is used.
|
||||||
|
pub struct EthSigningUnsafeClient<C, A, M> where
|
||||||
|
C: BlockChainClient,
|
||||||
|
A: AccountProvider,
|
||||||
|
M: MinerService {
|
||||||
|
client: Weak<C>,
|
||||||
|
accounts: Weak<A>,
|
||||||
|
miner: Weak<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, A, M> EthSigningUnsafeClient<C, A, M> where
|
||||||
|
C: BlockChainClient,
|
||||||
|
A: AccountProvider,
|
||||||
|
M: MinerService {
|
||||||
|
|
||||||
|
/// Creates new EthClient.
|
||||||
|
pub fn new(client: &Arc<C>, accounts: &Arc<A>, miner: &Arc<M>)
|
||||||
|
-> Self {
|
||||||
|
EthSigningUnsafeClient {
|
||||||
|
client: Arc::downgrade(client),
|
||||||
|
miner: Arc::downgrade(miner),
|
||||||
|
accounts: Arc::downgrade(accounts),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, A, M> EthSigning for EthSigningUnsafeClient<C, A, M> where
|
||||||
|
C: BlockChainClient + 'static,
|
||||||
|
A: AccountProvider + 'static,
|
||||||
|
M: MinerService + 'static {
|
||||||
|
|
||||||
|
fn sign(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
|
||||||
|
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(TransactionRequest, )>(params)
|
||||||
|
.and_then(|(request, )| {
|
||||||
|
let accounts = take_weak!(self.accounts);
|
||||||
|
match accounts.account_secret(&request.from) {
|
||||||
|
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
|
||||||
|
Err(_) => to_value(&H256::zero())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,16 +31,22 @@ macro_rules! rpc_unimplemented {
|
|||||||
|
|
||||||
mod web3;
|
mod web3;
|
||||||
mod eth;
|
mod eth;
|
||||||
|
mod eth_filter;
|
||||||
|
mod eth_signing;
|
||||||
mod net;
|
mod net;
|
||||||
mod personal;
|
mod personal;
|
||||||
|
mod personal_signer;
|
||||||
mod ethcore;
|
mod ethcore;
|
||||||
mod traces;
|
mod traces;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
|
||||||
pub use self::web3::Web3Client;
|
pub use self::web3::Web3Client;
|
||||||
pub use self::eth::{EthClient, EthFilterClient};
|
pub use self::eth::EthClient;
|
||||||
|
pub use self::eth_filter::EthFilterClient;
|
||||||
|
pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient};
|
||||||
pub use self::net::NetClient;
|
pub use self::net::NetClient;
|
||||||
pub use self::personal::PersonalClient;
|
pub use self::personal::PersonalClient;
|
||||||
|
pub use self::personal_signer::SignerClient;
|
||||||
pub use self::ethcore::EthcoreClient;
|
pub use self::ethcore::EthcoreClient;
|
||||||
pub use self::traces::TracesClient;
|
pub use self::traces::TracesClient;
|
||||||
pub use self::rpc::RpcClient;
|
pub use self::rpc::RpcClient;
|
||||||
@ -92,4 +98,4 @@ fn sign_and_dispatch<C, M>(client: &Weak<C>, miner: &Weak<M>, request: Transacti
|
|||||||
|
|
||||||
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
||||||
dispatch_transaction(&*client, &*miner, signed_transaction)
|
dispatch_transaction(&*client, &*miner, signed_transaction)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use jsonrpc_core::*;
|
|||||||
use v1::traits::Personal;
|
use v1::traits::Personal;
|
||||||
use v1::types::TransactionRequest;
|
use v1::types::TransactionRequest;
|
||||||
use v1::impls::sign_and_dispatch;
|
use v1::impls::sign_and_dispatch;
|
||||||
use util::keys::store::*;
|
use util::keys::store::AccountProvider;
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use ethcore::client::BlockChainClient;
|
use ethcore::client::BlockChainClient;
|
||||||
use ethminer::MinerService;
|
use ethminer::MinerService;
|
||||||
|
93
rpc/src/v1/impls/personal_signer.rs
Normal file
93
rpc/src/v1/impls/personal_signer.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Transactions Confirmations (personal) rpc implementation
|
||||||
|
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use v1::traits::PersonalSigner;
|
||||||
|
use v1::types::TransactionModification;
|
||||||
|
use v1::impls::sign_and_dispatch;
|
||||||
|
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||||
|
use util::keys::store::AccountProvider;
|
||||||
|
use util::numbers::*;
|
||||||
|
use ethcore::client::BlockChainClient;
|
||||||
|
use ethminer::MinerService;
|
||||||
|
|
||||||
|
/// Transactions confirmation (personal) rpc implementation.
|
||||||
|
pub struct SignerClient<A, C, M>
|
||||||
|
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||||
|
queue: Weak<ConfirmationsQueue>,
|
||||||
|
accounts: Weak<A>,
|
||||||
|
client: Weak<C>,
|
||||||
|
miner: Weak<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M>
|
||||||
|
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||||
|
|
||||||
|
/// Create new instance of signer client.
|
||||||
|
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>, queue: &Arc<ConfirmationsQueue>) -> Self {
|
||||||
|
SignerClient {
|
||||||
|
queue: Arc::downgrade(queue),
|
||||||
|
accounts: Arc::downgrade(store),
|
||||||
|
client: Arc::downgrade(client),
|
||||||
|
miner: Arc::downgrade(miner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: 'static, C: 'static, M: 'static> PersonalSigner for SignerClient<A, C, M>
|
||||||
|
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||||
|
|
||||||
|
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
||||||
|
let queue = take_weak!(self.queue);
|
||||||
|
to_value(&queue.requests())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(U256, TransactionModification, String)>(params).and_then(
|
||||||
|
|(id, modification, pass)| {
|
||||||
|
let accounts = take_weak!(self.accounts);
|
||||||
|
let queue = take_weak!(self.queue);
|
||||||
|
queue.remove_request(id)
|
||||||
|
.and_then(|confirmation| {
|
||||||
|
let mut request = confirmation.transaction;
|
||||||
|
// apply modification
|
||||||
|
if let Some(gas_price) = modification.gas_price {
|
||||||
|
request.gas_price = Some(gas_price);
|
||||||
|
}
|
||||||
|
match accounts.locked_account_secret(&request.from, &pass) {
|
||||||
|
Ok(secret) => Some(sign_and_dispatch(&self.client, &self.miner, request, secret)),
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| to_value(&H256::zero()))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(U256, )>(params).and_then(
|
||||||
|
|(id, )| {
|
||||||
|
let queue = take_weak!(self.queue);
|
||||||
|
let res = queue.remove_request(id);
|
||||||
|
to_value(&res.is_some())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,5 +25,6 @@ pub mod traits;
|
|||||||
pub mod tests;
|
pub mod tests;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc};
|
pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, Traces, Rpc};
|
||||||
pub use self::impls::*;
|
pub use self::impls::*;
|
||||||
|
pub use self::helpers::{SigningQueue, ConfirmationsQueue};
|
||||||
|
@ -35,8 +35,8 @@ use util::keys::{AccountProvider, TestAccount, TestAccountProvider};
|
|||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use ethjson::blockchain::BlockChain;
|
use ethjson::blockchain::BlockChain;
|
||||||
|
|
||||||
use v1::traits::eth::Eth;
|
use v1::traits::eth::{Eth, EthSigning};
|
||||||
use v1::impls::EthClient;
|
use v1::impls::{EthClient, EthSigningUnsafeClient};
|
||||||
use v1::tests::helpers::{TestSyncProvider, Config};
|
use v1::tests::helpers::{TestSyncProvider, Config};
|
||||||
|
|
||||||
fn account_provider() -> Arc<TestAccountProvider> {
|
fn account_provider() -> Arc<TestAccountProvider> {
|
||||||
@ -109,10 +109,15 @@ impl EthTester {
|
|||||||
&miner_service,
|
&miner_service,
|
||||||
&external_miner
|
&external_miner
|
||||||
);
|
);
|
||||||
|
let eth_sign = EthSigningUnsafeClient::new(
|
||||||
|
&client,
|
||||||
|
&account_provider,
|
||||||
|
&miner_service
|
||||||
|
);
|
||||||
|
|
||||||
let handler = IoHandler::new();
|
let handler = IoHandler::new();
|
||||||
let delegate = eth_client.to_delegate();
|
handler.add_delegate(eth_client.to_delegate());
|
||||||
handler.add_delegate(delegate);
|
handler.add_delegate(eth_sign.to_delegate());
|
||||||
|
|
||||||
EthTester {
|
EthTester {
|
||||||
_miner: miner_service,
|
_miner: miner_service,
|
||||||
@ -352,4 +357,4 @@ fn verify_transaction_counts(name: String, chain: BlockChain) {
|
|||||||
|
|
||||||
register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest");
|
register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest");
|
||||||
register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest");
|
register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest");
|
||||||
register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest");
|
register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest");
|
||||||
|
@ -27,7 +27,7 @@ use ethcore::receipt::LocalizedReceipt;
|
|||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
use ethminer::{ExternalMiner, MinerService};
|
use ethminer::{ExternalMiner, MinerService};
|
||||||
use ethsync::SyncState;
|
use ethsync::SyncState;
|
||||||
use v1::{Eth, EthClient};
|
use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
|
||||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||||
use rustc_serialize::hex::ToHex;
|
use rustc_serialize::hex::ToHex;
|
||||||
|
|
||||||
@ -72,8 +72,11 @@ impl Default for EthTester {
|
|||||||
let hashrates = Arc::new(RwLock::new(HashMap::new()));
|
let hashrates = Arc::new(RwLock::new(HashMap::new()));
|
||||||
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
||||||
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate();
|
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate();
|
||||||
|
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
|
||||||
let io = IoHandler::new();
|
let io = IoHandler::new();
|
||||||
io.add_delegate(eth);
|
io.add_delegate(eth);
|
||||||
|
io.add_delegate(sign);
|
||||||
|
|
||||||
EthTester {
|
EthTester {
|
||||||
client: client,
|
client: client,
|
||||||
sync: sync,
|
sync: sync,
|
||||||
|
75
rpc/src/v1/tests/mocked/eth_signing.rs
Normal file
75
rpc/src/v1/tests/mocked/eth_signing.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// 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::sync::Arc;
|
||||||
|
use jsonrpc_core::IoHandler;
|
||||||
|
use v1::impls::EthSigningQueueClient;
|
||||||
|
use v1::traits::EthSigning;
|
||||||
|
use v1::helpers::{ConfirmationsQueue, SigningQueue};
|
||||||
|
use util::keys::TestAccount;
|
||||||
|
|
||||||
|
struct EthSigningTester {
|
||||||
|
pub queue: Arc<ConfirmationsQueue>,
|
||||||
|
pub io: IoHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EthSigningTester {
|
||||||
|
fn default() -> Self {
|
||||||
|
let queue = Arc::new(ConfirmationsQueue::default());
|
||||||
|
let io = IoHandler::new();
|
||||||
|
io.add_delegate(EthSigningQueueClient::new(&queue).to_delegate());
|
||||||
|
|
||||||
|
EthSigningTester {
|
||||||
|
queue: queue,
|
||||||
|
io: io,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eth_signing() -> EthSigningTester {
|
||||||
|
EthSigningTester::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_add_transaction_to_queue() {
|
||||||
|
// given
|
||||||
|
let tester = eth_signing();
|
||||||
|
let account = TestAccount::new("123");
|
||||||
|
let address = account.address();
|
||||||
|
assert_eq!(tester.queue.requests().len(), 0);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_sendTransaction",
|
||||||
|
"params": [{
|
||||||
|
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||||
|
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||||
|
"gas": "0x76c0",
|
||||||
|
"gasPrice": "0x9184e72a000",
|
||||||
|
"value": "0x9184e72a"
|
||||||
|
}],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
|
||||||
|
}
|
@ -18,8 +18,10 @@
|
|||||||
//! method calls properly.
|
//! method calls properly.
|
||||||
|
|
||||||
mod eth;
|
mod eth;
|
||||||
|
mod eth_signing;
|
||||||
mod net;
|
mod net;
|
||||||
mod web3;
|
mod web3;
|
||||||
mod personal;
|
mod personal;
|
||||||
|
mod personal_signer;
|
||||||
mod ethcore;
|
mod ethcore;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
@ -176,4 +176,4 @@ fn sign_and_send_transaction() {
|
|||||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||||
|
|
||||||
assert_eq!(tester.io.handle_request(request.as_ref()), Some(response));
|
assert_eq!(tester.io.handle_request(request.as_ref()), Some(response));
|
||||||
}
|
}
|
||||||
|
169
rpc/src/v1/tests/mocked/personal_signer.rs
Normal file
169
rpc/src/v1/tests/mocked/personal_signer.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// 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::sync::Arc;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use jsonrpc_core::IoHandler;
|
||||||
|
use util::numbers::*;
|
||||||
|
use util::keys::{TestAccount, TestAccountProvider};
|
||||||
|
use ethcore::client::TestBlockChainClient;
|
||||||
|
use ethcore::transaction::{Transaction, Action};
|
||||||
|
use v1::{SignerClient, PersonalSigner};
|
||||||
|
use v1::tests::helpers::TestMinerService;
|
||||||
|
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||||
|
use v1::types::TransactionRequest;
|
||||||
|
|
||||||
|
|
||||||
|
struct PersonalSignerTester {
|
||||||
|
queue: Arc<ConfirmationsQueue>,
|
||||||
|
accounts: Arc<TestAccountProvider>,
|
||||||
|
io: IoHandler,
|
||||||
|
miner: Arc<TestMinerService>,
|
||||||
|
// these unused fields are necessary to keep the data alive
|
||||||
|
// as the handler has only weak pointers.
|
||||||
|
_client: Arc<TestBlockChainClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||||
|
let client = TestBlockChainClient::new();
|
||||||
|
Arc::new(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accounts_provider() -> Arc<TestAccountProvider> {
|
||||||
|
let accounts = HashMap::new();
|
||||||
|
let ap = TestAccountProvider::new(accounts);
|
||||||
|
Arc::new(ap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn miner_service() -> Arc<TestMinerService> {
|
||||||
|
Arc::new(TestMinerService::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signer_tester() -> PersonalSignerTester {
|
||||||
|
let queue = Arc::new(ConfirmationsQueue::default());
|
||||||
|
let accounts = accounts_provider();
|
||||||
|
let client = blockchain_client();
|
||||||
|
let miner = miner_service();
|
||||||
|
|
||||||
|
let io = IoHandler::new();
|
||||||
|
io.add_delegate(SignerClient::new(&accounts, &client, &miner, &queue).to_delegate());
|
||||||
|
|
||||||
|
PersonalSignerTester {
|
||||||
|
queue: queue,
|
||||||
|
accounts: accounts,
|
||||||
|
io: io,
|
||||||
|
miner: miner,
|
||||||
|
_client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_list_of_transactions_in_queue() {
|
||||||
|
// given
|
||||||
|
let tester = signer_tester();
|
||||||
|
tester.queue.add_request(TransactionRequest {
|
||||||
|
from: Address::from(1),
|
||||||
|
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||||
|
gas_price: Some(U256::from(10_000)),
|
||||||
|
gas: Some(U256::from(10_000_000)),
|
||||||
|
value: Some(U256::from(1)),
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_reject_transaction_from_queue_without_dispatching() {
|
||||||
|
// given
|
||||||
|
let tester = signer_tester();
|
||||||
|
tester.queue.add_request(TransactionRequest {
|
||||||
|
from: Address::from(1),
|
||||||
|
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||||
|
gas_price: Some(U256::from(10_000)),
|
||||||
|
gas: Some(U256::from(10_000_000)),
|
||||||
|
value: Some(U256::from(1)),
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
});
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 0);
|
||||||
|
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_confirm_transaction_and_dispatch() {
|
||||||
|
// given
|
||||||
|
let tester = signer_tester();
|
||||||
|
let account = TestAccount::new("test");
|
||||||
|
let address = account.address();
|
||||||
|
let secret = account.secret.clone();
|
||||||
|
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
|
||||||
|
tester.accounts.accounts
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(address, account);
|
||||||
|
tester.queue.add_request(TransactionRequest {
|
||||||
|
from: address,
|
||||||
|
to: Some(recipient),
|
||||||
|
gas_price: Some(U256::from(10_000)),
|
||||||
|
gas: Some(U256::from(10_000_000)),
|
||||||
|
value: Some(U256::from(1)),
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
});
|
||||||
|
let t = Transaction {
|
||||||
|
nonce: U256::zero(),
|
||||||
|
gas_price: U256::from(0x1000),
|
||||||
|
gas: U256::from(10_000_000),
|
||||||
|
action: Action::Call(recipient),
|
||||||
|
value: U256::from(0x1),
|
||||||
|
data: vec![]
|
||||||
|
}.sign(&secret);
|
||||||
|
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc":"2.0",
|
||||||
|
"method":"personal_confirmTransaction",
|
||||||
|
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
|
||||||
|
"id":1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 0);
|
||||||
|
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 1);
|
||||||
|
}
|
||||||
|
|
@ -74,12 +74,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
|||||||
/// Returns the code at given address at given time (block number).
|
/// Returns the code at given address at given time (block number).
|
||||||
fn code_at(&self, _: Params) -> Result<Value, Error>;
|
fn code_at(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Signs the data with given address signature.
|
|
||||||
fn sign(&self, _: Params) -> Result<Value, Error>;
|
|
||||||
|
|
||||||
/// Sends transaction.
|
|
||||||
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
|
||||||
|
|
||||||
/// Sends signed transaction.
|
/// Sends signed transaction.
|
||||||
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error>;
|
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
@ -150,8 +144,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
|||||||
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash);
|
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash);
|
||||||
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number);
|
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number);
|
||||||
delegate.add_method("eth_getCode", Eth::code_at);
|
delegate.add_method("eth_getCode", Eth::code_at);
|
||||||
delegate.add_method("eth_sign", Eth::sign);
|
|
||||||
delegate.add_method("eth_sendTransaction", Eth::send_transaction);
|
|
||||||
delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction);
|
delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction);
|
||||||
delegate.add_method("eth_call", Eth::call);
|
delegate.add_method("eth_call", Eth::call);
|
||||||
delegate.add_method("eth_estimateGas", Eth::estimate_gas);
|
delegate.add_method("eth_estimateGas", Eth::estimate_gas);
|
||||||
@ -208,3 +200,20 @@ pub trait EthFilter: Sized + Send + Sync + 'static {
|
|||||||
delegate
|
delegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signing methods implementation relying on unlocked accounts.
|
||||||
|
pub trait EthSigning: Sized + Send + Sync + 'static {
|
||||||
|
/// Signs the data with given address signature.
|
||||||
|
fn sign(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Sends transaction.
|
||||||
|
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("eth_sign", EthSigning::sign);
|
||||||
|
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,9 +25,11 @@ pub mod traces;
|
|||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
|
||||||
pub use self::web3::Web3;
|
pub use self::web3::Web3;
|
||||||
pub use self::eth::{Eth, EthFilter};
|
pub use self::eth::{Eth, EthFilter, EthSigning};
|
||||||
pub use self::net::Net;
|
pub use self::net::Net;
|
||||||
pub use self::personal::Personal;
|
pub use self::personal::{Personal, PersonalSigner};
|
||||||
pub use self::ethcore::Ethcore;
|
pub use self::ethcore::Ethcore;
|
||||||
pub use self::traces::Traces;
|
pub use self::traces::Traces;
|
||||||
pub use self::rpc::Rpc;
|
pub use self::rpc::Rpc;
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,3 +43,26 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
|||||||
delegate
|
delegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Personal extension for transactions confirmations rpc interface.
|
||||||
|
pub trait PersonalSigner: Sized + Send + Sync + 'static {
|
||||||
|
|
||||||
|
/// Returns a list of transactions to confirm.
|
||||||
|
fn transactions_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Confirm and send a specific transaction.
|
||||||
|
fn confirm_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Reject the transaction request.
|
||||||
|
fn reject_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm);
|
||||||
|
delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction);
|
||||||
|
delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ pub use self::log::Log;
|
|||||||
pub use self::optionals::OptionalValue;
|
pub use self::optionals::OptionalValue;
|
||||||
pub use self::sync::{SyncStatus, SyncInfo};
|
pub use self::sync::{SyncStatus, SyncInfo};
|
||||||
pub use self::transaction::Transaction;
|
pub use self::transaction::Transaction;
|
||||||
pub use self::transaction_request::TransactionRequest;
|
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
|
||||||
pub use self::call_request::CallRequest;
|
pub use self::call_request::CallRequest;
|
||||||
pub use self::receipt::Receipt;
|
pub use self::receipt::Receipt;
|
||||||
pub use self::trace::Trace;
|
pub use self::trace::Trace;
|
||||||
|
@ -21,7 +21,7 @@ use util::numbers::U256;
|
|||||||
use v1::types::bytes::Bytes;
|
use v1::types::bytes::Bytes;
|
||||||
|
|
||||||
/// Transaction request coming from RPC
|
/// Transaction request coming from RPC
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Deserialize)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
pub struct TransactionRequest {
|
pub struct TransactionRequest {
|
||||||
/// Sender
|
/// Sender
|
||||||
pub from: Address,
|
pub from: Address,
|
||||||
@ -40,6 +40,24 @@ pub struct TransactionRequest {
|
|||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transaction confirmation waiting in a queue
|
||||||
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)]
|
||||||
|
pub struct TransactionConfirmation {
|
||||||
|
/// Id of this confirmation
|
||||||
|
pub id: U256,
|
||||||
|
/// TransactionRequest
|
||||||
|
pub transaction: TransactionRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible modifications to the confirmed transaction sent by SystemUI
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct TransactionModification {
|
||||||
|
/// Modified gas price
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -135,5 +153,26 @@ mod tests {
|
|||||||
nonce: None,
|
nonce: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_deserialize_modification() {
|
||||||
|
// given
|
||||||
|
let s1 = r#"{
|
||||||
|
"gasPrice":"0x0ba43b7400"
|
||||||
|
}"#;
|
||||||
|
let s2 = r#"{}"#;
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
|
||||||
|
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1, TransactionModification {
|
||||||
|
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||||
|
});
|
||||||
|
assert_eq!(res2, TransactionModification {
|
||||||
|
gas_price: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,13 +9,8 @@ build = "build.rs"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rustc_version = "0.1"
|
rustc_version = "0.1"
|
||||||
serde_codegen = { version = "0.7.0", optional = true }
|
|
||||||
syntex = "^0.32.0"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "0.7.0"
|
|
||||||
serde_json = "0.7.0"
|
|
||||||
rustc-serialize = "0.3"
|
|
||||||
jsonrpc-core = "2.0"
|
jsonrpc-core = "2.0"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
env_logger = "0.3"
|
env_logger = "0.3"
|
||||||
@ -23,10 +18,7 @@ ws = "0.4.7"
|
|||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
|
|
||||||
serde_macros = { version = "0.7.0", optional = true }
|
|
||||||
clippy = { version = "0.0.69", optional = true}
|
clippy = { version = "0.0.69", optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serde_codegen"]
|
|
||||||
nightly = ["serde_macros"]
|
|
||||||
dev = ["clippy"]
|
dev = ["clippy"]
|
||||||
|
@ -19,34 +19,7 @@ extern crate rustc_version;
|
|||||||
use rustc_version::{version_meta, Channel};
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
serde::main();
|
|
||||||
if let Channel::Nightly = version_meta().channel {
|
if let Channel::Nightly = version_meta().channel {
|
||||||
println!("cargo:rustc-cfg=nightly");
|
println!("cargo:rustc-cfg=nightly");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "serde_macros"))]
|
|
||||||
mod serde {
|
|
||||||
extern crate syntex;
|
|
||||||
extern crate serde_codegen;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
|
|
||||||
let src = Path::new("src/types/mod.rs.in");
|
|
||||||
let dst = Path::new(&out_dir).join("mod.rs");
|
|
||||||
|
|
||||||
let mut registry = syntex::Registry::new();
|
|
||||||
|
|
||||||
serde_codegen::register(&mut registry);
|
|
||||||
registry.expand("", &src, &dst).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde_macros")]
|
|
||||||
mod serde {
|
|
||||||
pub fn main() {}
|
|
||||||
}
|
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
||||||
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
||||||
// Generated by serde
|
|
||||||
#![cfg_attr(all(nightly, feature="dev"), allow(redundant_closure_call))]
|
|
||||||
|
|
||||||
//! Signer module
|
//! Signer module
|
||||||
//!
|
//!
|
||||||
@ -45,18 +43,12 @@
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_json;
|
|
||||||
extern crate rustc_serialize;
|
|
||||||
|
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore_rpc as rpc;
|
extern crate ethcore_rpc as rpc;
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate ws;
|
extern crate ws;
|
||||||
|
|
||||||
mod signing_queue;
|
|
||||||
mod ws_server;
|
mod ws_server;
|
||||||
|
|
||||||
pub use ws_server::*;
|
pub use ws_server::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
|
||||||
// This file is part of Parity.
|
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use rpc::v1::types::TransactionRequest;
|
|
||||||
|
|
||||||
pub trait SigningQueue {
|
|
||||||
fn add_request(&mut self, transaction: TransactionRequest);
|
|
||||||
|
|
||||||
fn remove_request(&mut self, id: TransactionRequest);
|
|
||||||
|
|
||||||
fn requests(&self) -> &HashSet<TransactionRequest>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SigningQueue for HashSet<TransactionRequest> {
|
|
||||||
fn add_request(&mut self, transaction: TransactionRequest) {
|
|
||||||
self.insert(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_request(&mut self, id: TransactionRequest) {
|
|
||||||
self.remove(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn requests(&self) -> &HashSet<TransactionRequest> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use util::hash::Address;
|
|
||||||
use util::numbers::U256;
|
|
||||||
use rpc::v1::types::TransactionRequest;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_work_for_hashset() {
|
|
||||||
// given
|
|
||||||
let mut queue = HashSet::new();
|
|
||||||
|
|
||||||
let request = TransactionRequest {
|
|
||||||
from: Address::from(1),
|
|
||||||
to: Some(Address::from(2)),
|
|
||||||
gas_price: None,
|
|
||||||
gas: None,
|
|
||||||
value: Some(U256::from(10_000_000)),
|
|
||||||
data: None,
|
|
||||||
nonce: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// when
|
|
||||||
queue.add_request(request.clone());
|
|
||||||
let all = queue.requests();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(all.len(), 1);
|
|
||||||
assert!(all.contains(&request));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
|
||||||
// This file is part of Parity.
|
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Reusable types with JSON Serialization.
|
|
||||||
|
|
||||||
#[cfg(feature = "serde_macros")]
|
|
||||||
include!("mod.rs.in");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "serde_macros"))]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
|
@ -1,25 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
|
||||||
// This file is part of Parity.
|
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO [ToDr] Types are empty for now. But they are about to come.
|
|
@ -25,6 +25,7 @@ use std::sync::Arc;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use util::panics::{PanicHandler, OnPanicListener, MayPanic};
|
use util::panics::{PanicHandler, OnPanicListener, MayPanic};
|
||||||
use jsonrpc_core::{IoHandler, IoDelegate};
|
use jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
|
use rpc::Extendable;
|
||||||
|
|
||||||
mod session;
|
mod session;
|
||||||
|
|
||||||
@ -57,6 +58,12 @@ impl Default for ServerBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Extendable for ServerBuilder {
|
||||||
|
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||||
|
self.handler.add_delegate(delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ServerBuilder {
|
impl ServerBuilder {
|
||||||
/// Creates new `ServerBuilder`
|
/// Creates new `ServerBuilder`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -65,11 +72,6 @@ impl ServerBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds rpc delegate
|
|
||||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
|
||||||
self.handler.add_delegate(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Starts a new `WebSocket` server in separate thread.
|
/// Starts a new `WebSocket` server in separate thread.
|
||||||
/// Returns a `Server` handle which closes the server when droped.
|
/// Returns a `Server` handle which closes the server when droped.
|
||||||
pub fn start(self, addr: SocketAddr) -> Result<Server, ServerError> {
|
pub fn start(self, addr: SocketAddr) -> Result<Server, ServerError> {
|
||||||
|
Loading…
Reference in New Issue
Block a user