// Copyright 2019 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 .
//! RPC interface for the ManualSeal Engine.
use sp_consensus::ImportedAux;
use jsonrpc_core::Error;
use jsonrpc_derive::rpc;
use futures::{
channel::{mpsc, oneshot},
TryFutureExt,
FutureExt,
SinkExt
};
use serde::{Deserialize, Serialize};
use sp_runtime::Justification;
pub use self::gen_client::Client as ManualSealClient;
/// Future's type for jsonrpc
type FutureResult = Box + Send>;
/// sender passed to the authorship task to report errors or successes.
pub type Sender = Option>>;
/// Message sent to the background authorship task, usually by RPC.
pub enum EngineCommand {
/// Tells the engine to propose a new block
///
/// if create_empty == true, it will create empty blocks if there are no transactions
/// in the transaction pool.
///
/// if finalize == true, the block will be instantly finalized.
SealNewBlock {
/// if true, empty blocks(without extrinsics) will be created.
/// otherwise, will return Error::EmptyTransactionPool.
create_empty: bool,
/// instantly finalize this block?
finalize: bool,
/// specify the parent hash of the about-to-created block
parent_hash: Option,
/// sender to report errors/success to the rpc.
sender: Sender>,
},
/// Tells the engine to finalize the block with the supplied hash
FinalizeBlock {
/// hash of the block
hash: Hash,
/// sender to report errors/success to the rpc.
sender: Sender<()>,
/// finalization justification
justification: Option,
}
}
/// RPC trait that provides methods for interacting with the manual-seal authorship task over rpc.
#[rpc]
pub trait ManualSealApi {
/// Instructs the manual-seal authorship task to create a new block
#[rpc(name = "engine_createBlock")]
fn create_block(
&self,
create_empty: bool,
finalize: bool,
parent_hash: Option
) -> FutureResult>;
/// Instructs the manual-seal authorship task to finalize a block
#[rpc(name = "engine_finalizeBlock")]
fn finalize_block(
&self,
hash: Hash,
justification: Option
) -> FutureResult;
}
/// A struct that implements the [`ManualSealApi`].
pub struct ManualSeal {
import_block_channel: mpsc::Sender>,
}
/// return type of `engine_createBlock`
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct CreatedBlock {
/// hash of the created block.
pub hash: Hash,
/// some extra details about the import operation
pub aux: ImportedAux
}
impl ManualSeal {
/// Create new `ManualSeal` with the given reference to the client.
pub fn new(import_block_channel: mpsc::Sender>) -> Self {
Self { import_block_channel }
}
}
impl ManualSealApi for ManualSeal {
fn create_block(
&self,
create_empty: bool,
finalize: bool,
parent_hash: Option
) -> FutureResult> {
let mut sink = self.import_block_channel.clone();
let future = async move {
let (sender, receiver) = oneshot::channel();
let command = EngineCommand::SealNewBlock {
create_empty,
finalize,
parent_hash,
sender: Some(sender),
};
sink.send(command).await?;
receiver.await?
}.boxed();
Box::new(future.map_err(Error::from).compat())
}
fn finalize_block(&self, hash: Hash, justification: Option) -> FutureResult {
let mut sink = self.import_block_channel.clone();
let future = async move {
let (sender, receiver) = oneshot::channel();
sink.send(
EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification }
).await?;
receiver.await?.map(|_| true)
};
Box::new(future.boxed().map_err(Error::from).compat())
}
}
/// report any errors or successes encountered by the authorship task back
/// to the rpc
pub fn send_result(
sender: &mut Sender,
result: std::result::Result
) {
if let Some(sender) = sender.take() {
if let Err(err) = sender.send(result) {
log::warn!("Server is shutting down: {:?}", err)
}
} else {
// instant seal doesn't report errors over rpc, simply log them.
match result {
Ok(r) => log::info!("Instant Seal success: {:?}", r),
Err(e) => log::error!("Instant Seal encountered an error: {}", e)
}
}
}