sc_domains/domain_block_er/
execution_receipt_protocol.rs
1use domain_runtime_primitives::Balance;
18use futures::channel::oneshot;
19use futures::stream::StreamExt;
20use parity_scale_codec::{Decode, Encode};
21use sc_client_api::BlockBackend;
22use sc_network::request_responses::{IncomingRequest, OutgoingResponse};
23use sc_network::{NetworkBackend, PeerId};
24use sp_api::ProvideRuntimeApi;
25use sp_blockchain::HeaderBackend;
26use sp_domains::{DomainId, DomainsApi, ExecutionReceiptFor};
27use sp_runtime::traits::{Block as BlockT, Header};
28use std::marker::PhantomData;
29use std::sync::Arc;
30use std::time::Duration;
31use tracing::{debug, error, trace};
32
33pub(crate) const LOG_TARGET: &str = "domain_block_er_protocol";
34
35pub fn generate_protocol_config<Hash: AsRef<[u8]>, B: BlockT, N: NetworkBackend<B, B::Hash>>(
37 genesis_hash: Hash,
38 fork_id: Option<&str>,
39 inbound_queue: async_channel::Sender<IncomingRequest>,
40) -> N::RequestResponseProtocolConfig {
41 N::request_response_config(
42 generate_protocol_name(genesis_hash, fork_id).into(),
43 Vec::new(),
44 1024 * 1024,
45 16 * 1024 * 1024,
46 Duration::from_secs(40),
47 Some(inbound_queue),
48 )
49}
50
51pub fn generate_protocol_name<Hash: AsRef<[u8]>>(
53 genesis_hash: Hash,
54 fork_id: Option<&str>,
55) -> String {
56 let genesis_hash = genesis_hash.as_ref();
57 if let Some(fork_id) = fork_id {
58 format!(
59 "/{}/{}/last-confirmed-domain-block-receipt/1",
60 array_bytes::bytes2hex("", genesis_hash),
61 fork_id
62 )
63 } else {
64 format!(
65 "/{}/last-confirmed-domain-block-receipt/1",
66 array_bytes::bytes2hex("", genesis_hash)
67 )
68 }
69}
70
71#[derive(Clone, PartialEq, Encode, Decode, Debug)]
73pub enum DomainBlockERRequest {
74 LastConfirmedER(DomainId),
76}
77
78#[derive(Clone, PartialEq, Encode, Decode, Debug)]
80pub enum DomainBlockERResponse<CBlock: BlockT, DomainHeader: Header> {
81 LastConfirmedER(ExecutionReceiptFor<DomainHeader, CBlock, Balance>),
83}
84
85pub struct DomainBlockERRequestHandler<CBlock, Block, CClient>
87where
88 CBlock: BlockT,
89 Block: BlockT,
90{
91 consensus_client: Arc<CClient>,
92 request_receiver: async_channel::Receiver<IncomingRequest>,
93 _phantom: PhantomData<(CBlock, Block)>,
94}
95
96impl<CBlock, Block, CClient> DomainBlockERRequestHandler<CBlock, Block, CClient>
97where
98 CBlock: BlockT,
99 Block: BlockT,
100 CClient: ProvideRuntimeApi<CBlock>
101 + BlockBackend<CBlock>
102 + HeaderBackend<CBlock>
103 + Send
104 + Sync
105 + 'static,
106 CClient::Api: DomainsApi<CBlock, Block::Header>,
107{
108 pub fn new<NB>(
110 fork_id: Option<&str>,
111 consensus_client: Arc<CClient>,
112 num_peer_hint: usize,
113 ) -> (Self, NB::RequestResponseProtocolConfig)
114 where
115 NB: NetworkBackend<Block, Block::Hash>,
116 {
117 let capacity = std::cmp::max(num_peer_hint, 1);
120 let (tx, request_receiver) = async_channel::bounded(capacity);
121
122 let protocol_config = generate_protocol_config::<_, Block, NB>(
123 consensus_client
124 .block_hash(0u32.into())
125 .ok()
126 .flatten()
127 .expect("Genesis block exists; qed"),
128 fork_id,
129 tx,
130 );
131
132 (
133 Self {
134 request_receiver,
135 consensus_client,
136 _phantom: PhantomData,
137 },
138 protocol_config,
139 )
140 }
141
142 pub async fn run(mut self) {
144 while let Some(request) = self.request_receiver.next().await {
145 let IncomingRequest {
146 peer,
147 payload,
148 pending_response,
149 } = request;
150
151 match self.handle_request(payload, pending_response, &peer) {
152 Ok(()) => {
153 debug!(target: LOG_TARGET, "Handled domain block ER request from {}.", peer)
154 }
155 Err(e) => error!(
156 target: LOG_TARGET,
157 "Failed to handle domain block ER request from {}: {}",
158 peer, e,
159 ),
160 }
161 }
162 }
163
164 fn handle_request(
165 &self,
166 payload: Vec<u8>,
167 pending_response: oneshot::Sender<OutgoingResponse>,
168 peer: &PeerId,
169 ) -> Result<(), HandleRequestError> {
170 let request = DomainBlockERRequest::decode(&mut payload.as_slice())?;
171
172 trace!(target: LOG_TARGET, "Handle domain block ER request: {peer}, request: {request:?}",);
173
174 let result = {
175 let DomainBlockERRequest::LastConfirmedER(domain_id) = request;
176 let response = DomainBlockERResponse::<CBlock, Block::Header>::LastConfirmedER(
177 self.get_execution_receipts(domain_id)?,
178 );
179 Ok(response.encode())
180 };
181
182 pending_response
183 .send(OutgoingResponse {
184 result,
185 reputation_changes: Vec::new(),
186 sent_feedback: None,
187 })
188 .map_err(|_| HandleRequestError::SendResponse)
189 }
190
191 fn get_execution_receipts(
192 &self,
193 domain_id: DomainId,
194 ) -> Result<ExecutionReceiptFor<Block::Header, CBlock, Balance>, HandleRequestError> {
195 let best_consensus_hash = self.consensus_client.info().best_hash;
196
197 let last_confirmed_block_receipt = self
199 .consensus_client
200 .runtime_api()
201 .last_confirmed_domain_block_receipt(best_consensus_hash, domain_id);
202
203 let last_confirmed_block_receipt = match last_confirmed_block_receipt {
204 Ok(Some(last_confirmed_block_receipt)) => last_confirmed_block_receipt,
205 Ok(None) => {
206 debug!(
207 target: LOG_TARGET,
208 %domain_id,
209 %best_consensus_hash,
210 "Last confirmed domain block ER acquisition failed: no data.",
211 );
212
213 return Err(HandleRequestError::AbsentLastConfirmedDomainBlockData);
214 }
215 Err(err) => {
216 debug!(
217 target: LOG_TARGET,
218 %domain_id,
219 %best_consensus_hash,
220 ?err,
221 "Last confirmed domain block ER acquisition failed.",
222 );
223
224 return Err(HandleRequestError::LastConfirmedDomainDataAcquisitionFailed(err));
225 }
226 };
227
228 debug!(
229 target: LOG_TARGET,
230 ?last_confirmed_block_receipt,
231 "Last confirmed domain block receipt."
232 );
233
234 Ok(last_confirmed_block_receipt)
235 }
236}
237
238#[derive(Debug, thiserror::Error)]
239enum HandleRequestError {
240 #[error(transparent)]
241 Client(#[from] sp_blockchain::Error),
242
243 #[error("Failed to send response.")]
244 SendResponse,
245
246 #[error("Failed to decode request: {0}.")]
247 Decode(#[from] parity_scale_codec::Error),
248
249 #[error("Last confirmed domain block acquisition failed: no data.")]
250 AbsentLastConfirmedDomainBlockData,
251
252 #[error("Last confirmed domain block acquisition failed: no data.")]
253 LastConfirmedDomainDataAcquisitionFailed(#[from] sp_api::ApiError),
254}