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