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