sc_subspace_block_relay/consensus/
types.rs

1//! Consensus related types.
2//!
3//! The relay protocol needs to expose these data types to be included
4//! in the top level data types:
5//! 1. The request/response types to be included in
6//!    [`ProtocolInitialRequest`]/[`ProtocolInitialResponse`].
7//! 2. The handshake message to be included in `ProtocolMessage`.
8//!    The corresponding handshake response is not included in the
9//!    top level messages, and is private to the protocol
10//!    implementation.
11
12use crate::protocol::compact_block::{
13    CompactBlockHandshake, CompactBlockInitialRequest, CompactBlockInitialResponse,
14};
15use crate::types::RelayError;
16use crate::utils::{RelayCounter, RelayCounterVec};
17use derive_more::From;
18use parity_scale_codec::{Decode, Encode};
19use sc_network_common::sync::message::{BlockAttributes, BlockData, BlockRequest};
20use sp_runtime::generic::BlockId;
21use sp_runtime::traits::{Block as BlockT, NumberFor};
22use sp_runtime::Justifications;
23use subspace_runtime_primitives::{BlockHashFor, ExtrinsicFor, HeaderFor};
24use substrate_prometheus_endpoint::{PrometheusError, Registry};
25
26const STATUS_LABEL: &str = "status";
27const STATUS_SUCCESS: &str = "success";
28
29const DOWNLOAD_LABEL: &str = "client_download";
30const DOWNLOAD_BLOCKS: &str = "blocks";
31const DOWNLOAD_BYTES: &str = "bytes";
32
33/// Client -> server request.
34#[derive(From, Encode, Decode)]
35pub(crate) enum ConsensusRequest<Block: BlockT, TxHash> {
36    /// Initial request for block download. This may
37    /// result in partial blocks returned by the server,
38    /// with tx hash instead of the full transaction.
39    #[codec(index = 0)]
40    BlockDownloadV0(InitialRequest<Block>),
41
42    /// Protocol specific hand shake messages, following the
43    /// initial `BlockDownload` message (e.g) to resolve
44    /// tx pool misses.
45    #[codec(index = 1)]
46    ProtocolMessageV0(ProtocolMessage<Block, TxHash>),
47
48    /// Request for full block download, without going through
49    /// the relay protocols. This is used in scenarios like a node
50    /// doing initial sync.
51    /// TODO: versioning for substrate data types
52    #[codec(index = 2)]
53    FullBlockDownloadV0(FullDownloadRequest<Block>),
54    // Next version/variant goes here:
55    // #[codec(index = 3)]
56}
57
58/// Initial request for a single block.
59#[derive(Encode, Decode)]
60pub(crate) struct InitialRequest<Block: BlockT> {
61    /// Starting block.
62    pub(crate) from_block: BlockId<Block>,
63
64    /// Requested block components.
65    pub(crate) block_attributes: BlockAttributes,
66
67    /// The protocol specific part of the initial request.
68    pub(crate) protocol_request: ProtocolInitialRequest,
69}
70
71/// Initial response for a single block.
72#[derive(Encode, Decode)]
73pub(crate) struct InitialResponse<Block: BlockT, TxHash> {
74    ///  Hash of the block being downloaded.
75    pub(crate) block_hash: BlockHashFor<Block>,
76
77    /// The partial block, without the extrinsics.
78    pub(crate) partial_block: PartialBlock<Block>,
79
80    /// The opaque protocol specific part of the response.
81    /// This is optional because BlockAttributes::BODY may not be set in
82    /// the BlockRequest, in which case we don't need to fetch the
83    /// extrinsics.
84    pub(crate) protocol_response: Option<ProtocolInitialResponse<Block, TxHash>>,
85}
86
87/// Protocol specific part of the initial request.
88#[derive(From, Encode, Decode)]
89pub(crate) enum ProtocolInitialRequest {
90    #[codec(index = 0)]
91    CompactBlock(CompactBlockInitialRequest),
92    // New protocol goes here:
93    // #[codec(index = 1)]
94}
95
96/// Protocol specific part of the initial response.
97#[derive(From, Encode, Decode)]
98pub(crate) enum ProtocolInitialResponse<Block: BlockT, TxHash> {
99    #[codec(index = 0)]
100    CompactBlock(CompactBlockInitialResponse<BlockHashFor<Block>, TxHash, ExtrinsicFor<Block>>),
101    // New protocol goes here:
102    // #[codec(index = 1)]
103}
104
105/// Protocol specific handshake requests.
106#[derive(From, Encode, Decode)]
107pub(crate) enum ProtocolMessage<Block: BlockT, TxHash> {
108    #[codec(index = 0)]
109    CompactBlock(CompactBlockHandshake<BlockHashFor<Block>, TxHash>),
110    // New protocol goes here:
111    // #[codec(index = 1)]
112}
113
114impl<Block: BlockT, TxHash> From<CompactBlockHandshake<BlockHashFor<Block>, TxHash>>
115    for ConsensusRequest<Block, TxHash>
116{
117    fn from(
118        inner: CompactBlockHandshake<BlockHashFor<Block>, TxHash>,
119    ) -> ConsensusRequest<Block, TxHash> {
120        ConsensusRequest::ProtocolMessageV0(ProtocolMessage::CompactBlock(inner))
121    }
122}
123
124/// Request to download multiple/complete blocks, for scenarios like the
125/// sync phase. This does not involve the protocol optimizations, as the
126/// requesting node is just coming up and may not have the transactions
127/// in its pool. So it does not make sense to send response with tx hash,
128/// and the full blocks are sent back. This behaves like the default
129/// substrate block downloader.
130#[derive(Encode, Decode)]
131pub(crate) struct FullDownloadRequest<Block: BlockT>(pub(crate) BlockRequest<Block>);
132
133/// Response to the full download request.
134#[derive(Encode, Decode)]
135pub(crate) struct FullDownloadResponse<Block: BlockT>(pub(crate) Vec<BlockData<Block>>);
136
137/// The partial block response from the server. It has all the fields
138/// except the extrinsics(and some other meta info). The extrinsics
139/// are handled by the protocol.
140#[derive(Encode, Decode)]
141pub(crate) struct PartialBlock<Block: BlockT> {
142    pub(crate) parent_hash: BlockHashFor<Block>,
143    pub(crate) block_number: NumberFor<Block>,
144    pub(crate) block_header_hash: BlockHashFor<Block>,
145    pub(crate) header: Option<HeaderFor<Block>>,
146    pub(crate) indexed_body: Option<Vec<Vec<u8>>>,
147    pub(crate) justifications: Option<Justifications>,
148}
149
150impl<Block: BlockT> PartialBlock<Block> {
151    /// Builds the full block data.
152    pub(crate) fn block_data(self, body: Option<Vec<ExtrinsicFor<Block>>>) -> BlockData<Block> {
153        BlockData::<Block> {
154            hash: self.block_header_hash,
155            header: self.header,
156            body,
157            indexed_body: self.indexed_body,
158            receipt: None,
159            message_queue: None,
160            justification: None,
161            justifications: self.justifications,
162        }
163    }
164}
165
166/// Client side metrics.
167pub(crate) struct ConsensusClientMetrics {
168    pub(crate) requests: RelayCounterVec,
169    pub(crate) downloads: RelayCounterVec,
170    pub(crate) tx_pool_miss: RelayCounter,
171}
172
173impl ConsensusClientMetrics {
174    pub(crate) fn new(registry: Option<&Registry>) -> Result<Self, PrometheusError> {
175        Ok(Self {
176            requests: RelayCounterVec::new(
177                "relay_client_requests",
178                "Relay client request metrics(by completion status)",
179                &[STATUS_LABEL],
180                registry,
181            )?,
182            downloads: RelayCounterVec::new(
183                "relay_client_downloads",
184                "Relay client download metrics",
185                &[DOWNLOAD_LABEL],
186                registry,
187            )?,
188            tx_pool_miss: RelayCounter::new(
189                "relay_client_tx_pool_miss",
190                "Number of extrinsics not found in the tx pool",
191                registry,
192            )?,
193        })
194    }
195
196    /// Updates the metrics on successful download completion.
197    pub(crate) fn on_download<Block: BlockT>(&self, blocks: &[BlockData<Block>]) {
198        self.requests.inc(STATUS_LABEL, STATUS_SUCCESS);
199        if let Ok(blocks) = u64::try_from(blocks.len()) {
200            self.downloads
201                .inc_by(DOWNLOAD_LABEL, DOWNLOAD_BLOCKS, blocks);
202        }
203        if let Ok(bytes) = u64::try_from(blocks.encoded_size()) {
204            self.downloads.inc_by(DOWNLOAD_LABEL, DOWNLOAD_BYTES, bytes);
205        }
206    }
207
208    /// Updates the metrics on failed download.
209    pub(crate) fn on_download_fail(&self, err: &RelayError) {
210        self.requests.inc(STATUS_LABEL, err.as_ref());
211    }
212}
213
214/// Server side metrics.
215pub(crate) struct ConsensusServerMetrics {
216    pub(crate) requests: RelayCounterVec,
217}
218
219impl ConsensusServerMetrics {
220    pub(crate) fn new(registry: Option<&Registry>) -> Result<Self, PrometheusError> {
221        Ok(Self {
222            requests: RelayCounterVec::new(
223                "relay_server_status",
224                "Relay server request metrics(by status)",
225                &[STATUS_LABEL],
226                registry,
227            )?,
228        })
229    }
230
231    /// Updates the metrics on a successful request.
232    pub(crate) fn on_request(&self) {
233        self.requests.inc(STATUS_LABEL, STATUS_SUCCESS);
234    }
235
236    /// Updates the metrics on a failed request.
237    pub(crate) fn on_failed_request(&self, err: &RelayError) {
238        self.requests.inc(STATUS_LABEL, err.as_ref());
239    }
240}