Silent running operating modes (#1477)

* Command=line options.

* Keep alive for the eth protocol.

* Wire up final pieces.

* No network when dark.

* Passive and dark mode work.

* Ensure all RPCs keep alive.

* Fix tests.

* Fix minor bug.

* Minor whitespace.

* Split out some of the sleep-state.

* Fix help text.
This commit is contained in:
Gav Wood 2016-07-05 17:50:46 +02:00 committed by GitHub
parent 62b9c1b14f
commit c26cfc1c5a
16 changed files with 326 additions and 24 deletions

View File

@ -17,7 +17,8 @@
//! Blockchain database client.
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::time::Instant;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
use util::*;
use util::panics::*;
use views::BlockView;
@ -38,7 +39,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile,
use client::{BlockID, TransactionID, UncleID, TraceId, Mode, ClientConfig, DatabaseCompactionProfile,
BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, TransactionImportError,
BlockImportError, TransactionImportResult};
use client::Error as ClientError;
@ -54,6 +55,7 @@ use evm::Factory as EvmFactory;
use miner::{Miner, MinerService, AccountDetails};
const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -83,9 +85,24 @@ impl ClientReport {
}
}
struct SleepState {
last_activity: Option<Instant>,
last_autosleep: Option<Instant>,
}
impl SleepState {
fn new(awake: bool) -> Self {
SleepState {
last_activity: match awake { false => None, true => Some(Instant::now()) },
last_autosleep: match awake { false => Some(Instant::now()), true => None },
}
}
}
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
pub struct Client {
mode: Mode,
chain: Arc<BlockChain>,
tracedb: Arc<TraceDB<BlockChain>>,
engine: Arc<Box<Engine>>,
@ -98,6 +115,8 @@ pub struct Client {
vm_factory: Arc<EvmFactory>,
trie_factory: TrieFactory,
miner: Arc<Miner>,
sleep_state: Mutex<SleepState>,
liveness: AtomicBool,
io_channel: IoChannel<NetSyncMessage>,
queue_transactions: AtomicUsize,
}
@ -134,9 +153,8 @@ impl Client {
spec: Spec,
path: &Path,
miner: Arc<Miner>,
message_channel: IoChannel<NetSyncMessage>)
-> Result<Arc<Client>, ClientError>
{
message_channel: IoChannel<NetSyncMessage>
) -> Result<Arc<Client>, ClientError> {
let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
@ -167,7 +185,11 @@ impl Client {
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
let client = Client {
sleep_state: Mutex::new(SleepState::new(awake)),
liveness: AtomicBool::new(awake),
mode: config.mode,
chain: chain,
tracedb: tracedb,
engine: engine,
@ -183,7 +205,6 @@ impl Client {
io_channel: message_channel,
queue_transactions: AtomicUsize::new(0),
};
Ok(Arc::new(client))
}
@ -449,9 +470,41 @@ impl Client {
}
/// Tick the client.
// TODO: manage by real events.
pub fn tick(&self) {
self.chain.collect_garbage();
self.block_queue.collect_garbage();
match self.mode {
Mode::Dark(timeout) => {
let mut ss = self.sleep_state.lock().unwrap();
if let Some(t) = ss.last_activity {
if Instant::now() > t + timeout {
self.sleep();
ss.last_activity = None;
}
}
}
Mode::Passive(timeout, wakeup_after) => {
let mut ss = self.sleep_state.lock().unwrap();
let now = Instant::now();
if let Some(t) = ss.last_activity {
if now > t + timeout {
self.sleep();
ss.last_activity = None;
ss.last_autosleep = Some(now);
}
}
if let Some(t) = ss.last_autosleep {
if now > t + wakeup_after {
self.wake_up();
ss.last_activity = Some(now);
ss.last_autosleep = None;
}
}
}
_ => {}
}
}
/// Set up the cache behaviour.
@ -487,6 +540,29 @@ impl Client {
})
}
}
fn wake_up(&self) {
if !self.liveness.load(AtomicOrdering::Relaxed) {
self.liveness.store(true, AtomicOrdering::Relaxed);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::StartNetwork)).unwrap();
trace!(target: "mode", "wake_up: Waking.");
}
}
fn sleep(&self) {
if self.liveness.load(AtomicOrdering::Relaxed) {
// only sleep if the import queue is mostly empty.
if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON {
self.liveness.store(false, AtomicOrdering::Relaxed);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::StopNetwork)).unwrap();
trace!(target: "mode", "sleep: Sleeping.");
} else {
trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
// TODO: Consider uncommenting.
//*self.last_activity.lock().unwrap() = Some(Instant::now());
}
}
}
}
impl BlockChainClient for Client {
@ -528,6 +604,12 @@ impl BlockChainClient for Client {
ret
}
fn keep_alive(&self) {
if self.mode != Mode::Active {
self.wake_up();
(*self.sleep_state.lock().unwrap()).last_activity = Some(Instant::now());
}
}
fn block_header(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub use std::time::Duration;
pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch};
@ -35,6 +36,23 @@ impl Default for DatabaseCompactionProfile {
fn default() -> Self { DatabaseCompactionProfile::Default }
}
/// Operating mode for the client.
#[derive(Debug, Eq, PartialEq)]
pub enum Mode {
/// Always on.
Active,
/// Goes offline after RLP is inactive for some (given) time, but
/// comes back online after a while of inactivity.
Passive(Duration, Duration),
/// Goes offline after RLP is inactive for some (given) time and
/// stays inactive.
Dark(Duration),
}
impl Default for Mode {
fn default() -> Self { Mode::Active }
}
/// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, Default)]
pub struct ClientConfig {
@ -56,6 +74,8 @@ pub struct ClientConfig {
pub db_cache_size: Option<usize>,
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Operating mode
pub mode: Mode,
/// Type of block verifier used by client.
pub verifier_type: VerifierType,
}

View File

@ -23,7 +23,7 @@ mod test_client;
mod trace;
pub use self::client::*;
pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::error::Error;
pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
@ -63,6 +63,11 @@ pub struct CallAnalytics {
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Should be called by any external-facing interface when actively using the client.
/// To minimise chatter, there's no need to call more than once every 30s.
fn keep_alive(&self) {}
/// Get raw block header data by block id.
fn block_header(&self, id: BlockID) -> Option<Bytes>;

View File

@ -32,7 +32,19 @@ Usage:
parity [options]
parity ui [options]
Protocol Options:
Operating Options:
--mode MODE Set the operating mode. MODE can be one of:
active - Parity continuously syncs the chain.
passive - Parity syncs initially, then sleeps and
wakes regularly to resync.
dark - Parity syncs only when an external interface
is active. [default: active].
--mode-timeout SECS Specify the number of seconds before inactivity
timeout occurs when mode is dark or passive
[default: 300].
--mode-alarm SECS Specify the number of seconds before auto sleep
reawake timeout occurs when mode is passive
[default: 3600].
--chain CHAIN Specify the blockchain type. CHAIN may be either a
JSON chain specification file or olympic, frontier,
homestead, mainnet, morden, or testnet
@ -269,6 +281,9 @@ pub struct Args {
pub arg_pid_file: String,
pub arg_file: Option<String>,
pub arg_path: Vec<String>,
pub flag_mode: String,
pub flag_mode_timeout: u64,
pub flag_mode_alarm: u64,
pub flag_chain: String,
pub flag_db_path: String,
pub flag_identity: String,

View File

@ -28,7 +28,7 @@ use util::*;
use util::log::Colour::*;
use ethcore::account_provider::AccountProvider;
use util::network_settings::NetworkSettings;
use ethcore::client::{append_path, get_db_path, ClientConfig, DatabaseCompactionProfile, Switch, VMType};
use ethcore::client::{append_path, get_db_path, Mode, ClientConfig, DatabaseCompactionProfile, Switch, VMType};
use ethcore::miner::{MinerOptions, PendingSet};
use ethcore::ethereum;
use ethcore::spec::Spec;
@ -61,6 +61,15 @@ impl Configuration {
}
}
pub fn mode(&self) -> Mode {
match &(self.args.flag_mode[..]) {
"active" => Mode::Active,
"passive" => Mode::Passive(Duration::from_secs(self.args.flag_mode_timeout), Duration::from_secs(self.args.flag_mode_alarm)),
"dark" => Mode::Dark(Duration::from_secs(self.args.flag_mode_timeout)),
_ => die!("{}: Invalid address for --mode. Must be one of active, passive or dark.", self.args.flag_mode),
}
}
fn net_port(&self) -> u16 {
self.args.flag_port
}
@ -302,6 +311,8 @@ impl Configuration {
pub fn client_config(&self, spec: &Spec) -> ClientConfig {
let mut client_config = ClientConfig::default();
client_config.mode = self.mode();
match self.args.flag_cache {
Some(mb) => {
client_config.blockchain.max_cache_size = mb * 1024 * 1024;

View File

@ -82,7 +82,7 @@ use rustc_serialize::hex::FromHex;
use ctrlc::CtrlC;
use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version};
use util::panics::{MayPanic, ForwardPanic, PanicHandler};
use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError};
use ethcore::client::{Mode, BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError};
use ethcore::error::{ImportError};
use ethcore::service::ClientService;
use ethcore::spec::Spec;
@ -213,7 +213,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
// Build client
let mut service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path()), miner.clone(), !conf.args.flag_no_network
client_config,
spec,
net_settings,
Path::new(&conf.path()),
miner.clone(),
match conf.mode() { Mode::Dark(..) => false, _ => !conf.args.flag_no_network }
).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service);
@ -282,7 +287,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
});
// Register IO handler
let io_handler = Arc::new(ClientIoHandler {
let io_handler = Arc::new(ClientIoHandler {
client: service.client(),
info: Informant::new(conf.have_color()),
sync: sync.clone(),

View File

@ -166,7 +166,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone(), queue).to_delegate())
},
Api::EthcoreSet => {
server.add_delegate(EthcoreSetClient::new(&deps.miner, &deps.net_service).to_delegate())
server.add_delegate(EthcoreSetClient::new(&deps.client, &deps.miner, &deps.net_service).to_delegate())
},
Api::Traces => {
server.add_delegate(TracesClient::new(&deps.client, &deps.miner).to_delegate())

View File

@ -241,6 +241,19 @@ fn no_author_err() -> Error {
}
}
impl<C, S, M, EM> EthClient<C, S, M, EM> where
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
M: MinerService + 'static,
EM: ExternalMinerService + 'static {
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
@ -248,6 +261,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
EM: ExternalMinerService + 'static {
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())),
_ => Err(Error::invalid_params())
@ -255,6 +269,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn syncing(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let status = take_weak!(self.sync).status();
@ -281,6 +296,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn author(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&take_weak!(self.miner).author()),
_ => Err(Error::invalid_params()),
@ -288,6 +304,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn is_mining(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&self.external_miner.is_mining()),
_ => Err(Error::invalid_params())
@ -295,6 +312,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn hashrate(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&self.external_miner.hashrate()),
_ => Err(Error::invalid_params())
@ -302,6 +320,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn gas_price(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
@ -312,11 +331,13 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn accounts(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
let store = take_weak!(self.accounts);
to_value(&store.accounts())
}
fn block_number(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&U256::from(take_weak!(self.client).chain_info().best_block_number)),
_ => Err(Error::invalid_params())
@ -324,6 +345,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn balance(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params_default_second(params)
.and_then(|(address, block_number,)| match block_number {
BlockNumber::Pending => to_value(&take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address)),
@ -332,6 +354,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn storage_at(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params_default_third::<Address, U256>(params)
.and_then(|(address, position, block_number,)| match block_number {
BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))),
@ -343,6 +366,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn transaction_count(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params_default_second(params)
.and_then(|(address, block_number,)| match block_number {
BlockNumber::Pending => to_value(&take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address)),
@ -351,6 +375,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256,)>(params)
.and_then(|(hash,)| // match
take_weak!(self.client).block(BlockID::Hash(hash))
@ -358,6 +383,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn block_transaction_count_by_number(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(BlockNumber,)>(params)
.and_then(|(block_number,)| match block_number {
BlockNumber::Pending => to_value(
@ -369,6 +395,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn block_uncles_count_by_hash(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256,)>(params)
.and_then(|(hash,)|
take_weak!(self.client).block(BlockID::Hash(hash))
@ -376,6 +403,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn block_uncles_count_by_number(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(BlockNumber,)>(params)
.and_then(|(block_number,)| match block_number {
BlockNumber::Pending => to_value(&U256::from(0)),
@ -385,6 +413,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn code_at(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params_default_second(params)
.and_then(|(address, block_number,)| match block_number {
BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)),
@ -394,16 +423,19 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn block_by_hash(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256, bool)>(params)
.and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash), include_txs))
}
fn block_by_number(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(BlockNumber, bool)>(params)
.and_then(|(number, include_txs)| self.block(number.into(), include_txs))
}
fn transaction_by_hash(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256,)>(params)
.and_then(|(hash,)| {
let miner = take_weak!(self.miner);
@ -415,16 +447,19 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn transaction_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256, Index)>(params)
.and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash), index.value())))
}
fn transaction_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(BlockNumber, Index)>(params)
.and_then(|(number, index)| self.transaction(TransactionID::Location(number.into(), index.value())))
}
fn transaction_receipt(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256,)>(params)
.and_then(|(hash,)| {
let miner = take_weak!(self.miner);
@ -440,16 +475,19 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn uncle_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256, Index)>(params)
.and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash), position: index.value() }))
}
fn uncle_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(BlockNumber, Index)>(params)
.and_then(|(number, index)| self.uncle(UncleID { block: number.into(), position: index.value() }))
}
fn compilers(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&vec![] as &Vec<String>),
_ => Err(Error::invalid_params())
@ -457,6 +495,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn logs(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Filter,)>(params)
.and_then(|(filter,)| {
let include_pending = filter.to_block == Some(BlockNumber::Pending);
@ -476,6 +515,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn work(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let client = take_weak!(self.client);
@ -512,6 +552,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn submit_work(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| {
trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
let miner = take_weak!(self.miner);
@ -523,6 +564,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn submit_hashrate(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256, H256)>(params).and_then(|(rate, id)| {
self.external_miner.submit_hashrate(rate, id);
to_value(&true)
@ -530,6 +572,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Bytes, )>(params)
.and_then(|(raw_transaction, )| {
let raw_transaction = raw_transaction.to_vec();
@ -541,6 +584,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn call(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
trace!(target: "jsonrpc", "call: {:?}", params);
from_params_default_second(params)
.and_then(|(request, block_number,)| {
@ -555,6 +599,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn estimate_gas(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params_default_second(params)
.and_then(|(request, block_number,)| {
let signed = try!(self.sign_call(request));
@ -568,14 +613,17 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
}
fn compile_lll(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
rpc_unimplemented!()
}
fn compile_serpent(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
rpc_unimplemented!()
}
fn compile_solidity(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
rpc_unimplemented!()
}
}

View File

@ -52,6 +52,12 @@ impl<C, M> EthFilterClient<C, M> where
polls: Mutex::new(PollManager::new()),
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C, M> EthFilter for EthFilterClient<C, M> where
@ -59,6 +65,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
M: MinerService + 'static {
fn new_filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Filter,)>(params)
.and_then(|(filter,)| {
let mut polls = self.polls.lock().unwrap();
@ -69,6 +76,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
}
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
@ -80,6 +88,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
}
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
@ -93,6 +102,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
}
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
let client = take_weak!(self.client);
from_params::<(Index,)>(params)
.and_then(|(index,)| {
@ -181,6 +191,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
}
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Index,)>(params)
.and_then(|(index,)| {
let mut polls = self.polls.lock().unwrap();
@ -206,6 +217,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
}
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Index,)>(params)
.and_then(|(index,)| {
self.polls.lock().unwrap().remove_poll(&index.value());

View File

@ -61,6 +61,12 @@ impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: Miner
miner: Arc::downgrade(miner),
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C, M> EthSigning for EthSigningQueueClient<C, M>
@ -68,12 +74,14 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
{
fn sign(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
warn!("Invoking eth_sign is not yet supported with signer enabled.");
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
rpc_unimplemented!()
}
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(TransactionRequest, )>(params)
.and_then(|(mut request, )| {
let accounts = take_weak!(self.accounts);
@ -118,6 +126,12 @@ impl<C, M> EthSigningUnsafeClient<C, M> where
accounts: Arc::downgrade(accounts),
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
@ -125,12 +139,14 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
M: MinerService + 'static {
fn sign(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
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> {
try!(self.active());
from_params::<(TransactionRequest, )>(params)
.and_then(|(request, )| {
let sender = request.from;

View File

@ -52,56 +52,74 @@ impl<C, M> EthcoreClient<C, M> where C: MiningBlockChainClient, M: MinerService
confirmations_queue: queue,
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: MiningBlockChainClient + 'static {
fn transactions_limit(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&take_weak!(self.miner).transactions_limit())
}
fn min_gas_price(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&take_weak!(self.miner).minimal_gas_price())
}
fn extra_data(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&Bytes::new(take_weak!(self.miner).extra_data()))
}
fn gas_floor_target(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&take_weak!(self.miner).gas_floor_target())
}
fn gas_ceil_target(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&take_weak!(self.miner).gas_ceil_target())
}
fn dev_logs(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
let logs = self.logger.logs();
to_value(&logs.deref().as_slice())
}
fn dev_logs_levels(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&self.logger.levels())
}
fn net_chain(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&self.settings.chain)
}
fn net_max_peers(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&self.settings.max_peers)
}
fn net_port(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&self.settings.network_port)
}
fn node_name(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
to_value(&self.settings.name)
}
fn rpc_settings(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
let mut map = BTreeMap::new();
map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled));
map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone()));
@ -110,6 +128,7 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
}
fn default_extra_data(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&Bytes::new(version_data())),
_ => Err(Error::invalid_params()),
@ -117,6 +136,7 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
}
fn gas_price_statistics(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) {
Ok(stats) => to_value(&stats
@ -130,6 +150,7 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
}
fn unsigned_transactions_count(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
match self.confirmations_queue {
None => Err(Error {
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),

View File

@ -20,31 +20,46 @@ use util::network::{NetworkService, NonReservedPeerMode};
use std::sync::{Arc, Weak};
use jsonrpc_core::*;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethcore::service::SyncMessage;
use v1::traits::EthcoreSet;
use v1::types::Bytes;
/// Ethcore-specific rpc interface for operations altering the settings.
pub struct EthcoreSetClient<M> where
pub struct EthcoreSetClient<C, M> where
C: MiningBlockChainClient,
M: MinerService {
client: Weak<C>,
miner: Weak<M>,
net: Weak<NetworkService<SyncMessage>>,
}
impl<M> EthcoreSetClient<M> where M: MinerService {
impl<C, M> EthcoreSetClient<C, M> where
C: MiningBlockChainClient,
M: MinerService {
/// Creates new `EthcoreSetClient`.
pub fn new(miner: &Arc<M>, net: &Arc<NetworkService<SyncMessage>>) -> Self {
pub fn new(client: &Arc<C>, miner: &Arc<M>, net: &Arc<NetworkService<SyncMessage>>) -> Self {
EthcoreSetClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
net: Arc::downgrade(net),
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
impl<C, M> EthcoreSet for EthcoreSetClient<C, M> where
C: MiningBlockChainClient + 'static,
M: MinerService + 'static {
fn set_min_gas_price(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256,)>(params).and_then(|(gas_price,)| {
take_weak!(self.miner).set_minimal_gas_price(gas_price);
to_value(&true)
@ -52,6 +67,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn set_gas_floor_target(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256,)>(params).and_then(|(target,)| {
take_weak!(self.miner).set_gas_floor_target(target);
to_value(&true)
@ -59,6 +75,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn set_gas_ceil_target(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256,)>(params).and_then(|(target,)| {
take_weak!(self.miner).set_gas_ceil_target(target);
to_value(&true)
@ -66,6 +83,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn set_extra_data(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Bytes,)>(params).and_then(|(extra_data,)| {
take_weak!(self.miner).set_extra_data(extra_data.to_vec());
to_value(&true)
@ -73,6 +91,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn set_author(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Address,)>(params).and_then(|(author,)| {
take_weak!(self.miner).set_author(author);
to_value(&true)
@ -80,6 +99,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn set_transactions_limit(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(usize,)>(params).and_then(|(limit,)| {
take_weak!(self.miner).set_transactions_limit(limit);
to_value(&true)
@ -87,6 +107,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn set_tx_gas_limit(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256,)>(params).and_then(|(limit,)| {
take_weak!(self.miner).set_tx_gas_limit(limit.into());
to_value(&true)
@ -94,6 +115,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn add_reserved_peer(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(String,)>(params).and_then(|(peer,)| {
match take_weak!(self.net).add_reserved_peer(&peer) {
Ok(()) => to_value(&true),
@ -103,6 +125,7 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn remove_reserved_peer(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(String,)>(params).and_then(|(peer,)| {
match take_weak!(self.net).remove_reserved_peer(&peer) {
Ok(()) => to_value(&true),
@ -112,11 +135,13 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}
fn drop_non_reserved_peers(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
take_weak!(self.net).set_non_reserved_mode(NonReservedPeerMode::Deny);
to_value(&true)
}
fn accept_non_reserved_peers(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
take_weak!(self.net).set_non_reserved_mode(NonReservedPeerMode::Accept);
to_value(&true)
}

View File

@ -43,22 +43,31 @@ impl<C, M> PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService
signer_port: signer_port,
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService {
fn signer_enabled(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
self.signer_port
.map(|v| to_value(&v))
.unwrap_or_else(|| to_value(&false))
}
fn accounts(&self, _: Params) -> Result<Value, Error> {
try!(self.active());
let store = take_weak!(self.accounts);
to_value(&store.accounts())
}
fn new_account(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(String, )>(params).and_then(
|(pass, )| {
let store = take_weak!(self.accounts);
@ -71,6 +80,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
}
fn unlock_account(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(Address, String, u64)>(params).and_then(
|(account, account_pass, _)|{
let store = take_weak!(self.accounts);
@ -82,6 +92,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
}
fn sign_and_send_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(TransactionRequest, String)>(params)
.and_then(|(request, password)| {
let sender = request.from;

View File

@ -46,16 +46,24 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
miner: Arc::downgrade(miner),
}
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
try!(self.active());
let queue = take_weak!(self.queue);
to_value(&queue.requests())
}
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256, TransactionModification, String)>(params).and_then(
|(id, modification, pass)| {
let accounts = take_weak!(self.accounts);
@ -87,6 +95,7 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
}
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(U256, )>(params).and_then(
|(id, )| {
let queue = take_weak!(self.queue);

View File

@ -55,10 +55,17 @@ impl<C, M> TracesClient<C, M> where C: BlockChainClient, M: MinerService {
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
}.fake_sign(from))
}
fn active(&self) -> Result<(), Error> {
// TODO: only call every 30s at most.
take_weak!(self.client).keep_alive();
Ok(())
}
}
impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M: MinerService + 'static {
fn filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(TraceFilter,)>(params)
.and_then(|(filter, )| {
let client = take_weak!(self.client);
@ -69,6 +76,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
}
fn block_traces(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(BlockNumber,)>(params)
.and_then(|(block_number,)| {
let client = take_weak!(self.client);
@ -79,6 +87,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
}
fn transaction_traces(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256,)>(params)
.and_then(|(transaction_hash,)| {
let client = take_weak!(self.client);
@ -89,6 +98,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
}
fn trace(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
from_params::<(H256, Vec<Index>)>(params)
.and_then(|(transaction_hash, address)| {
let client = take_weak!(self.client);
@ -103,6 +113,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
}
fn call(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
trace!(target: "jsonrpc", "call: {:?}", params);
from_params(params)
.and_then(|(request, flags)| {

View File

@ -20,6 +20,7 @@ use jsonrpc_core::IoHandler;
use v1::{EthcoreSet, EthcoreSetClient};
use ethcore::miner::MinerService;
use ethcore::service::SyncMessage;
use ethcore::client::TestBlockChainClient;
use v1::tests::helpers::TestMinerService;
use util::numbers::*;
use util::network::{NetworkConfiguration, NetworkService};
@ -29,20 +30,25 @@ fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
fn client_service() -> Arc<TestBlockChainClient> {
Arc::new(TestBlockChainClient::default())
}
fn network_service() -> Arc<NetworkService<SyncMessage>> {
Arc::new(NetworkService::new(NetworkConfiguration::new()).unwrap())
}
fn ethcore_set_client(miner: &Arc<TestMinerService>, net: &Arc<NetworkService<SyncMessage>>) -> EthcoreSetClient<TestMinerService> {
EthcoreSetClient::new(miner, net)
fn ethcore_set_client(client: &Arc<TestBlockChainClient>, miner: &Arc<TestMinerService>, net: &Arc<NetworkService<SyncMessage>>) -> EthcoreSetClient<TestBlockChainClient, TestMinerService> {
EthcoreSetClient::new(client, miner, net)
}
#[test]
fn rpc_ethcore_set_min_gas_price() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let io = IoHandler::new();
io.add_delegate(ethcore_set_client(&miner, &network).to_delegate());
io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
@ -50,12 +56,14 @@ fn rpc_ethcore_set_min_gas_price() {
assert_eq!(io.handle_request(request), Some(response.to_owned()));
assert_eq!(miner.minimal_gas_price(), U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
}
#[test]
fn rpc_ethcore_set_gas_floor_target() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let io = IoHandler::new();
io.add_delegate(ethcore_set_client(&miner, &network).to_delegate());
io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
@ -67,9 +75,10 @@ fn rpc_ethcore_set_gas_floor_target() {
#[test]
fn rpc_ethcore_set_extra_data() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let io = IoHandler::new();
io.add_delegate(ethcore_set_client(&miner, &network).to_delegate());
io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
@ -81,9 +90,10 @@ fn rpc_ethcore_set_extra_data() {
#[test]
fn rpc_ethcore_set_author() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let io = IoHandler::new();
io.add_delegate(ethcore_set_client(&miner, &network).to_delegate());
io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
@ -95,9 +105,10 @@ fn rpc_ethcore_set_author() {
#[test]
fn rpc_ethcore_set_transactions_limit() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let io = IoHandler::new();
io.add_delegate(ethcore_set_client(&miner, &network).to_delegate());
io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setTransactionsLimit", "params":[10240240], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;