1use crate::{
24 offchain::{
25 self, storage::InMemOffchainStorage, HttpError, HttpRequestId as RequestId,
26 HttpRequestStatus as RequestStatus, OffchainOverlayedChange, OffchainStorage,
27 OpaqueNetworkState, StorageKind, Timestamp, TransactionPool,
28 },
29 OpaquePeerId,
30};
31use std::{
32 collections::{BTreeMap, VecDeque},
33 sync::Arc,
34};
35
36use parking_lot::RwLock;
37
38#[derive(Debug, Default, PartialEq, Eq)]
40pub struct PendingRequest {
41 pub method: String,
43 pub uri: String,
45 pub meta: Vec<u8>,
47 pub headers: Vec<(String, String)>,
49 pub body: Vec<u8>,
51 pub sent: bool,
53 pub response: Option<Vec<u8>>,
55 pub read: usize,
57 pub response_headers: Vec<(String, String)>,
59}
60
61#[derive(Debug, Clone, Default)]
63pub struct TestPersistentOffchainDB {
64 persistent: Arc<RwLock<InMemOffchainStorage>>,
65}
66
67impl TestPersistentOffchainDB {
68 const PREFIX: &'static [u8] = b"";
69
70 pub fn new() -> Self {
72 Self { persistent: Arc::new(RwLock::new(InMemOffchainStorage::default())) }
73 }
74
75 pub fn apply_offchain_changes(
77 &mut self,
78 changes: impl Iterator<Item = ((Vec<u8>, Vec<u8>), OffchainOverlayedChange)>,
79 ) {
80 let mut me = self.persistent.write();
81 for ((_prefix, key), value_operation) in changes {
82 match value_operation {
83 OffchainOverlayedChange::SetValue(val) => {
84 me.set(Self::PREFIX, key.as_slice(), val.as_slice())
85 },
86 OffchainOverlayedChange::Remove => me.remove(Self::PREFIX, key.as_slice()),
87 }
88 }
89 }
90
91 pub fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
93 OffchainStorage::get(self, Self::PREFIX, key)
94 }
95}
96
97impl OffchainStorage for TestPersistentOffchainDB {
98 fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) {
99 self.persistent.write().set(prefix, key, value);
100 }
101
102 fn remove(&mut self, prefix: &[u8], key: &[u8]) {
103 self.persistent.write().remove(prefix, key);
104 }
105
106 fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>> {
107 self.persistent.read().get(prefix, key)
108 }
109
110 fn compare_and_set(
111 &mut self,
112 prefix: &[u8],
113 key: &[u8],
114 old_value: Option<&[u8]>,
115 new_value: &[u8],
116 ) -> bool {
117 self.persistent.write().compare_and_set(prefix, key, old_value, new_value)
118 }
119}
120
121#[derive(Debug, Default)]
125pub struct OffchainState {
126 pub requests: BTreeMap<RequestId, PendingRequest>,
128 expected_requests: VecDeque<PendingRequest>,
130 pub persistent_storage: TestPersistentOffchainDB,
132 pub local_storage: InMemOffchainStorage,
134 pub seed: [u8; 32],
136 pub timestamp: Timestamp,
138}
139
140impl OffchainState {
141 pub fn fulfill_pending_request(
143 &mut self,
144 id: u16,
145 expected: PendingRequest,
146 response: impl Into<Vec<u8>>,
147 response_headers: impl IntoIterator<Item = (String, String)>,
148 ) {
149 match self.requests.get_mut(&RequestId(id)) {
150 None => {
151 panic!("Missing pending request: {:?}.\n\nAll: {:?}", id, self.requests);
152 },
153 Some(req) => {
154 assert_eq!(*req, expected);
155 req.response = Some(response.into());
156 req.response_headers = response_headers.into_iter().collect();
157 },
158 }
159 }
160
161 fn fulfill_expected(&mut self, id: u16) {
162 if let Some(mut req) = self.expected_requests.pop_back() {
163 let response = req.response.take().expect("Response checked when added.");
164 let headers = std::mem::take(&mut req.response_headers);
165 self.fulfill_pending_request(id, req, response, headers);
166 }
167 }
168
169 pub fn expect_request(&mut self, expected: PendingRequest) {
177 if expected.response.is_none() {
178 panic!("Expected request needs to have a response.");
179 }
180 self.expected_requests.push_front(expected);
181 }
182}
183
184impl Drop for OffchainState {
185 fn drop(&mut self) {
186 if !self.expected_requests.is_empty() && !std::thread::panicking() {
188 panic!("Unfulfilled expected requests: {:?}", self.expected_requests);
189 }
190 }
191}
192
193#[derive(Clone, Default, Debug)]
195pub struct TestOffchainExt(pub Arc<RwLock<OffchainState>>);
196
197impl TestOffchainExt {
198 pub fn new() -> (Self, Arc<RwLock<OffchainState>>) {
200 let ext = Self::default();
201 let state = ext.0.clone();
202 (ext, state)
203 }
204
205 pub fn with_offchain_db(
207 offchain_db: TestPersistentOffchainDB,
208 ) -> (Self, Arc<RwLock<OffchainState>>) {
209 let (ext, state) = Self::new();
210 ext.0.write().persistent_storage = offchain_db;
211 (ext, state)
212 }
213}
214
215impl offchain::Externalities for TestOffchainExt {
216 fn is_validator(&self) -> bool {
217 true
218 }
219
220 fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
221 Ok(OpaqueNetworkState { peer_id: Default::default(), external_addresses: vec![] })
222 }
223
224 fn timestamp(&mut self) -> Timestamp {
225 self.0.read().timestamp
226 }
227
228 fn sleep_until(&mut self, deadline: Timestamp) {
229 self.0.write().timestamp = deadline;
230 }
231
232 fn random_seed(&mut self) -> [u8; 32] {
233 self.0.read().seed
234 }
235
236 fn http_request_start(
237 &mut self,
238 method: &str,
239 uri: &str,
240 meta: &[u8],
241 ) -> Result<RequestId, ()> {
242 let mut state = self.0.write();
243 let id = RequestId(state.requests.len() as u16);
244 state.requests.insert(
245 id,
246 PendingRequest {
247 method: method.into(),
248 uri: uri.into(),
249 meta: meta.into(),
250 ..Default::default()
251 },
252 );
253 Ok(id)
254 }
255
256 fn http_request_add_header(
257 &mut self,
258 request_id: RequestId,
259 name: &str,
260 value: &str,
261 ) -> Result<(), ()> {
262 let mut state = self.0.write();
263 if let Some(req) = state.requests.get_mut(&request_id) {
264 req.headers.push((name.into(), value.into()));
265 Ok(())
266 } else {
267 Err(())
268 }
269 }
270
271 fn http_request_write_body(
272 &mut self,
273 request_id: RequestId,
274 chunk: &[u8],
275 _deadline: Option<Timestamp>,
276 ) -> Result<(), HttpError> {
277 let mut state = self.0.write();
278
279 let sent = {
280 let req = state.requests.get_mut(&request_id).ok_or(HttpError::IoError)?;
281 req.body.extend(chunk);
282 if chunk.is_empty() {
283 req.sent = true;
284 }
285 req.sent
286 };
287
288 if sent {
289 state.fulfill_expected(request_id.0);
290 }
291
292 Ok(())
293 }
294
295 fn http_response_wait(
296 &mut self,
297 ids: &[RequestId],
298 _deadline: Option<Timestamp>,
299 ) -> Vec<RequestStatus> {
300 let state = self.0.read();
301
302 ids.iter()
303 .map(|id| match state.requests.get(id) {
304 Some(req) if req.response.is_none() => {
305 panic!("No `response` provided for request with id: {:?}", id)
306 },
307 None => RequestStatus::Invalid,
308 _ => RequestStatus::Finished(200),
309 })
310 .collect()
311 }
312
313 fn http_response_headers(&mut self, request_id: RequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
314 let state = self.0.read();
315 if let Some(req) = state.requests.get(&request_id) {
316 req.response_headers
317 .clone()
318 .into_iter()
319 .map(|(k, v)| (k.into_bytes(), v.into_bytes()))
320 .collect()
321 } else {
322 Default::default()
323 }
324 }
325
326 fn http_response_read_body(
327 &mut self,
328 request_id: RequestId,
329 buffer: &mut [u8],
330 _deadline: Option<Timestamp>,
331 ) -> Result<usize, HttpError> {
332 let mut state = self.0.write();
333 if let Some(req) = state.requests.get_mut(&request_id) {
334 let response = req
335 .response
336 .as_mut()
337 .unwrap_or_else(|| panic!("No response provided for request: {:?}", request_id));
338
339 if req.read >= response.len() {
340 state.requests.remove(&request_id);
342 Ok(0)
343 } else {
344 let read = std::cmp::min(buffer.len(), response[req.read..].len());
345 buffer[0..read].copy_from_slice(&response[req.read..req.read + read]);
346 req.read += read;
347 Ok(read)
348 }
349 } else {
350 Err(HttpError::IoError)
351 }
352 }
353
354 fn set_authorized_nodes(&mut self, _nodes: Vec<OpaquePeerId>, _authorized_only: bool) {
355 unimplemented!()
356 }
357}
358
359impl offchain::DbExternalities for TestOffchainExt {
360 fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
361 let mut state = self.0.write();
362 match kind {
363 StorageKind::LOCAL => state.local_storage.set(b"", key, value),
364 StorageKind::PERSISTENT => state.persistent_storage.set(b"", key, value),
365 };
366 }
367
368 fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
369 let mut state = self.0.write();
370 match kind {
371 StorageKind::LOCAL => state.local_storage.remove(b"", key),
372 StorageKind::PERSISTENT => state.persistent_storage.remove(b"", key),
373 };
374 }
375
376 fn local_storage_compare_and_set(
377 &mut self,
378 kind: StorageKind,
379 key: &[u8],
380 old_value: Option<&[u8]>,
381 new_value: &[u8],
382 ) -> bool {
383 let mut state = self.0.write();
384 match kind {
385 StorageKind::LOCAL => {
386 state.local_storage.compare_and_set(b"", key, old_value, new_value)
387 },
388 StorageKind::PERSISTENT => {
389 state.persistent_storage.compare_and_set(b"", key, old_value, new_value)
390 },
391 }
392 }
393
394 fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
395 let state = self.0.read();
396 match kind {
397 StorageKind::LOCAL => state.local_storage.get(TestPersistentOffchainDB::PREFIX, key),
398 StorageKind::PERSISTENT => state.persistent_storage.get(key),
399 }
400 }
401}
402
403#[derive(Default)]
405pub struct PoolState {
406 pub transactions: Vec<Vec<u8>>,
408}
409
410#[derive(Default)]
420pub struct TestTransactionPoolExt(Arc<RwLock<PoolState>>);
421
422impl TestTransactionPoolExt {
423 pub fn new() -> (Self, Arc<RwLock<PoolState>>) {
425 let ext = Self::default();
426 let state = ext.0.clone();
427 (ext, state)
428 }
429}
430
431impl TransactionPool for TestTransactionPoolExt {
432 fn submit_transaction(&mut self, extrinsic: Vec<u8>) -> Result<(), ()> {
433 self.0.write().transactions.push(extrinsic);
434 Ok(())
435 }
436}