diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs new file mode 100644 index 0000000000..68c79819e5 --- /dev/null +++ b/polkadot/api/src/full.rs @@ -0,0 +1,352 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Strongly typed API for full Polkadot client. + +use client::backend::{Backend, LocalBackend}; +use client::{self, Client, LocalCallExecutor}; +use polkadot_executor::Executor as LocalDispatch; +use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; +use state_machine::{self, OverlayedChanges}; + +use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp}; +use primitives::parachain::{DutyRoster, CandidateReceipt, Id as ParaId}; +use runtime::{self, Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall, ParachainsCall}; + +use {CheckedBlockId, BlockBuilder, PolkadotApi, LocalPolkadotApi, ErrorKind, Error, Result}; + +/// A checked block ID used for the substrate-client implementation of CheckedBlockId; +#[derive(Debug, Clone, Copy)] +pub struct CheckedId(pub BlockId); + +impl CheckedBlockId for CheckedId { + fn block_id(&self) -> &BlockId { + &self.0 + } +} + +// set up the necessary scaffolding to execute a set of calls to the runtime. +// this creates a new block on top of the given ID and initialises it. +macro_rules! with_runtime { + ($client: ident, $at: expr, $exec: expr) => {{ + let parent = $at.block_id(); + let header = Header { + parent_hash: $client.block_hash_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))?, + number: $client.block_number_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))? + 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + }; + + $client.state_at(parent).map_err(Error::from).and_then(|state| { + let mut changes = Default::default(); + let mut ext = state_machine::Ext::new(&mut changes, &state); + + ::substrate_executor::with_native_environment(&mut ext, || { + ::runtime::Executive::initialise_block(&header); + ($exec)() + }).map_err(Into::into) + }) + }} +} + +/// A polkadot block builder. +#[derive(Debug, Clone)] +pub struct ClientBlockBuilder { + parent: BlockId, + changes: OverlayedChanges, + state: S, + header: Header, + timestamp: Timestamp, + extrinsics: Vec, +} + +impl ClientBlockBuilder + where S::Error: Into +{ + // initialises a block, ready to allow extrinsics to be applied. + fn initialise_block(&mut self) -> Result<()> { + let result = { + let mut ext = state_machine::Ext::new(&mut self.changes, &self.state); + let h = self.header.clone(); + + ::substrate_executor::with_native_environment( + &mut ext, + || runtime::Executive::initialise_block(&h), + ).map_err(Into::into) + }; + + match result { + Ok(_) => { + self.changes.commit_prospective(); + Ok(()) + } + Err(e) => { + self.changes.discard_prospective(); + Err(e) + } + } + } + + // executes a extrinsic, inherent or otherwise, without appending to the list. + fn apply_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { + let result = { + let mut ext = state_machine::Ext::new(&mut self.changes, &self.state); + + ::substrate_executor::with_native_environment( + &mut ext, + move || runtime::Executive::apply_extrinsic(extrinsic), + ).map_err(Into::into) + }; + + match result { + Ok(_) => { + self.changes.commit_prospective(); + Ok(()) + } + Err(e) => { + self.changes.discard_prospective(); + Err(e) + } + } + } +} + +impl BlockBuilder for ClientBlockBuilder + where S::Error: Into +{ + fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { + // Check that this is not an "inherent" extrinsic. + if extrinsic.signature == Default::default() { + bail!(ErrorKind::PushedInherentTransaction(extrinsic)); + } else { + self.apply_extrinsic(extrinsic.clone())?; + self.extrinsics.push(extrinsic); + Ok(()) + } + } + + fn bake(mut self) -> Block { + let mut ext = state_machine::Ext::new(&mut self.changes, &self.state); + + let final_header = ::substrate_executor::with_native_environment( + &mut ext, + move || runtime::Executive::finalise_block() + ).expect("all inherent extrinsics pushed; all other extrinsics executed correctly; qed"); + Block { + header: final_header, + extrinsics: self.extrinsics, + } + } +} + +impl PolkadotApi for Client>> + where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ + type CheckedBlockId = CheckedId; + type BlockBuilder = ClientBlockBuilder; + + fn check_id(&self, id: BlockId) -> Result { + // bail if the code is not the same as the natively linked. + if self.code_at(&id)? != LocalDispatch::native_equivalent() { + bail!("This node is out of date. Block authoring may not work correctly. Bailing.") + } + + Ok(CheckedId(id)) + } + + fn session_keys(&self, at: &CheckedId) -> Result> { + with_runtime!(self, at, ::runtime::Consensus::authorities) + } + + fn validators(&self, at: &CheckedId) -> Result> { + with_runtime!(self, at, ::runtime::Session::validators) + } + + fn random_seed(&self, at: &CheckedId) -> Result { + with_runtime!(self, at, ::runtime::System::random_seed) + } + + fn duty_roster(&self, at: &CheckedId) -> Result { + with_runtime!(self, at, ::runtime::Parachains::calculate_duty_roster) + } + + fn timestamp(&self, at: &CheckedId) -> Result { + with_runtime!(self, at, ::runtime::Timestamp::now) + } + + fn evaluate_block(&self, at: &CheckedId, block: Block) -> Result { + use substrate_executor::error::ErrorKind as ExecErrorKind; + + let res = with_runtime!(self, at, || ::runtime::Executive::execute_block(block)); + match res { + Ok(()) => Ok(true), + Err(err) => match err.kind() { + &ErrorKind::Executor(ExecErrorKind::Runtime) => Ok(false), + _ => Err(err) + } + } + } + + fn index(&self, at: &CheckedId, account: AccountId) -> Result { + with_runtime!(self, at, || ::runtime::System::account_index(account)) + } + + fn active_parachains(&self, at: &CheckedId) -> Result> { + with_runtime!(self, at, ::runtime::Parachains::active_parachains) + } + + fn parachain_code(&self, at: &CheckedId, parachain: ParaId) -> Result>> { + with_runtime!(self, at, || ::runtime::Parachains::parachain_code(parachain)) + } + + fn parachain_head(&self, at: &CheckedId, parachain: ParaId) -> Result>> { + with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain)) + } + + fn build_block(&self, parent: &CheckedId, timestamp: Timestamp, parachains: Vec) -> Result { + let parent = parent.block_id(); + let header = Header { + parent_hash: self.block_hash_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))?, + number: self.block_number_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))? + 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + }; + + let extrinsics = vec![ + UncheckedExtrinsic { + extrinsic: Extrinsic { + signed: Default::default(), + index: Default::default(), + function: Call::Timestamp(TimestampCall::set(timestamp)), + }, + signature: Default::default(), + }, + UncheckedExtrinsic { + extrinsic: Extrinsic { + signed: Default::default(), + index: Default::default(), + function: Call::Parachains(ParachainsCall::set_heads(parachains)), + }, + signature: Default::default(), + } + ]; + + let mut builder = ClientBlockBuilder { + parent: *parent, + changes: OverlayedChanges::default(), + state: self.state_at(parent)?, + header, + timestamp, + extrinsics: extrinsics.clone(), + }; + + builder.initialise_block()?; + + for inherent in extrinsics { + builder.apply_extrinsic(inherent)?; + } + + Ok(builder) + } +} + +impl LocalPolkadotApi for Client>> + where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{} + +#[cfg(test)] +mod tests { + use super::*; + use keyring::Keyring; + use codec::Slicable; + use client::{self, LocalCallExecutor}; + use client::in_mem::Backend as InMemory; + use substrate_executor::NativeExecutionDispatch; + use substrate_primitives::{self, Header}; + use runtime::{GenesisConfig, ConsensusConfig, SessionConfig, BuildExternalities}; + + fn validators() -> Vec { + vec![ + Keyring::One.to_raw_public(), + Keyring::Two.to_raw_public(), + ] + } + + fn client() -> Client>> { + struct GenesisBuilder; + + impl client::GenesisBuilder for GenesisBuilder { + fn build(self) -> (Header, Vec<(Vec, Vec)>) { + let genesis_config = GenesisConfig { + consensus: Some(ConsensusConfig { + code: LocalDispatch::native_equivalent().to_vec(), + authorities: validators(), + }), + system: None, + session: Some(SessionConfig { + validators: validators(), + session_length: 100, + }), + council: Some(Default::default()), + democracy: Some(Default::default()), + parachains: Some(Default::default()), + staking: Some(Default::default()), + }; + + let storage = genesis_config.build_externalities(); + let block = ::client::genesis::construct_genesis_block(&storage); + (substrate_primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + } + } + + ::client::new_in_mem(LocalDispatch::new(), GenesisBuilder).unwrap() + } + + #[test] + fn gets_session_and_validator_keys() { + let client = client(); + let id = client.check_id(BlockId::Number(0)).unwrap(); + assert_eq!(client.session_keys(&id).unwrap(), validators()); + assert_eq!(client.validators(&id).unwrap(), validators()); + } + + #[test] + fn build_block() { + let client = client(); + + let id = client.check_id(BlockId::Number(0)).unwrap(); + let block_builder = client.build_block(&id, 1_000_000, Vec::new()).unwrap(); + let block = block_builder.bake(); + + assert_eq!(block.header.number, 1); + assert!(block.header.extrinsics_root != Default::default()); + } + + #[test] + fn fails_to_check_id_for_unknown_block() { + assert!(client().check_id(BlockId::Number(100)).is_err()); + } + + #[test] + fn gets_random_seed_with_genesis() { + let client = client(); + + let id = client.check_id(BlockId::Number(0)).unwrap(); + assert!(client.random_seed(&id).is_ok()); + } +} diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index fd24f52766..bef92d4d9f 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -34,14 +34,12 @@ extern crate error_chain; #[cfg(test)] extern crate substrate_keyring as keyring; -use client::backend::Backend; -use client::Client; -use polkadot_executor::Executor as LocalDispatch; -use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; -use state_machine::OverlayedChanges; +pub mod full; +pub mod light; + use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp}; use primitives::parachain::{DutyRoster, CandidateReceipt, Id as ParaId}; -use runtime::{Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall, ParachainsCall}; +use runtime::{Block, UncheckedExtrinsic}; error_chain! { errors { @@ -152,315 +150,8 @@ pub trait PolkadotApi { fn build_block(&self, parent: &Self::CheckedBlockId, timestamp: Timestamp, parachains: Vec) -> Result; } -/// A checked block ID used for the substrate-client implementation of CheckedBlockId; -#[derive(Debug, Clone, Copy)] -pub struct CheckedId(BlockId); +/// Mark for all Polkadot API implementations, that are making use of state data, stored locally. +pub trait LocalPolkadotApi: PolkadotApi {} -impl CheckedBlockId for CheckedId { - fn block_id(&self) -> &BlockId { - &self.0 - } -} - -// set up the necessary scaffolding to execute a set of calls to the runtime. -// this creates a new block on top of the given ID and initialises it. -macro_rules! with_runtime { - ($client: ident, $at: expr, $exec: expr) => {{ - let parent = $at.block_id(); - let header = Header { - parent_hash: $client.block_hash_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))?, - number: $client.block_number_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))? + 1, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }; - - $client.state_at(parent).map_err(Error::from).and_then(|state| { - let mut changes = Default::default(); - let mut ext = state_machine::Ext::new(&mut changes, &state); - - ::substrate_executor::with_native_environment(&mut ext, || { - ::runtime::Executive::initialise_block(&header); - ($exec)() - }).map_err(Into::into) - }) - }} -} - -impl PolkadotApi for Client> - where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> -{ - type CheckedBlockId = CheckedId; - type BlockBuilder = ClientBlockBuilder; - - fn check_id(&self, id: BlockId) -> Result { - // bail if the code is not the same as the natively linked. - if self.code_at(&id)? != LocalDispatch::native_equivalent() { - bail!("This node is out of date. Block authoring may not work correctly. Bailing.") - } - - Ok(CheckedId(id)) - } - - fn session_keys(&self, at: &CheckedId) -> Result> { - with_runtime!(self, at, ::runtime::Consensus::authorities) - } - - fn validators(&self, at: &CheckedId) -> Result> { - with_runtime!(self, at, ::runtime::Session::validators) - } - - fn random_seed(&self, at: &CheckedId) -> Result { - with_runtime!(self, at, ::runtime::System::random_seed) - } - - fn duty_roster(&self, at: &CheckedId) -> Result { - with_runtime!(self, at, ::runtime::Parachains::calculate_duty_roster) - } - - fn timestamp(&self, at: &CheckedId) -> Result { - with_runtime!(self, at, ::runtime::Timestamp::now) - } - - fn evaluate_block(&self, at: &CheckedId, block: Block) -> Result { - use substrate_executor::error::ErrorKind as ExecErrorKind; - - let res = with_runtime!(self, at, || ::runtime::Executive::execute_block(block)); - match res { - Ok(()) => Ok(true), - Err(err) => match err.kind() { - &ErrorKind::Executor(ExecErrorKind::Runtime) => Ok(false), - _ => Err(err) - } - } - } - - fn index(&self, at: &CheckedId, account: AccountId) -> Result { - with_runtime!(self, at, || ::runtime::System::account_index(account)) - } - - fn active_parachains(&self, at: &CheckedId) -> Result> { - with_runtime!(self, at, ::runtime::Parachains::active_parachains) - } - - fn parachain_code(&self, at: &CheckedId, parachain: ParaId) -> Result>> { - with_runtime!(self, at, || ::runtime::Parachains::parachain_code(parachain)) - } - - fn parachain_head(&self, at: &CheckedId, parachain: ParaId) -> Result>> { - with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain)) - } - - fn build_block(&self, parent: &CheckedId, timestamp: Timestamp, parachains: Vec) -> Result { - let parent = parent.block_id(); - let header = Header { - parent_hash: self.block_hash_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))?, - number: self.block_number_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))? + 1, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }; - - let extrinsics = vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: Default::default(), - index: Default::default(), - function: Call::Timestamp(TimestampCall::set(timestamp)), - }, - signature: Default::default(), - }, - UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: Default::default(), - index: Default::default(), - function: Call::Parachains(ParachainsCall::set_heads(parachains)), - }, - signature: Default::default(), - } - ]; - - let mut builder = ClientBlockBuilder { - parent: *parent, - changes: OverlayedChanges::default(), - state: self.state_at(parent)?, - header, - timestamp, - extrinsics: extrinsics.clone(), - }; - - builder.initialise_block()?; - - for inherent in extrinsics { - builder.apply_extrinsic(inherent)?; - } - - Ok(builder) - } -} - -/// A polkadot block builder. -#[derive(Debug, Clone)] -pub struct ClientBlockBuilder { - parent: BlockId, - changes: OverlayedChanges, - state: S, - header: Header, - timestamp: Timestamp, - extrinsics: Vec, -} - -impl ClientBlockBuilder - where S::Error: Into -{ - // initialises a block, ready to allow extrinsics to be applied. - fn initialise_block(&mut self) -> Result<()> { - let result = { - let mut ext = state_machine::Ext::new(&mut self.changes, &self.state); - let h = self.header.clone(); - - ::substrate_executor::with_native_environment( - &mut ext, - || runtime::Executive::initialise_block(&h), - ).map_err(Into::into) - }; - - match result { - Ok(_) => { - self.changes.commit_prospective(); - Ok(()) - } - Err(e) => { - self.changes.discard_prospective(); - Err(e) - } - } - } - - // executes a extrinsic, inherent or otherwise, without appending to the list. - fn apply_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { - let result = { - let mut ext = state_machine::Ext::new(&mut self.changes, &self.state); - - ::substrate_executor::with_native_environment( - &mut ext, - move || runtime::Executive::apply_extrinsic(extrinsic), - ).map_err(Into::into) - }; - - match result { - Ok(_) => { - self.changes.commit_prospective(); - Ok(()) - } - Err(e) => { - self.changes.discard_prospective(); - Err(e) - } - } - } -} - -impl BlockBuilder for ClientBlockBuilder - where S::Error: Into -{ - fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { - // Check that this is not an "inherent" extrinsic. - if extrinsic.signature == Default::default() { - bail!(ErrorKind::PushedInherentTransaction(extrinsic)); - } else { - self.apply_extrinsic(extrinsic.clone())?; - self.extrinsics.push(extrinsic); - Ok(()) - } - } - - fn bake(mut self) -> Block { - let mut ext = state_machine::Ext::new(&mut self.changes, &self.state); - - let final_header = ::substrate_executor::with_native_environment( - &mut ext, - move || runtime::Executive::finalise_block() - ).expect("all inherent extrinsics pushed; all other extrinsics executed correctly; qed"); - Block { - header: final_header, - extrinsics: self.extrinsics, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use keyring::Keyring; - use codec::Slicable; - use client::in_mem::Backend as InMemory; - use substrate_executor::NativeExecutionDispatch; - use runtime::{GenesisConfig, ConsensusConfig, SessionConfig, BuildExternalities}; - - fn validators() -> Vec { - vec![ - Keyring::One.to_raw_public(), - Keyring::Two.to_raw_public(), - ] - } - - fn client() -> Client> { - let genesis_config = GenesisConfig { - consensus: Some(ConsensusConfig { - code: LocalDispatch::native_equivalent().to_vec(), - authorities: validators(), - }), - system: None, - session: Some(SessionConfig { - validators: validators(), - session_length: 100, - }), - council: Some(Default::default()), - democracy: Some(Default::default()), - parachains: Some(Default::default()), - staking: Some(Default::default()), - }; - ::client::new_in_mem( - LocalDispatch::new(), - || { - let storage = genesis_config.build_externalities(); - let block = ::client::genesis::construct_genesis_block(&storage); - (substrate_primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) - } - ).unwrap() - } - - #[test] - fn gets_session_and_validator_keys() { - let client = client(); - let id = client.check_id(BlockId::Number(0)).unwrap(); - assert_eq!(client.session_keys(&id).unwrap(), validators()); - assert_eq!(client.validators(&id).unwrap(), validators()); - } - - #[test] - fn build_block() { - let client = client(); - - let id = client.check_id(BlockId::Number(0)).unwrap(); - let block_builder = client.build_block(&id, 1_000_000, Vec::new()).unwrap(); - let block = block_builder.bake(); - - assert_eq!(block.header.number, 1); - assert!(block.header.extrinsics_root != Default::default()); - } - - #[test] - fn fails_to_check_id_for_unknown_block() { - assert!(client().check_id(BlockId::Number(100)).is_err()); - } - - #[test] - fn gets_random_seed_with_genesis() { - let client = client(); - - let id = client.check_id(BlockId::Number(0)).unwrap(); - assert!(client.random_seed(&id).is_ok()); - } -} +/// Mark for all Polkadot API implementations, that are fetching required state data from remote nodes. +pub trait RemotePolkadotApi: PolkadotApi {} diff --git a/polkadot/api/src/light.rs b/polkadot/api/src/light.rs new file mode 100644 index 0000000000..6038a8ec1f --- /dev/null +++ b/polkadot/api/src/light.rs @@ -0,0 +1,106 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Strongly typed API for light Polkadot client. + +use std::sync::Arc; +use client::backend::{Backend, RemoteBackend}; +use client::{Client, CallExecutor}; +use codec::Slicable; +use state_machine; +use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp}; +use primitives::parachain::{DutyRoster, CandidateReceipt, Id as ParaId}; +use runtime::{Block, UncheckedExtrinsic}; +use full::CheckedId; +use {PolkadotApi, RemotePolkadotApi, BlockBuilder, CheckedBlockId, Result, ErrorKind}; + +/// Remote polkadot API implementation. +pub struct RemotePolkadotApiWrapper(pub Arc>); + +/// Block builder for light client. +pub struct LightBlockBuilder; + +impl PolkadotApi for RemotePolkadotApiWrapper + where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ + type CheckedBlockId = CheckedId; + type BlockBuilder = LightBlockBuilder; + + fn check_id(&self, id: BlockId) -> Result { + Ok(CheckedId(id)) + } + + fn session_keys(&self, at: &CheckedId) -> Result> { + self.0.executor().call(at.block_id(), "authorities", &[]) + .and_then(|r| Vec::::decode(&mut &r.return_data[..]) + .ok_or("error decoding session keys".into())) + .map_err(Into::into) + } + + fn validators(&self, _at: &CheckedId) -> Result> { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn random_seed(&self, _at: &Self::CheckedBlockId) -> Result { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn duty_roster(&self, _at: &CheckedId) -> Result { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn timestamp(&self, _at: &CheckedId) -> Result { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn evaluate_block(&self, _at: &CheckedId, _block: Block) -> Result { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn index(&self, _at: &CheckedId, _account: AccountId) -> Result { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result> { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn parachain_code(&self, _at: &Self::CheckedBlockId, _parachain: ParaId) -> Result>> { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn parachain_head(&self, _at: &Self::CheckedBlockId, _parachain: ParaId) -> Result>> { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn build_block(&self, _parent: &CheckedId, _timestamp: Timestamp, _parachains: Vec) -> Result { + Err(ErrorKind::UnknownRuntime.into()) + } +} + +impl RemotePolkadotApi for RemotePolkadotApiWrapper + where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{} + +impl BlockBuilder for LightBlockBuilder { + fn push_extrinsic(&mut self, _extrinsic: UncheckedExtrinsic) -> Result<()> { + Err(ErrorKind::UnknownRuntime.into()) + } + + fn bake(self) -> Block { + unimplemented!() + } +} diff --git a/polkadot/cli/src/cli.yml b/polkadot/cli/src/cli.yml index c839ba94f7..81ea904959 100644 --- a/polkadot/cli/src/cli.yml +++ b/polkadot/cli/src/cli.yml @@ -37,6 +37,10 @@ args: long: validator help: Enable validator mode takes_value: false + - light: + long: light + help: Run in light client mode + takes_value: false - port: long: port value_name: PORT diff --git a/polkadot/cli/src/informant.rs b/polkadot/cli/src/informant.rs index 70c49f9c44..54affa839e 100644 --- a/polkadot/cli/src/informant.rs +++ b/polkadot/cli/src/informant.rs @@ -23,12 +23,18 @@ use tokio_core::reactor; use network::{SyncState, SyncProvider}; use runtime_support::Hashable; use primitives::block::HeaderHash; -use client::BlockchainEvents; +use state_machine; +use client::{self, BlockchainEvents}; const TIMER_INTERVAL_MS: u64 = 5000; /// Spawn informant on the event loop -pub fn start(service: &Service, handle: reactor::Handle) { +pub fn start(service: &Service, handle: reactor::Handle) + where + B: client::backend::Backend + Send + Sync + 'static, + E: client::CallExecutor + Send + Sync + 'static, + client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ let interval = reactor::Interval::new_at(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS), &handle) .expect("Error creating informant timer"); diff --git a/polkadot/cli/src/lib.rs b/polkadot/cli/src/lib.rs index 604c665910..0934755f20 100644 --- a/polkadot/cli/src/lib.rs +++ b/polkadot/cli/src/lib.rs @@ -107,16 +107,7 @@ pub fn run(args: I) -> error::Result<()> where I: IntoIterator, T: Into + Clone, { - let mut core = reactor::Core::new().expect("tokio::Core could not be created"); - let exit = { - // can't use signal directly here because CtrlC takes only `Fn`. - let (exit_send, exit) = mpsc::channel(1); - ctrlc::CtrlC::set_handler(move || { - exit_send.clone().send(()).wait().expect("Error sending exit notification"); - }); - - exit - }; + let core = reactor::Core::new().expect("tokio::Core could not be created"); let yaml = load_yaml!("./cli.yml"); let matches = match clap::App::from_yaml(yaml).version(crate_version!()).get_matches_from_safe(args) { @@ -152,10 +143,12 @@ pub fn run(args: I) -> error::Result<()> where if matches.is_present("collator") { info!("Starting collator."); role = service::Role::COLLATOR; - } - else if matches.is_present("validator") { + } else if matches.is_present("validator") { info!("Starting validator."); role = service::Role::VALIDATOR; + } else if matches.is_present("light") { + info!("Starting light."); + role = service::Role::LIGHT; } match matches.value_of("chain") { @@ -195,13 +188,33 @@ pub fn run(args: I) -> error::Result<()> where config.keys = matches.values_of("key").unwrap_or_default().map(str::to_owned).collect(); - let service = service::Service::new(config)?; + match role == service::Role::LIGHT { + true => run_until_exit(core, service::new_light(config)?, &matches), + false => run_until_exit(core, service::new_full(config)?, &matches), + } +} + +fn run_until_exit(mut core: reactor::Core, service: service::Service, matches: &clap::ArgMatches) -> error::Result<()> + where + B: client::backend::Backend + Send + Sync + 'static, + E: client::CallExecutor + Send + Sync + 'static, + client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ + let exit = { + // can't use signal directly here because CtrlC takes only `Fn`. + let (exit_send, exit) = mpsc::channel(1); + ctrlc::CtrlC::set_handler(move || { + exit_send.clone().send(()).wait().expect("Error sending exit notification"); + }); + + exit + }; informant::start(&service, core.handle()); let _rpc_servers = { - let http_address = parse_address("127.0.0.1:9933", "rpc-port", &matches)?; - let ws_address = parse_address("127.0.0.1:9944", "ws-port", &matches)?; + let http_address = parse_address("127.0.0.1:9933", "rpc-port", matches)?; + let ws_address = parse_address("127.0.0.1:9944", "ws-port", matches)?; let handler = || { let chain = rpc::apis::chain::Chain::new(service.client(), core.remote()); diff --git a/polkadot/consensus/src/service.rs b/polkadot/consensus/src/service.rs index cae27b16fd..2177e87412 100644 --- a/polkadot/consensus/src/service.rs +++ b/polkadot/consensus/src/service.rs @@ -29,7 +29,7 @@ use ed25519; use futures::prelude::*; use futures::{future, Canceled}; use parking_lot::Mutex; -use polkadot_api::PolkadotApi; +use polkadot_api::LocalPolkadotApi; use polkadot_primitives::AccountId; use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt}; use primitives::{Hash, AuthorityId}; @@ -233,15 +233,17 @@ pub struct Service { impl Service { /// Create and start a new instance. - pub fn new( + pub fn new( client: Arc, + api: Arc, network: Arc, transaction_pool: Arc>, parachain_empty_duration: Duration, key: ed25519::Pair, ) -> Service where - C: BlockchainEvents + ChainHead + bft::BlockImport + bft::Authorities + PolkadotApi + Send + Sync + 'static, + A: LocalPolkadotApi + Send + Sync + 'static, + C: BlockchainEvents + ChainHead + bft::BlockImport + bft::Authorities + Send + Sync + 'static, { let (signal, exit) = ::exit_future::signal(); let thread = thread::spawn(move || { @@ -249,7 +251,7 @@ impl Service { let key = Arc::new(key); let factory = ProposerFactory { - client: client.clone(), + client: api.clone(), transaction_pool: transaction_pool.clone(), network: Network(network.clone()), collators: NoCollators, diff --git a/polkadot/service/Cargo.toml b/polkadot/service/Cargo.toml index 469dc2e201..bb9fb170bd 100644 --- a/polkadot/service/Cargo.toml +++ b/polkadot/service/Cargo.toml @@ -27,3 +27,4 @@ substrate-client = { path = "../../substrate/client" } substrate-client-db = { path = "../../substrate/client/db" } substrate-codec = { path = "../../substrate/codec" } substrate-executor = { path = "../../substrate/executor" } +substrate-state-machine = { path = "../../substrate/state-machine" } diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 7ed649b672..3dde4242bc 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -35,6 +35,7 @@ extern crate substrate_network as network; extern crate substrate_codec as codec; extern crate substrate_client_db as client_db; extern crate substrate_executor; +extern crate substrate_state_machine as state_machine; extern crate exit_future; extern crate tokio_core; @@ -55,7 +56,7 @@ use futures::prelude::*; use parking_lot::Mutex; use tokio_core::reactor::Core; use codec::Slicable; -use primitives::block::{Id as BlockId, Extrinsic, ExtrinsicHash, HeaderHash}; +use primitives::block::{Id as BlockId, Extrinsic, ExtrinsicHash, HeaderHash, Header}; use primitives::{AuthorityId, hashing}; use transaction_pool::TransactionPool; use substrate_executor::NativeExecutor; @@ -64,31 +65,39 @@ use keystore::Store as Keystore; use polkadot_api::PolkadotApi; use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig, StakingConfig, BuildExternalities}; -use client::{genesis, BlockchainEvents}; +use client::backend::Backend; +use client::{genesis, Client, BlockchainEvents, CallExecutor}; use network::ManageNetwork; use exit_future::Signal; pub use self::error::{ErrorKind, Error}; pub use config::{Configuration, Role, ChainSpec}; -type Client = client::Client>; +type CodeExecutor = NativeExecutor; /// Polkadot service. -pub struct Service { +pub struct Service { thread: Option>, - client: Arc, + client: Arc>, network: Arc, transaction_pool: Arc>, signal: Option, _consensus: Option, } -struct TransactionPoolAdapter { +struct TransactionPoolAdapter where A: Send + Sync, E: Send + Sync { pool: Arc>, - client: Arc, + client: Arc>, + api: Arc, } -impl network::TransactionPool for TransactionPoolAdapter { +impl network::TransactionPool for TransactionPoolAdapter + where + B: Backend + Send + Sync, + E: client::CallExecutor + Send + Sync, + client::error::Error: From<<::State as state_machine::backend::Backend>::Error>, + A: PolkadotApi + Send + Sync, +{ fn transactions(&self) -> Vec<(ExtrinsicHash, Vec)> { let best_block = match self.client.info() { Ok(info) => info.chain.best_hash, @@ -97,10 +106,11 @@ impl network::TransactionPool for TransactionPoolAdapter { return Vec::new(); } }; - let id = self.client.check_id(BlockId::Hash(best_block)).expect("Best block is always valid; qed."); + + let id = self.api.check_id(BlockId::Hash(best_block)).expect("Best block is always valid; qed."); let mut pool = self.pool.lock(); - pool.cull(None, transaction_pool::Ready::create(id, &*self.client)); - pool.pending(transaction_pool::Ready::create(id, &*self.client)).map(|t| { + pool.cull(None, transaction_pool::Ready::create(id.clone(), &*self.api)); + pool.pending(transaction_pool::Ready::create(id, &*self.api)).map(|t| { let hash = ::primitives::Hash::from(&t.hash()[..]); let tx = codec::Slicable::encode(t.as_transaction()); (hash, tx) @@ -257,16 +267,89 @@ fn local_testnet_config() -> ChainConfig { ]) } -impl Service { +struct GenesisBuilder { + config: GenesisConfig, +} + +impl client::GenesisBuilder for GenesisBuilder { + fn build(self) -> (Header, Vec<(Vec, Vec)>) { + let storage = self.config.build_externalities(); + let block = genesis::construct_genesis_block(&storage); + (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + } +} + +/// Creates light client and register protocol with the network service +pub fn new_light(config: Configuration) -> Result>>, error::Error> { + Service::new(move |_, executor, genesis_builder: GenesisBuilder| { + let client_backend = client::light::new_light_backend(); + let fetch_checker = Arc::new(client::light::new_fetch_checker(client_backend.clone(), executor)); + let fetcher = Arc::new(network::OnDemand::new(fetch_checker)); + let client = client::light::new_light(client_backend, fetcher.clone(), genesis_builder)?; + Ok((Arc::new(client), Some(fetcher))) + }, + |client| Arc::new(polkadot_api::light::RemotePolkadotApiWrapper(client.clone())), + |_client, _network, _tx_pool, _keystore| Ok(None), + config) +} + +/// Creates full client and register protocol with the network service +pub fn new_full(config: Configuration) -> Result>, error::Error> { + let is_validator = (config.roles & Role::VALIDATOR) == Role::VALIDATOR; + Service::new(|db_settings, executor, genesis_builder: GenesisBuilder| + Ok((Arc::new(client_db::new_client(db_settings, executor, genesis_builder)?), None)), + |client| client, + |client, network, tx_pool, keystore| { + if !is_validator { + return Ok(None); + } + + // Load the first available key. Code above makes sure it exisis. + let key = keystore.load(&keystore.contents()?[0], "")?; + info!("Using authority key {:?}", key.public()); + Ok(Some(consensus::Service::new( + client.clone(), + client.clone(), + network.clone(), + tx_pool.clone(), + ::std::time::Duration::from_millis(4000), // TODO: dynamic + key, + ))) + }, + config) +} + +impl Service + where + B: Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + 'static, + client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ /// Creates and register protocol with the network service - pub fn new(mut config: Configuration) -> Result { + fn new(client_creator: F, api_creator: G, consensus_creator: C, mut config: Configuration) -> Result + where + F: FnOnce( + client_db::DatabaseSettings, + CodeExecutor, + GenesisBuilder, + ) -> Result<(Arc>, Option>>), error::Error>, + G: Fn( + Arc>, + ) -> Arc, + C: Fn( + Arc>, + Arc, + Arc>, + &Keystore + ) -> Result, error::Error>, + A: PolkadotApi + Send + Sync + 'static, + { use std::sync::Barrier; let (signal, exit) = ::exit_future::signal(); // Create client let executor = polkadot_executor::Executor::new(); - let mut storage = Default::default(); let mut keystore = Keystore::open(config.keystore_path.into())?; for seed in &config.keys { @@ -285,10 +368,8 @@ impl Service { }; config.network.boot_nodes.extend(boot_nodes); - let prepare_genesis = || { - storage = genesis_config.build_externalities(); - let block = genesis::construct_genesis_block(&storage); - (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + let genesis_builder = GenesisBuilder { + config: genesis_config, }; let db_settings = client_db::DatabaseSettings { @@ -296,13 +377,15 @@ impl Service { path: config.database_path.into(), }; - let client = Arc::new(client_db::new_client(db_settings, executor, prepare_genesis)?); + let (client, on_demand) = client_creator(db_settings, executor, genesis_builder)?; + let api = api_creator(client.clone()); let best_header = client.best_block_header()?; info!("Starting Polkadot. Best block is #{}", best_header.number); let transaction_pool = Arc::new(Mutex::new(TransactionPool::new(config.transaction_pool))); let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { pool: transaction_pool.clone(), client: client.clone(), + api: api.clone(), }); let network_params = network::Params { config: network::ProtocolConfig { @@ -310,11 +393,13 @@ impl Service { }, network_config: config.network, chain: client.clone(), + on_demand: on_demand.clone().map(|d| d as Arc), transaction_pool: transaction_pool_adapter, }; let network = network::Service::new(network_params)?; let barrier = ::std::sync::Arc::new(Barrier::new(2)); + on_demand.map(|on_demand| on_demand.set_service_link(Arc::downgrade(&network))); let thread = { let client = client.clone(); @@ -347,20 +432,7 @@ impl Service { barrier.wait(); // Spin consensus service if configured - let consensus_service = if config.roles & Role::VALIDATOR == Role::VALIDATOR { - // Load the first available key. Code above makes sure it exisis. - let key = keystore.load(&keystore.contents()?[0], "")?; - info!("Using authority key {:?}", key.public()); - Some(consensus::Service::new( - client.clone(), - network.clone(), - transaction_pool.clone(), - ::std::time::Duration::from_millis(4000), // TODO: dynamic - key, - )) - } else { - None - }; + let consensus_service = consensus_creator(client.clone(), network.clone(), transaction_pool.clone(), &keystore)?; Ok(Service { thread: Some(thread), @@ -373,7 +445,7 @@ impl Service { } /// Get shared client instance. - pub fn client(&self) -> Arc { + pub fn client(&self) -> Arc> { self.client.clone() } @@ -396,7 +468,12 @@ fn prune_transactions(pool: &mut TransactionPool, extrinsics: &[Extrinsic]) { } /// Produce a task which prunes any finalized transactions from the pool. -pub fn prune_imported(client: &Client, pool: &Mutex, hash: HeaderHash) { +pub fn prune_imported(client: &Client, pool: &Mutex, hash: HeaderHash) + where + B: Backend + Send + Sync, + E: CallExecutor + Send + Sync, + client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ let id = BlockId::Hash(hash); match client.body(&id) { Ok(Some(body)) => prune_transactions(&mut *pool.lock(), &body[..]), @@ -405,7 +482,7 @@ pub fn prune_imported(client: &Client, pool: &Mutex, hash: Head } } -impl Drop for Service { +impl Drop for Service { fn drop(&mut self) { self.network.stop_network();