1#[cfg(test)]
18mod tests;
19
20use crate::mmr::sync::decode_mmr_data;
21use crate::mmr::{get_offchain_key, get_temp_key};
22use futures::channel::oneshot;
23use futures::stream::StreamExt;
24use parity_scale_codec::{Decode, Encode};
25use sc_client_api::{BlockBackend, ProofProvider};
26use sc_network::config::ProtocolId;
27use sc_network::request_responses::{IncomingRequest, OutgoingResponse};
28use sc_network::{NetworkBackend, PeerId};
29use schnellru::{ByLength, LruMap};
30use sp_blockchain::HeaderBackend;
31use sp_core::offchain::storage::OffchainDb;
32use sp_core::offchain::{DbExternalities, OffchainStorage, StorageKind};
33use sp_mmr_primitives::utils::NodesUtils;
34use sp_runtime::traits::Block as BlockT;
35use std::collections::BTreeMap;
36use std::marker::PhantomData;
37use std::sync::Arc;
38use std::time::Duration;
39use subspace_core_primitives::BlockNumber;
40use subspace_runtime_primitives::BlockHashFor;
41use tracing::{debug, error, trace};
42
43const MAX_NUMBER_OF_SAME_REQUESTS_PER_PEER: usize = 2;
44
45pub const MAX_MMR_ITEMS: u32 = 20000;
47
48mod rep {
49 use sc_network::ReputationChange as Rep;
50
51 pub const SAME_REQUEST: Rep = Rep::new(i32::MIN, "Same state request multiple times");
53}
54
55pub fn generate_protocol_config<Hash: AsRef<[u8]>, B: BlockT, N: NetworkBackend<B, B::Hash>>(
58 _: &ProtocolId,
59 genesis_hash: Hash,
60 fork_id: Option<&str>,
61 inbound_queue: async_channel::Sender<IncomingRequest>,
62) -> N::RequestResponseProtocolConfig {
63 N::request_response_config(
64 generate_protocol_name(genesis_hash, fork_id).into(),
65 Vec::new(),
66 1024 * 1024,
67 16 * 1024 * 1024,
68 Duration::from_secs(40),
69 Some(inbound_queue),
70 )
71}
72
73pub fn generate_protocol_name<Hash: AsRef<[u8]>>(
75 genesis_hash: Hash,
76 fork_id: Option<&str>,
77) -> String {
78 let genesis_hash = genesis_hash.as_ref();
79 if let Some(fork_id) = fork_id {
80 format!("/{}/{}/mmr/1", hex::encode(genesis_hash), fork_id)
81 } else {
82 format!("/{}/mmr/1", hex::encode(genesis_hash))
83 }
84}
85
86fn leaf_index_that_added_node(position: BlockNumber) -> BlockNumber {
87 NodesUtils::leaf_index_that_added_node(position.into())
88 .try_into()
89 .expect("Always its into a block number; qed")
90}
91
92#[derive(Eq, PartialEq, Clone, Hash)]
94struct SeenRequestsKey {
95 peer: PeerId,
96 starting_position: u32,
97}
98
99#[derive(Clone, PartialEq, Encode, Decode, Debug)]
101pub struct MmrRequest {
102 pub starting_position: u32,
104 pub limit: u32,
106}
107
108#[derive(Clone, PartialEq, Encode, Decode, Debug)]
109pub struct MmrResponse {
110 pub mmr_data: BTreeMap<u32, Vec<u8>>,
112}
113
114enum SeenRequestsValue {
116 First,
118 Fulfilled(usize),
120}
121
122pub struct MmrRequestHandler<Block, OS, Client>
124where
125 Block: BlockT,
126{
127 request_receiver: async_channel::Receiver<IncomingRequest>,
128 seen_requests: LruMap<SeenRequestsKey, SeenRequestsValue>,
132
133 offchain_db: OffchainDb<OS>,
134
135 client: Arc<Client>,
136
137 _phantom: PhantomData<Block>,
138}
139
140impl<Block, OS, Client> MmrRequestHandler<Block, OS, Client>
141where
142 Block: BlockT<Hash = sp_core::H256>,
143 Client:
144 HeaderBackend<Block> + BlockBackend<Block> + ProofProvider<Block> + Send + Sync + 'static,
145 OS: OffchainStorage,
146{
147 pub fn new<NB>(
149 protocol_id: &ProtocolId,
150 fork_id: Option<&str>,
151 client: Arc<Client>,
152 num_peer_hint: usize,
153 offchain_storage: OS,
154 ) -> (Self, NB::RequestResponseProtocolConfig)
155 where
156 NB: NetworkBackend<Block, BlockHashFor<Block>>,
157 {
158 let capacity = std::cmp::max(num_peer_hint, 1);
161 let (tx, request_receiver) = async_channel::bounded(capacity);
162
163 let protocol_config = generate_protocol_config::<_, Block, NB>(
164 protocol_id,
165 client
166 .block_hash(0u32.into())
167 .ok()
168 .flatten()
169 .expect("Genesis block exists; qed"),
170 fork_id,
171 tx,
172 );
173
174 let capacity = ByLength::new(num_peer_hint.max(1) as u32 * 2);
175 let seen_requests = LruMap::new(capacity);
176
177 (
178 Self {
179 client,
180 request_receiver,
181 seen_requests,
182 offchain_db: OffchainDb::new(offchain_storage),
183 _phantom: PhantomData,
184 },
185 protocol_config,
186 )
187 }
188
189 pub async fn run(mut self) {
191 while let Some(request) = self.request_receiver.next().await {
192 let IncomingRequest {
193 peer,
194 payload,
195 pending_response,
196 } = request;
197
198 match self.handle_request(payload, pending_response, &peer) {
199 Ok(()) => debug!("Handled MMR request from {}.", peer),
200 Err(e) => {
201 error!("Failed to handle MMR request from {}: {}", peer, e,)
202 }
203 }
204 }
205 }
206
207 fn handle_request(
208 &mut self,
209 payload: Vec<u8>,
210 pending_response: oneshot::Sender<OutgoingResponse>,
211 peer: &PeerId,
212 ) -> Result<(), HandleRequestError> {
213 let request = MmrRequest::decode(&mut payload.as_slice())?;
214
215 let key = SeenRequestsKey {
216 peer: *peer,
217 starting_position: request.starting_position,
218 };
219
220 let mut reputation_changes = Vec::new();
221
222 match self.seen_requests.get(&key) {
223 Some(SeenRequestsValue::First) => {}
224 Some(SeenRequestsValue::Fulfilled(requests)) => {
225 *requests = requests.saturating_add(1);
226
227 if *requests > MAX_NUMBER_OF_SAME_REQUESTS_PER_PEER {
228 reputation_changes.push(rep::SAME_REQUEST);
229 }
230 }
231 None => {
232 self.seen_requests
233 .insert(key.clone(), SeenRequestsValue::First);
234 }
235 }
236
237 trace!("Handle MMR request: {peer}, request: {request:?}",);
238
239 let result = if request.limit > MAX_MMR_ITEMS {
240 error!(
241 "Invalid MMR request from peer={peer}: {:?}",
242 HandleRequestError::MaxItemsLimitExceeded
243 );
244
245 Err(())
246 } else {
247 let mut mmr_data = BTreeMap::new();
248 for position in request.starting_position..(request.starting_position + request.limit) {
249 let canon_key = get_offchain_key(position.into());
250 let storage_value = self
251 .offchain_db
252 .local_storage_get(StorageKind::PERSISTENT, &canon_key);
253
254 let block_number = leaf_index_that_added_node(position);
255 trace!( %position, %block_number, "Storage data present: {}", storage_value.is_some());
256
257 if let Some(storage_value) = storage_value {
258 mmr_data.insert(position, storage_value);
259 } else {
260 if let Ok(Some(hash)) = self.client.hash(block_number.into()) {
261 let temp_key = get_temp_key(position.into(), hash);
262 let storage_value = self
263 .offchain_db
264 .local_storage_get(StorageKind::PERSISTENT, &temp_key);
265
266 if let Some(storage_value) = storage_value {
267 let data = decode_mmr_data(&storage_value);
268 trace!( %position, %block_number,"MMR node: {data:?}");
269 mmr_data.insert(position, storage_value);
270 continue;
271 } else {
272 debug!( %position, %block_number, ?hash, "Didn't find value in storage.")
273 }
274 } else {
275 debug!( %position, %block_number, "Didn't find hash.")
276 }
277 break; }
279 }
280
281 if let Some(value) = self.seen_requests.get(&key) {
282 if let SeenRequestsValue::First = value {
285 *value = SeenRequestsValue::Fulfilled(1);
286 }
287 }
288
289 let response = MmrResponse { mmr_data };
290
291 Ok(response.encode())
292 };
293
294 pending_response
295 .send(OutgoingResponse {
296 result,
297 reputation_changes,
298 sent_feedback: None,
299 })
300 .map_err(|_| HandleRequestError::SendResponse)
301 }
302}
303
304#[derive(Debug, thiserror::Error)]
305enum HandleRequestError {
306 #[error("Invalid request: max MMR nodes limit exceeded.")]
307 MaxItemsLimitExceeded,
308
309 #[error(transparent)]
310 Client(#[from] sp_blockchain::Error),
311
312 #[error("Failed to send response.")]
313 SendResponse,
314
315 #[error("Failed to decode request: {0}.")]
316 Decode(#[from] parity_scale_codec::Error),
317}