mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-24 12:25:51 +00:00
Preparing light client structure [v2] (#150)
* light client structure + remote call requests
This commit is contained in:
committed by
GitHub
parent
ab7eae6f88
commit
fa642cf01a
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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<S> {
|
||||||
|
parent: BlockId,
|
||||||
|
changes: OverlayedChanges,
|
||||||
|
state: S,
|
||||||
|
header: Header,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
extrinsics: Vec<UncheckedExtrinsic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: state_machine::Backend> ClientBlockBuilder<S>
|
||||||
|
where S::Error: Into<client::error::Error>
|
||||||
|
{
|
||||||
|
// 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<S: state_machine::Backend> BlockBuilder for ClientBlockBuilder<S>
|
||||||
|
where S::Error: Into<client::error::Error>
|
||||||
|
{
|
||||||
|
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<B: LocalBackend> PolkadotApi for Client<B, LocalCallExecutor<B, NativeExecutor<LocalDispatch>>>
|
||||||
|
where ::client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
||||||
|
{
|
||||||
|
type CheckedBlockId = CheckedId;
|
||||||
|
type BlockBuilder = ClientBlockBuilder<B::State>;
|
||||||
|
|
||||||
|
fn check_id(&self, id: BlockId) -> Result<CheckedId> {
|
||||||
|
// 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<Vec<SessionKey>> {
|
||||||
|
with_runtime!(self, at, ::runtime::Consensus::authorities)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validators(&self, at: &CheckedId) -> Result<Vec<AccountId>> {
|
||||||
|
with_runtime!(self, at, ::runtime::Session::validators)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_seed(&self, at: &CheckedId) -> Result<Hash> {
|
||||||
|
with_runtime!(self, at, ::runtime::System::random_seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn duty_roster(&self, at: &CheckedId) -> Result<DutyRoster> {
|
||||||
|
with_runtime!(self, at, ::runtime::Parachains::calculate_duty_roster)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timestamp(&self, at: &CheckedId) -> Result<Timestamp> {
|
||||||
|
with_runtime!(self, at, ::runtime::Timestamp::now)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_block(&self, at: &CheckedId, block: Block) -> Result<bool> {
|
||||||
|
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<Index> {
|
||||||
|
with_runtime!(self, at, || ::runtime::System::account_index(account))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_parachains(&self, at: &CheckedId) -> Result<Vec<ParaId>> {
|
||||||
|
with_runtime!(self, at, ::runtime::Parachains::active_parachains)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parachain_code(&self, at: &CheckedId, parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
||||||
|
with_runtime!(self, at, || ::runtime::Parachains::parachain_code(parachain))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parachain_head(&self, at: &CheckedId, parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
||||||
|
with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_block(&self, parent: &CheckedId, timestamp: Timestamp, parachains: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> {
|
||||||
|
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<B: LocalBackend> LocalPolkadotApi for Client<B, LocalCallExecutor<B, NativeExecutor<LocalDispatch>>>
|
||||||
|
where ::client::error::Error: From<<<B as Backend>::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<AccountId> {
|
||||||
|
vec![
|
||||||
|
Keyring::One.to_raw_public(),
|
||||||
|
Keyring::Two.to_raw_public(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client() -> Client<InMemory, LocalCallExecutor<InMemory, NativeExecutor<LocalDispatch>>> {
|
||||||
|
struct GenesisBuilder;
|
||||||
|
|
||||||
|
impl client::GenesisBuilder for GenesisBuilder {
|
||||||
|
fn build(self) -> (Header, Vec<(Vec<u8>, Vec<u8>)>) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-317
@@ -34,14 +34,12 @@ extern crate error_chain;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate substrate_keyring as keyring;
|
extern crate substrate_keyring as keyring;
|
||||||
|
|
||||||
use client::backend::Backend;
|
pub mod full;
|
||||||
use client::Client;
|
pub mod light;
|
||||||
use polkadot_executor::Executor as LocalDispatch;
|
|
||||||
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
|
||||||
use state_machine::OverlayedChanges;
|
|
||||||
use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp};
|
use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp};
|
||||||
use primitives::parachain::{DutyRoster, CandidateReceipt, Id as ParaId};
|
use primitives::parachain::{DutyRoster, CandidateReceipt, Id as ParaId};
|
||||||
use runtime::{Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall, ParachainsCall};
|
use runtime::{Block, UncheckedExtrinsic};
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
errors {
|
errors {
|
||||||
@@ -152,315 +150,8 @@ pub trait PolkadotApi {
|
|||||||
fn build_block(&self, parent: &Self::CheckedBlockId, timestamp: Timestamp, parachains: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder>;
|
fn build_block(&self, parent: &Self::CheckedBlockId, timestamp: Timestamp, parachains: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A checked block ID used for the substrate-client implementation of CheckedBlockId;
|
/// Mark for all Polkadot API implementations, that are making use of state data, stored locally.
|
||||||
#[derive(Debug, Clone, Copy)]
|
pub trait LocalPolkadotApi: PolkadotApi {}
|
||||||
pub struct CheckedId(BlockId);
|
|
||||||
|
|
||||||
impl CheckedBlockId for CheckedId {
|
/// Mark for all Polkadot API implementations, that are fetching required state data from remote nodes.
|
||||||
fn block_id(&self) -> &BlockId {
|
pub trait RemotePolkadotApi: PolkadotApi {}
|
||||||
&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<B: Backend> PolkadotApi for Client<B, NativeExecutor<LocalDispatch>>
|
|
||||||
where ::client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
|
||||||
{
|
|
||||||
type CheckedBlockId = CheckedId;
|
|
||||||
type BlockBuilder = ClientBlockBuilder<B::State>;
|
|
||||||
|
|
||||||
fn check_id(&self, id: BlockId) -> Result<CheckedId> {
|
|
||||||
// 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<Vec<SessionKey>> {
|
|
||||||
with_runtime!(self, at, ::runtime::Consensus::authorities)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validators(&self, at: &CheckedId) -> Result<Vec<AccountId>> {
|
|
||||||
with_runtime!(self, at, ::runtime::Session::validators)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_seed(&self, at: &CheckedId) -> Result<Hash> {
|
|
||||||
with_runtime!(self, at, ::runtime::System::random_seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duty_roster(&self, at: &CheckedId) -> Result<DutyRoster> {
|
|
||||||
with_runtime!(self, at, ::runtime::Parachains::calculate_duty_roster)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timestamp(&self, at: &CheckedId) -> Result<Timestamp> {
|
|
||||||
with_runtime!(self, at, ::runtime::Timestamp::now)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate_block(&self, at: &CheckedId, block: Block) -> Result<bool> {
|
|
||||||
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<Index> {
|
|
||||||
with_runtime!(self, at, || ::runtime::System::account_index(account))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn active_parachains(&self, at: &CheckedId) -> Result<Vec<ParaId>> {
|
|
||||||
with_runtime!(self, at, ::runtime::Parachains::active_parachains)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parachain_code(&self, at: &CheckedId, parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
|
||||||
with_runtime!(self, at, || ::runtime::Parachains::parachain_code(parachain))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parachain_head(&self, at: &CheckedId, parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
|
||||||
with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_block(&self, parent: &CheckedId, timestamp: Timestamp, parachains: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> {
|
|
||||||
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<S> {
|
|
||||||
parent: BlockId,
|
|
||||||
changes: OverlayedChanges,
|
|
||||||
state: S,
|
|
||||||
header: Header,
|
|
||||||
timestamp: Timestamp,
|
|
||||||
extrinsics: Vec<UncheckedExtrinsic>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: state_machine::Backend> ClientBlockBuilder<S>
|
|
||||||
where S::Error: Into<client::error::Error>
|
|
||||||
{
|
|
||||||
// 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<S: state_machine::Backend> BlockBuilder for ClientBlockBuilder<S>
|
|
||||||
where S::Error: Into<client::error::Error>
|
|
||||||
{
|
|
||||||
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<AccountId> {
|
|
||||||
vec![
|
|
||||||
Keyring::One.to_raw_public(),
|
|
||||||
Keyring::Two.to_raw_public(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn client() -> Client<InMemory, NativeExecutor<LocalDispatch>> {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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<B: Backend, E: CallExecutor>(pub Arc<Client<B, E>>);
|
||||||
|
|
||||||
|
/// Block builder for light client.
|
||||||
|
pub struct LightBlockBuilder;
|
||||||
|
|
||||||
|
impl<B: Backend, E: CallExecutor> PolkadotApi for RemotePolkadotApiWrapper<B, E>
|
||||||
|
where ::client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
||||||
|
{
|
||||||
|
type CheckedBlockId = CheckedId;
|
||||||
|
type BlockBuilder = LightBlockBuilder;
|
||||||
|
|
||||||
|
fn check_id(&self, id: BlockId) -> Result<CheckedId> {
|
||||||
|
Ok(CheckedId(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn session_keys(&self, at: &CheckedId) -> Result<Vec<SessionKey>> {
|
||||||
|
self.0.executor().call(at.block_id(), "authorities", &[])
|
||||||
|
.and_then(|r| Vec::<SessionKey>::decode(&mut &r.return_data[..])
|
||||||
|
.ok_or("error decoding session keys".into()))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validators(&self, _at: &CheckedId) -> Result<Vec<AccountId>> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_seed(&self, _at: &Self::CheckedBlockId) -> Result<Hash> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn duty_roster(&self, _at: &CheckedId) -> Result<DutyRoster> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timestamp(&self, _at: &CheckedId) -> Result<Timestamp> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_block(&self, _at: &CheckedId, _block: Block) -> Result<bool> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(&self, _at: &CheckedId, _account: AccountId) -> Result<Index> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result<Vec<ParaId>> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parachain_code(&self, _at: &Self::CheckedBlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parachain_head(&self, _at: &Self::CheckedBlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_block(&self, _parent: &CheckedId, _timestamp: Timestamp, _parachains: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: RemoteBackend, E: CallExecutor> RemotePolkadotApi for RemotePolkadotApiWrapper<B, E>
|
||||||
|
where ::client::error::Error: From<<<B as Backend>::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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,10 @@ args:
|
|||||||
long: validator
|
long: validator
|
||||||
help: Enable validator mode
|
help: Enable validator mode
|
||||||
takes_value: false
|
takes_value: false
|
||||||
|
- light:
|
||||||
|
long: light
|
||||||
|
help: Run in light client mode
|
||||||
|
takes_value: false
|
||||||
- port:
|
- port:
|
||||||
long: port
|
long: port
|
||||||
value_name: PORT
|
value_name: PORT
|
||||||
|
|||||||
@@ -23,12 +23,18 @@ use tokio_core::reactor;
|
|||||||
use network::{SyncState, SyncProvider};
|
use network::{SyncState, SyncProvider};
|
||||||
use runtime_support::Hashable;
|
use runtime_support::Hashable;
|
||||||
use primitives::block::HeaderHash;
|
use primitives::block::HeaderHash;
|
||||||
use client::BlockchainEvents;
|
use state_machine;
|
||||||
|
use client::{self, BlockchainEvents};
|
||||||
|
|
||||||
const TIMER_INTERVAL_MS: u64 = 5000;
|
const TIMER_INTERVAL_MS: u64 = 5000;
|
||||||
|
|
||||||
/// Spawn informant on the event loop
|
/// Spawn informant on the event loop
|
||||||
pub fn start(service: &Service, handle: reactor::Handle) {
|
pub fn start<B, E>(service: &Service<B, E>, handle: reactor::Handle)
|
||||||
|
where
|
||||||
|
B: client::backend::Backend + Send + Sync + 'static,
|
||||||
|
E: client::CallExecutor + Send + Sync + 'static,
|
||||||
|
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>
|
||||||
|
{
|
||||||
let interval = reactor::Interval::new_at(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS), &handle)
|
let interval = reactor::Interval::new_at(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS), &handle)
|
||||||
.expect("Error creating informant timer");
|
.expect("Error creating informant timer");
|
||||||
|
|
||||||
|
|||||||
+28
-15
@@ -107,16 +107,7 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
T: Into<std::ffi::OsString> + Clone,
|
T: Into<std::ffi::OsString> + Clone,
|
||||||
{
|
{
|
||||||
let mut core = reactor::Core::new().expect("tokio::Core could not be created");
|
let 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 yaml = load_yaml!("./cli.yml");
|
let yaml = load_yaml!("./cli.yml");
|
||||||
let matches = match clap::App::from_yaml(yaml).version(crate_version!()).get_matches_from_safe(args) {
|
let matches = match clap::App::from_yaml(yaml).version(crate_version!()).get_matches_from_safe(args) {
|
||||||
@@ -152,10 +143,12 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
if matches.is_present("collator") {
|
if matches.is_present("collator") {
|
||||||
info!("Starting collator.");
|
info!("Starting collator.");
|
||||||
role = service::Role::COLLATOR;
|
role = service::Role::COLLATOR;
|
||||||
}
|
} else if matches.is_present("validator") {
|
||||||
else if matches.is_present("validator") {
|
|
||||||
info!("Starting validator.");
|
info!("Starting validator.");
|
||||||
role = service::Role::VALIDATOR;
|
role = service::Role::VALIDATOR;
|
||||||
|
} else if matches.is_present("light") {
|
||||||
|
info!("Starting light.");
|
||||||
|
role = service::Role::LIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
match matches.value_of("chain") {
|
match matches.value_of("chain") {
|
||||||
@@ -195,13 +188,33 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
|
|
||||||
config.keys = matches.values_of("key").unwrap_or_default().map(str::to_owned).collect();
|
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<B, E>(mut core: reactor::Core, service: service::Service<B, E>, matches: &clap::ArgMatches) -> error::Result<()>
|
||||||
|
where
|
||||||
|
B: client::backend::Backend + Send + Sync + 'static,
|
||||||
|
E: client::CallExecutor + Send + Sync + 'static,
|
||||||
|
client::error::Error: From<<<B as client::backend::Backend>::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());
|
informant::start(&service, core.handle());
|
||||||
|
|
||||||
let _rpc_servers = {
|
let _rpc_servers = {
|
||||||
let http_address = parse_address("127.0.0.1:9933", "rpc-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 ws_address = parse_address("127.0.0.1:9944", "ws-port", matches)?;
|
||||||
|
|
||||||
let handler = || {
|
let handler = || {
|
||||||
let chain = rpc::apis::chain::Chain::new(service.client(), core.remote());
|
let chain = rpc::apis::chain::Chain::new(service.client(), core.remote());
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use ed25519;
|
|||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures::{future, Canceled};
|
use futures::{future, Canceled};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use polkadot_api::PolkadotApi;
|
use polkadot_api::LocalPolkadotApi;
|
||||||
use polkadot_primitives::AccountId;
|
use polkadot_primitives::AccountId;
|
||||||
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt};
|
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt};
|
||||||
use primitives::{Hash, AuthorityId};
|
use primitives::{Hash, AuthorityId};
|
||||||
@@ -233,15 +233,17 @@ pub struct Service {
|
|||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
/// Create and start a new instance.
|
/// Create and start a new instance.
|
||||||
pub fn new<C>(
|
pub fn new<A, C>(
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
|
api: Arc<A>,
|
||||||
network: Arc<net::ConsensusService>,
|
network: Arc<net::ConsensusService>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<Mutex<TransactionPool>>,
|
||||||
parachain_empty_duration: Duration,
|
parachain_empty_duration: Duration,
|
||||||
key: ed25519::Pair,
|
key: ed25519::Pair,
|
||||||
) -> Service
|
) -> Service
|
||||||
where
|
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 (signal, exit) = ::exit_future::signal();
|
||||||
let thread = thread::spawn(move || {
|
let thread = thread::spawn(move || {
|
||||||
@@ -249,7 +251,7 @@ impl Service {
|
|||||||
let key = Arc::new(key);
|
let key = Arc::new(key);
|
||||||
|
|
||||||
let factory = ProposerFactory {
|
let factory = ProposerFactory {
|
||||||
client: client.clone(),
|
client: api.clone(),
|
||||||
transaction_pool: transaction_pool.clone(),
|
transaction_pool: transaction_pool.clone(),
|
||||||
network: Network(network.clone()),
|
network: Network(network.clone()),
|
||||||
collators: NoCollators,
|
collators: NoCollators,
|
||||||
|
|||||||
@@ -27,3 +27,4 @@ substrate-client = { path = "../../substrate/client" }
|
|||||||
substrate-client-db = { path = "../../substrate/client/db" }
|
substrate-client-db = { path = "../../substrate/client/db" }
|
||||||
substrate-codec = { path = "../../substrate/codec" }
|
substrate-codec = { path = "../../substrate/codec" }
|
||||||
substrate-executor = { path = "../../substrate/executor" }
|
substrate-executor = { path = "../../substrate/executor" }
|
||||||
|
substrate-state-machine = { path = "../../substrate/state-machine" }
|
||||||
|
|||||||
+113
-36
@@ -35,6 +35,7 @@ extern crate substrate_network as network;
|
|||||||
extern crate substrate_codec as codec;
|
extern crate substrate_codec as codec;
|
||||||
extern crate substrate_client_db as client_db;
|
extern crate substrate_client_db as client_db;
|
||||||
extern crate substrate_executor;
|
extern crate substrate_executor;
|
||||||
|
extern crate substrate_state_machine as state_machine;
|
||||||
|
|
||||||
extern crate exit_future;
|
extern crate exit_future;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
@@ -55,7 +56,7 @@ use futures::prelude::*;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tokio_core::reactor::Core;
|
use tokio_core::reactor::Core;
|
||||||
use codec::Slicable;
|
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 primitives::{AuthorityId, hashing};
|
||||||
use transaction_pool::TransactionPool;
|
use transaction_pool::TransactionPool;
|
||||||
use substrate_executor::NativeExecutor;
|
use substrate_executor::NativeExecutor;
|
||||||
@@ -64,31 +65,39 @@ use keystore::Store as Keystore;
|
|||||||
use polkadot_api::PolkadotApi;
|
use polkadot_api::PolkadotApi;
|
||||||
use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig,
|
use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig,
|
||||||
SessionConfig, StakingConfig, BuildExternalities};
|
SessionConfig, StakingConfig, BuildExternalities};
|
||||||
use client::{genesis, BlockchainEvents};
|
use client::backend::Backend;
|
||||||
|
use client::{genesis, Client, BlockchainEvents, CallExecutor};
|
||||||
use network::ManageNetwork;
|
use network::ManageNetwork;
|
||||||
use exit_future::Signal;
|
use exit_future::Signal;
|
||||||
|
|
||||||
pub use self::error::{ErrorKind, Error};
|
pub use self::error::{ErrorKind, Error};
|
||||||
pub use config::{Configuration, Role, ChainSpec};
|
pub use config::{Configuration, Role, ChainSpec};
|
||||||
|
|
||||||
type Client = client::Client<client_db::Backend, NativeExecutor<LocalDispatch>>;
|
type CodeExecutor = NativeExecutor<LocalDispatch>;
|
||||||
|
|
||||||
/// Polkadot service.
|
/// Polkadot service.
|
||||||
pub struct Service {
|
pub struct Service<B, E> {
|
||||||
thread: Option<thread::JoinHandle<()>>,
|
thread: Option<thread::JoinHandle<()>>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client<B, E>>,
|
||||||
network: Arc<network::Service>,
|
network: Arc<network::Service>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<Mutex<TransactionPool>>,
|
||||||
signal: Option<Signal>,
|
signal: Option<Signal>,
|
||||||
_consensus: Option<consensus::Service>,
|
_consensus: Option<consensus::Service>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TransactionPoolAdapter {
|
struct TransactionPoolAdapter<B, E, A> where A: Send + Sync, E: Send + Sync {
|
||||||
pool: Arc<Mutex<TransactionPool>>,
|
pool: Arc<Mutex<TransactionPool>>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client<B, E>>,
|
||||||
|
api: Arc<A>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl network::TransactionPool for TransactionPoolAdapter {
|
impl<B, E, A> network::TransactionPool for TransactionPoolAdapter<B, E, A>
|
||||||
|
where
|
||||||
|
B: Backend + Send + Sync,
|
||||||
|
E: client::CallExecutor + Send + Sync,
|
||||||
|
client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>,
|
||||||
|
A: PolkadotApi + Send + Sync,
|
||||||
|
{
|
||||||
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)> {
|
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)> {
|
||||||
let best_block = match self.client.info() {
|
let best_block = match self.client.info() {
|
||||||
Ok(info) => info.chain.best_hash,
|
Ok(info) => info.chain.best_hash,
|
||||||
@@ -97,10 +106,11 @@ impl network::TransactionPool for TransactionPoolAdapter {
|
|||||||
return Vec::new();
|
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();
|
let mut pool = self.pool.lock();
|
||||||
pool.cull(None, transaction_pool::Ready::create(id, &*self.client));
|
pool.cull(None, transaction_pool::Ready::create(id.clone(), &*self.api));
|
||||||
pool.pending(transaction_pool::Ready::create(id, &*self.client)).map(|t| {
|
pool.pending(transaction_pool::Ready::create(id, &*self.api)).map(|t| {
|
||||||
let hash = ::primitives::Hash::from(&t.hash()[..]);
|
let hash = ::primitives::Hash::from(&t.hash()[..]);
|
||||||
let tx = codec::Slicable::encode(t.as_transaction());
|
let tx = codec::Slicable::encode(t.as_transaction());
|
||||||
(hash, tx)
|
(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<u8>, Vec<u8>)>) {
|
||||||
|
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<Service<client::light::Backend, client::RemoteCallExecutor<client::light::Backend, network::OnDemand<network::Service>>>, 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<Service<client_db::Backend, client::LocalCallExecutor<client_db::Backend, CodeExecutor>>, 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<B, E> Service<B, E>
|
||||||
|
where
|
||||||
|
B: Backend + Send + Sync + 'static,
|
||||||
|
E: CallExecutor + Send + Sync + 'static,
|
||||||
|
client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
||||||
|
{
|
||||||
/// Creates and register protocol with the network service
|
/// Creates and register protocol with the network service
|
||||||
pub fn new(mut config: Configuration) -> Result<Service, error::Error> {
|
fn new<F, G, C, A>(client_creator: F, api_creator: G, consensus_creator: C, mut config: Configuration) -> Result<Self, error::Error>
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
client_db::DatabaseSettings,
|
||||||
|
CodeExecutor,
|
||||||
|
GenesisBuilder,
|
||||||
|
) -> Result<(Arc<Client<B, E>>, Option<Arc<network::OnDemand<network::Service>>>), error::Error>,
|
||||||
|
G: Fn(
|
||||||
|
Arc<Client<B, E>>,
|
||||||
|
) -> Arc<A>,
|
||||||
|
C: Fn(
|
||||||
|
Arc<Client<B, E>>,
|
||||||
|
Arc<network::Service>,
|
||||||
|
Arc<Mutex<TransactionPool>>,
|
||||||
|
&Keystore
|
||||||
|
) -> Result<Option<consensus::Service>, error::Error>,
|
||||||
|
A: PolkadotApi + Send + Sync + 'static,
|
||||||
|
{
|
||||||
use std::sync::Barrier;
|
use std::sync::Barrier;
|
||||||
|
|
||||||
let (signal, exit) = ::exit_future::signal();
|
let (signal, exit) = ::exit_future::signal();
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
let executor = polkadot_executor::Executor::new();
|
let executor = polkadot_executor::Executor::new();
|
||||||
let mut storage = Default::default();
|
|
||||||
|
|
||||||
let mut keystore = Keystore::open(config.keystore_path.into())?;
|
let mut keystore = Keystore::open(config.keystore_path.into())?;
|
||||||
for seed in &config.keys {
|
for seed in &config.keys {
|
||||||
@@ -285,10 +368,8 @@ impl Service {
|
|||||||
};
|
};
|
||||||
config.network.boot_nodes.extend(boot_nodes);
|
config.network.boot_nodes.extend(boot_nodes);
|
||||||
|
|
||||||
let prepare_genesis = || {
|
let genesis_builder = GenesisBuilder {
|
||||||
storage = genesis_config.build_externalities();
|
config: genesis_config,
|
||||||
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 db_settings = client_db::DatabaseSettings {
|
let db_settings = client_db::DatabaseSettings {
|
||||||
@@ -296,13 +377,15 @@ impl Service {
|
|||||||
path: config.database_path.into(),
|
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()?;
|
let best_header = client.best_block_header()?;
|
||||||
info!("Starting Polkadot. Best block is #{}", best_header.number);
|
info!("Starting Polkadot. Best block is #{}", best_header.number);
|
||||||
let transaction_pool = Arc::new(Mutex::new(TransactionPool::new(config.transaction_pool)));
|
let transaction_pool = Arc::new(Mutex::new(TransactionPool::new(config.transaction_pool)));
|
||||||
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
|
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
|
||||||
pool: transaction_pool.clone(),
|
pool: transaction_pool.clone(),
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
|
api: api.clone(),
|
||||||
});
|
});
|
||||||
let network_params = network::Params {
|
let network_params = network::Params {
|
||||||
config: network::ProtocolConfig {
|
config: network::ProtocolConfig {
|
||||||
@@ -310,11 +393,13 @@ impl Service {
|
|||||||
},
|
},
|
||||||
network_config: config.network,
|
network_config: config.network,
|
||||||
chain: client.clone(),
|
chain: client.clone(),
|
||||||
|
on_demand: on_demand.clone().map(|d| d as Arc<network::OnDemandService>),
|
||||||
transaction_pool: transaction_pool_adapter,
|
transaction_pool: transaction_pool_adapter,
|
||||||
};
|
};
|
||||||
|
|
||||||
let network = network::Service::new(network_params)?;
|
let network = network::Service::new(network_params)?;
|
||||||
let barrier = ::std::sync::Arc::new(Barrier::new(2));
|
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 thread = {
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
@@ -347,20 +432,7 @@ impl Service {
|
|||||||
barrier.wait();
|
barrier.wait();
|
||||||
|
|
||||||
// Spin consensus service if configured
|
// Spin consensus service if configured
|
||||||
let consensus_service = if config.roles & Role::VALIDATOR == Role::VALIDATOR {
|
let consensus_service = consensus_creator(client.clone(), network.clone(), transaction_pool.clone(), &keystore)?;
|
||||||
// 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
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Service {
|
Ok(Service {
|
||||||
thread: Some(thread),
|
thread: Some(thread),
|
||||||
@@ -373,7 +445,7 @@ impl Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get shared client instance.
|
/// Get shared client instance.
|
||||||
pub fn client(&self) -> Arc<Client> {
|
pub fn client(&self) -> Arc<Client<B, E>> {
|
||||||
self.client.clone()
|
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.
|
/// Produce a task which prunes any finalized transactions from the pool.
|
||||||
pub fn prune_imported(client: &Client, pool: &Mutex<TransactionPool>, hash: HeaderHash) {
|
pub fn prune_imported<B, E>(client: &Client<B, E>, pool: &Mutex<TransactionPool>, hash: HeaderHash)
|
||||||
|
where
|
||||||
|
B: Backend + Send + Sync,
|
||||||
|
E: CallExecutor + Send + Sync,
|
||||||
|
client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
||||||
|
{
|
||||||
let id = BlockId::Hash(hash);
|
let id = BlockId::Hash(hash);
|
||||||
match client.body(&id) {
|
match client.body(&id) {
|
||||||
Ok(Some(body)) => prune_transactions(&mut *pool.lock(), &body[..]),
|
Ok(Some(body)) => prune_transactions(&mut *pool.lock(), &body[..]),
|
||||||
@@ -405,7 +482,7 @@ pub fn prune_imported(client: &Client, pool: &Mutex<TransactionPool>, hash: Head
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Service {
|
impl<B, E> Drop for Service<B, E> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.network.stop_network();
|
self.network.stop_network();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user