Transaction queue integration + submission RPC (#99)

* Implement transaction queue RPC.

* whitespace

* Support without_std env in environmental!  (#110)

* Make environmental crate support without_std env.

* Small doc fixes.

* Remove dead code.
This commit is contained in:
Gav Wood
2018-04-09 16:28:06 +02:00
committed by GitHub
parent f72e9e7e13
commit 3024a839f2
19 changed files with 209 additions and 3 deletions
+4
View File
@@ -219,6 +219,7 @@ dependencies = [
"substrate-codec 0.1.0", "substrate-codec 0.1.0",
"substrate-executor 0.1.0", "substrate-executor 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-rpc 0.1.0",
"substrate-rpc-servers 0.1.0", "substrate-rpc-servers 0.1.0",
"substrate-runtime-io 0.1.0", "substrate-runtime-io 0.1.0",
"substrate-state-machine 0.1.0", "substrate-state-machine 0.1.0",
@@ -1364,8 +1365,10 @@ dependencies = [
"polkadot-api 0.1.0", "polkadot-api 0.1.0",
"polkadot-primitives 0.1.0", "polkadot-primitives 0.1.0",
"polkadot-runtime 0.1.0", "polkadot-runtime 0.1.0",
"substrate-client 0.1.0",
"substrate-codec 0.1.0", "substrate-codec 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-rpc 0.1.0",
"substrate-runtime-primitives 0.1.0", "substrate-runtime-primitives 0.1.0",
"transaction-pool 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "transaction-pool 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@@ -1865,6 +1868,7 @@ dependencies = [
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git)",
"jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git)",
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-executor 0.1.0", "substrate-executor 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
+1
View File
@@ -19,6 +19,7 @@ substrate-state-machine = { path = "../../substrate/state-machine" }
substrate-executor = { path = "../../substrate/executor" } substrate-executor = { path = "../../substrate/executor" }
substrate-primitives = { path = "../../substrate/primitives" } substrate-primitives = { path = "../../substrate/primitives" }
substrate-rpc-servers = { path = "../../substrate/rpc-servers" } substrate-rpc-servers = { path = "../../substrate/rpc-servers" }
substrate-rpc = { path = "../../substrate/rpc" }
demo-primitives = { path = "../primitives" } demo-primitives = { path = "../primitives" }
demo-executor = { path = "../executor" } demo-executor = { path = "../executor" }
demo-runtime = { path = "../runtime" } demo-runtime = { path = "../runtime" }
+9 -1
View File
@@ -26,6 +26,7 @@ extern crate substrate_runtime_io as runtime_io;
extern crate substrate_state_machine as state_machine; extern crate substrate_state_machine as state_machine;
extern crate substrate_client as client; extern crate substrate_client as client;
extern crate substrate_primitives as primitives; extern crate substrate_primitives as primitives;
extern crate substrate_rpc;
extern crate substrate_rpc_servers as rpc; extern crate substrate_rpc_servers as rpc;
extern crate demo_primitives; extern crate demo_primitives;
extern crate demo_executor; extern crate demo_executor;
@@ -49,6 +50,13 @@ use demo_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfi
SessionConfig, StakingConfig, BuildExternalities}; SessionConfig, StakingConfig, BuildExternalities};
use client::genesis; use client::genesis;
struct DummyPool;
impl substrate_rpc::author::AuthorApi for DummyPool {
fn submit_extrinsic(&self, _: primitives::block::Extrinsic) -> substrate_rpc::author::error::Result<()> {
Err(substrate_rpc::author::error::ErrorKind::Unimplemented.into())
}
}
/// Parse command line arguments and start the node. /// Parse command line arguments and start the node.
/// ///
/// IANA unassigned port ranges that we could use: /// IANA unassigned port ranges that we could use:
@@ -126,7 +134,7 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
let client = Arc::new(client::new_in_mem(executor, prepare_genesis)?); let client = Arc::new(client::new_in_mem(executor, prepare_genesis)?);
let address = "127.0.0.1:9933".parse().unwrap(); let address = "127.0.0.1:9933".parse().unwrap();
let handler = rpc::rpc_handler(client); let handler = rpc::rpc_handler(client, DummyPool);
let server = rpc::start_http(&address, handler)?; let server = rpc::start_http(&address, handler)?;
if let Some(_) = matches.subcommand_matches("validator") { if let Some(_) = matches.subcommand_matches("validator") {
+2 -1
View File
@@ -118,7 +118,7 @@ pub fn run<I, T>(args: I, exit: mpsc::Receiver<()>) -> error::Result<()> where
let rpc_port: u16 = port.parse().expect("Invalid RPC port value specified."); let rpc_port: u16 = port.parse().expect("Invalid RPC port value specified.");
address.set_port(rpc_port); address.set_port(rpc_port);
} }
let handler = rpc::rpc_handler(service.client()); let handler = rpc::rpc_handler(service.client(), service.transaction_pool());
let _server = rpc::start_http(&address, handler)?; let _server = rpc::start_http(&address, handler)?;
exit.recv().ok(); exit.recv().ok();
@@ -150,6 +150,7 @@ fn default_base_path() -> PathBuf {
&app_info, &app_info,
).expect("app directories exist on all supported platforms; qed") ).expect("app directories exist on all supported platforms; qed")
} }
fn init_logger(pattern: &str) { fn init_logger(pattern: &str) {
let mut builder = env_logger::LogBuilder::new(); let mut builder = env_logger::LogBuilder::new();
// Disable info logging by default for some modules: // Disable info logging by default for some modules:
+4
View File
@@ -570,9 +570,13 @@ impl<C: PolkadotApi, R: TableRouter> bft::Proposer for Proposer<C, R> {
} }
let polkadot_block = block_builder.bake(); let polkadot_block = block_builder.bake();
info!("Proposing block [number: {}; extrinsics: [{}], parent_hash: {}]", polkadot_block.header.number, polkadot_block.extrinsics.len(), polkadot_block.header.parent_hash);
let substrate_block = Slicable::decode(&mut polkadot_block.encode().as_slice()) let substrate_block = Slicable::decode(&mut polkadot_block.encode().as_slice())
.expect("polkadot blocks defined to serialize to substrate blocks correctly; qed"); .expect("polkadot blocks defined to serialize to substrate blocks correctly; qed");
assert!(evaluate_proposal(&substrate_block, &*self.client, current_timestamp(), &self.parent_hash, &self.parent_id).is_ok());
Ok(substrate_block) Ok(substrate_block)
} }
Binary file not shown.
@@ -9,6 +9,8 @@ error-chain = "0.11"
polkadot-api = { path = "../api" } polkadot-api = { path = "../api" }
polkadot-primitives = { path = "../primitives" } polkadot-primitives = { path = "../primitives" }
polkadot-runtime = { path = "../runtime" } polkadot-runtime = { path = "../runtime" }
substrate-client = { path = "../../substrate/client" }
substrate-rpc = { path = "../../substrate/rpc" }
substrate-primitives = { path = "../../substrate/primitives" } substrate-primitives = { path = "../../substrate/primitives" }
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
substrate-codec = { path = "../../substrate/codec" } substrate-codec = { path = "../../substrate/codec" }
@@ -17,6 +17,7 @@
extern crate ed25519; extern crate ed25519;
extern crate ethereum_types; extern crate ethereum_types;
extern crate substrate_codec as codec; extern crate substrate_codec as codec;
extern crate substrate_rpc;
extern crate substrate_primitives as substrate_primitives; extern crate substrate_primitives as substrate_primitives;
extern crate substrate_runtime_primitives as substrate_runtime_primitives; extern crate substrate_runtime_primitives as substrate_runtime_primitives;
extern crate polkadot_runtime as runtime; extern crate polkadot_runtime as runtime;
@@ -31,8 +32,10 @@ use std::collections::HashMap;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::Arc; use std::sync::Arc;
use codec::Slicable;
use polkadot_api::PolkadotApi; use polkadot_api::PolkadotApi;
use primitives::{AccountId, Timestamp}; use primitives::{AccountId, Timestamp};
use substrate_primitives::block::Extrinsic;
use runtime::{Block, UncheckedExtrinsic, TimestampCall, Call}; use runtime::{Block, UncheckedExtrinsic, TimestampCall, Call};
use substrate_runtime_primitives::traits::Checkable; use substrate_runtime_primitives::traits::Checkable;
use transaction_pool::{Pool, Readiness}; use transaction_pool::{Pool, Readiness};
@@ -371,6 +374,16 @@ impl TransactionPool {
} }
} }
impl substrate_rpc::author::AsyncAuthorApi for TransactionPool {
fn submit_extrinsic(&mut self, xt: Extrinsic) -> substrate_rpc::author::error::Result<()> {
self.import(xt
.using_encoded(|ref mut s| UncheckedExtrinsic::decode(s))
.ok_or(substrate_rpc::author::error::ErrorKind::InvalidFormat)?)
.map(|_| ())
.map_err(|_| substrate_rpc::author::error::ErrorKind::PoolError.into())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
} }
@@ -47,6 +47,21 @@ impl Slicable for Transaction {
} }
} }
/// Simple generic extrinsic type.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
pub struct Extrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
impl Slicable for Extrinsic {
fn decode<I: Input>(input: &mut I) -> Option<Self> {
Vec::<u8>::decode(input).map(Extrinsic)
}
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.0.using_encoded(f)
}
}
/// Execution log (event) /// Execution log (event)
#[derive(PartialEq, Eq, Clone, Default)] #[derive(PartialEq, Eq, Clone, Default)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
+3 -1
View File
@@ -26,11 +26,13 @@ extern crate jsonrpc_http_server as http;
use std::io; use std::io;
/// Construct rpc `IoHandler` /// Construct rpc `IoHandler`
pub fn rpc_handler<S>(state: S) -> rpc::IoHandler where pub fn rpc_handler<S, T>(state: S, transaction_pool: T) -> rpc::IoHandler where
S: apis::state::StateApi, S: apis::state::StateApi,
T: apis::author::AuthorApi,
{ {
let mut io = rpc::IoHandler::new(); let mut io = rpc::IoHandler::new();
io.extend_with(state.to_delegate()); io.extend_with(state.to_delegate());
io.extend_with(transaction_pool.to_delegate());
io io
} }
+1
View File
@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]
parking_lot = "0.4"
error-chain = "0.11" error-chain = "0.11"
jsonrpc-core = { git="https://github.com/paritytech/jsonrpc.git" } jsonrpc-core = { git="https://github.com/paritytech/jsonrpc.git" }
jsonrpc-macros = { git="https://github.com/paritytech/jsonrpc.git" } jsonrpc-macros = { git="https://github.com/paritytech/jsonrpc.git" }
@@ -0,0 +1,54 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
use client;
use rpc;
error_chain! {
links {
Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"];
}
errors {
/// Not implemented yet
Unimplemented {
description("not yet implemented"),
display("Method Not Implemented"),
}
/// Invalid format
InvalidFormat {
description("invalid format"),
display("Invalid format for the extrinsic data"),
}
/// Some error with the pool since the import failed.
PoolError {
description("pool import failed"),
display("Pool import failed"),
}
}
}
impl From<Error> for rpc::Error {
fn from(e: Error) -> Self {
match e {
Error(ErrorKind::Unimplemented, _) => rpc::Error {
code: rpc::ErrorCode::ServerError(-1),
message: "Not implemented yet".into(),
data: None,
},
_ => rpc::Error::internal_error(),
}
}
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate block-author/full-node API.
use std::sync::Arc;
use parking_lot::Mutex;
use primitives::block::Extrinsic;
pub mod error;
#[cfg(test)]
mod tests;
use self::error::Result;
build_rpc_trait! {
/// Substrate authoring RPC API
pub trait AuthorApi {
/// Submit extrinsic for inclusion in block.
#[rpc(name = "author_submitExtrinsic")]
fn submit_extrinsic(&self, Extrinsic) -> Result<()>;
}
}
/// Variant of the AuthorApi that doesn't need to be Sync + Send + 'static.
pub trait AsyncAuthorApi: Send + 'static {
/// Submit extrinsic for inclusion in block.
fn submit_extrinsic(&mut self, Extrinsic) -> Result<()>;
}
impl<T: AsyncAuthorApi> AuthorApi for Arc<Mutex<T>> {
fn submit_extrinsic(&self, xt: Extrinsic) -> Result<()> {
self.as_ref().lock().submit_extrinsic(xt)
}
}
@@ -0,0 +1,50 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
use primitives::block;
use substrate_executor as executor;
use super::*;
use super::error::*;
#[derive(Default)]
struct DummyTxPool {
submitted: Vec<block::Extrinsic>,
}
impl AsyncAuthorApi for DummyTxPool {
/// Submit extrinsic for inclusion in block.
fn submit_extrinsic(&mut self, xt: Extrinsic) -> Result<()> {
if self.submitted.len() < 1 {
self.submitted.push(xt);
Ok(())
} else {
Err(ErrorKind::PoolError.into())
}
}
}
#[test]
fn submit_transaction_should_not_cause_error() {
let mut p = Arc::new(Mutex::new(DummyTxPool::default()));
assert_matches!(
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])),
Ok(())
);
assert!(
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])).is_err()
);
}
+2
View File
@@ -18,6 +18,7 @@
#![warn(missing_docs)] #![warn(missing_docs)]
extern crate parking_lot;
extern crate jsonrpc_core as rpc; extern crate jsonrpc_core as rpc;
extern crate substrate_client as client; extern crate substrate_client as client;
extern crate substrate_primitives as primitives; extern crate substrate_primitives as primitives;
@@ -38,3 +39,4 @@ extern crate substrate_runtime_support as runtime_support;
pub mod chain; pub mod chain;
pub mod state; pub mod state;
pub mod author;