// 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) } } }