referrerpolicy=no-referrer-when-downgrade

sc_consensus_manual_seal/
rpc.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! RPC interface for the `ManualSeal` Engine.
20
21use crate::error::Error;
22use futures::{
23	channel::{mpsc, oneshot},
24	SinkExt,
25};
26use jsonrpsee::{core::async_trait, proc_macros::rpc};
27use sc_consensus::ImportedAux;
28use serde::{Deserialize, Serialize};
29use sp_runtime::EncodedJustification;
30
31/// Sender passed to the authorship task to report errors or successes.
32pub type Sender<T> = Option<oneshot::Sender<std::result::Result<T, Error>>>;
33
34/// Message sent to the background authorship task, usually by RPC.
35pub enum EngineCommand<Hash> {
36	/// Tells the engine to propose a new block
37	///
38	/// if create_empty == true, it will create empty blocks if there are no transactions
39	/// in the transaction pool.
40	///
41	/// if finalize == true, the block will be instantly finalized.
42	SealNewBlock {
43		/// if true, empty blocks(without extrinsics) will be created.
44		/// otherwise, will return Error::EmptyTransactionPool.
45		create_empty: bool,
46		/// instantly finalize this block?
47		finalize: bool,
48		/// specify the parent hash of the about-to-created block
49		parent_hash: Option<Hash>,
50		/// sender to report errors/success to the rpc.
51		sender: Sender<CreatedBlock<Hash>>,
52	},
53	/// Tells the engine to finalize the block with the supplied hash
54	FinalizeBlock {
55		/// hash of the block
56		hash: Hash,
57		/// sender to report errors/success to the rpc.
58		sender: Sender<()>,
59		/// finalization justification
60		justification: Option<EncodedJustification>,
61	},
62}
63
64/// RPC trait that provides methods for interacting with the manual-seal authorship task over rpc.
65#[rpc(client, server)]
66pub trait ManualSealApi<Hash> {
67	/// Instructs the manual-seal authorship task to create a new block
68	#[method(name = "engine_createBlock")]
69	async fn create_block(
70		&self,
71		create_empty: bool,
72		finalize: bool,
73		parent_hash: Option<Hash>,
74	) -> Result<CreatedBlock<Hash>, Error>;
75
76	/// Instructs the manual-seal authorship task to finalize a block
77	#[method(name = "engine_finalizeBlock")]
78	async fn finalize_block(
79		&self,
80		hash: Hash,
81		justification: Option<EncodedJustification>,
82	) -> Result<bool, Error>;
83}
84
85/// A struct that implements the [`ManualSealApiServer`].
86pub struct ManualSeal<Hash> {
87	import_block_channel: mpsc::Sender<EngineCommand<Hash>>,
88}
89
90/// return type of `engine_createBlock`
91#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
92pub struct CreatedBlock<Hash> {
93	/// hash of the created block.
94	pub hash: Hash,
95	/// some extra details about the import operation
96	pub aux: ImportedAux,
97	/// uncompacted storage proof size (zero mean that there is no proof)
98	pub proof_size: usize,
99}
100
101impl<Hash> ManualSeal<Hash> {
102	/// Create new `ManualSeal` with the given reference to the client.
103	pub fn new(import_block_channel: mpsc::Sender<EngineCommand<Hash>>) -> Self {
104		Self { import_block_channel }
105	}
106}
107
108#[async_trait]
109impl<Hash: Send + 'static> ManualSealApiServer<Hash> for ManualSeal<Hash> {
110	async fn create_block(
111		&self,
112		create_empty: bool,
113		finalize: bool,
114		parent_hash: Option<Hash>,
115	) -> Result<CreatedBlock<Hash>, Error> {
116		let mut sink = self.import_block_channel.clone();
117		let (sender, receiver) = oneshot::channel();
118		// NOTE: this sends a Result over the channel.
119		let command = EngineCommand::SealNewBlock {
120			create_empty,
121			finalize,
122			parent_hash,
123			sender: Some(sender),
124		};
125
126		sink.send(command).await?;
127
128		match receiver.await {
129			Ok(Ok(rx)) => Ok(rx),
130			Ok(Err(e)) => Err(e.into()),
131			Err(e) => Err(e.into()),
132		}
133	}
134
135	async fn finalize_block(
136		&self,
137		hash: Hash,
138		justification: Option<EncodedJustification>,
139	) -> Result<bool, Error> {
140		let mut sink = self.import_block_channel.clone();
141		let (sender, receiver) = oneshot::channel();
142		let command = EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification };
143		sink.send(command).await?;
144		receiver.await.map(|_| true).map_err(Into::into)
145	}
146}
147
148/// report any errors or successes encountered by the authorship task back
149/// to the rpc
150pub fn send_result<T: std::fmt::Debug>(
151	sender: &mut Sender<T>,
152	result: std::result::Result<T, crate::Error>,
153) {
154	if let Some(sender) = sender.take() {
155		if let Err(err) = sender.send(result) {
156			match err {
157				Ok(value) => log::warn!("Server is shutting down: {:?}", value),
158				Err(error) => log::warn!("Server is shutting down with error: {}", error),
159			}
160		}
161	} else {
162		// Sealing/Finalization with no RPC sender such as instant seal or delayed finalize doesn't
163		// report errors over rpc, simply log them.
164		match result {
165			Ok(r) => log::info!("Consensus with no RPC sender success: {:?}", r),
166			Err(e) => log::error!("Consensus with no RPC sender encountered an error: {}", e),
167		}
168	}
169}