tet_core/offchain/
mod.rs

1// This file is part of Tetcore.
2
3// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Offchain workers types
19
20use codec::{Encode, Decode};
21use tetcore_std::{prelude::{Vec, Box}, convert::TryFrom};
22use crate::{OpaquePeerId, RuntimeDebug};
23use tp_runtime_interface::pass_by::{PassByCodec, PassByInner, PassByEnum};
24
25pub use crate::crypto::KeyTypeId;
26
27#[cfg(feature = "std")]
28pub mod storage;
29#[cfg(feature = "std")]
30pub mod testing;
31
32/// Local storage prefix used by the Offchain Worker API to
33pub const STORAGE_PREFIX : &'static [u8] = b"storage";
34
35/// Offchain workers local storage.
36pub trait OffchainStorage: Clone + Send + Sync {
37	/// Persist a value in storage under given key and prefix.
38	fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]);
39
40	/// Clear a storage entry under given key and prefix.
41	fn remove(&mut self, prefix: &[u8], key: &[u8]);
42
43	/// Retrieve a value from storage under given key and prefix.
44	fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>>;
45
46	/// Replace the value in storage if given old_value matches the current one.
47	///
48	/// Returns `true` if the value has been set and false otherwise.
49	fn compare_and_set(
50		&mut self,
51		prefix: &[u8],
52		key: &[u8],
53		old_value: Option<&[u8]>,
54		new_value: &[u8],
55	) -> bool;
56}
57
58/// A type of supported crypto.
59#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)]
60#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
61#[repr(C)]
62pub enum StorageKind {
63	/// Persistent storage is non-revertible and not fork-aware. It means that any value
64	/// set by the offchain worker triggered at block `N(hash1)` is persisted even
65	/// if that block is reverted as non-canonical and is available for the worker
66	/// that is re-run at block `N(hash2)`.
67	/// This storage can be used by offchain workers to handle forks
68	/// and coordinate offchain workers running on different forks.
69	PERSISTENT = 1,
70	/// Local storage is revertible and fork-aware. It means that any value
71	/// set by the offchain worker triggered at block `N(hash1)` is reverted
72	/// if that block is reverted as non-canonical and is NOT available for the worker
73	/// that is re-run at block `N(hash2)`.
74	LOCAL = 2,
75}
76
77impl TryFrom<u32> for StorageKind {
78	type Error = ();
79
80	fn try_from(kind: u32) -> Result<Self, Self::Error> {
81		match kind {
82			e if e == u32::from(StorageKind::PERSISTENT as u8) => Ok(StorageKind::PERSISTENT),
83			e if e == u32::from(StorageKind::LOCAL as u8) => Ok(StorageKind::LOCAL),
84			_ => Err(()),
85		}
86	}
87}
88
89impl From<StorageKind> for u32 {
90	fn from(c: StorageKind) -> Self {
91		c as u8 as u32
92	}
93}
94
95/// Opaque type for offchain http requests.
96#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, Encode, Decode, PassByInner)]
97#[cfg_attr(feature = "std", derive(Hash))]
98pub struct HttpRequestId(pub u16);
99
100impl From<HttpRequestId> for u32 {
101	fn from(c: HttpRequestId) -> Self {
102		c.0 as u32
103	}
104}
105
106/// An error enum returned by some http methods.
107#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, PassByEnum)]
108#[repr(C)]
109pub enum HttpError {
110	/// The requested action couldn't been completed within a deadline.
111	DeadlineReached = 1,
112	/// There was an IO Error while processing the request.
113	IoError = 2,
114	/// The ID of the request is invalid in this context.
115	Invalid = 3,
116}
117
118impl TryFrom<u32> for HttpError {
119	type Error = ();
120
121	fn try_from(error: u32) -> Result<Self, Self::Error> {
122		match error {
123			e if e == HttpError::DeadlineReached as u8 as u32 => Ok(HttpError::DeadlineReached),
124			e if e == HttpError::IoError as u8 as u32 => Ok(HttpError::IoError),
125			e if e == HttpError::Invalid as u8 as u32 => Ok(HttpError::Invalid),
126			_ => Err(())
127		}
128	}
129}
130
131impl From<HttpError> for u32 {
132	fn from(c: HttpError) -> Self {
133		c as u8 as u32
134	}
135}
136
137/// Status of the HTTP request
138#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, PassByCodec)]
139pub enum HttpRequestStatus {
140	/// Deadline was reached while we waited for this request to finish.
141	///
142	/// Note the deadline is controlled by the calling part, it not necessarily
143	/// means that the request has timed out.
144	DeadlineReached,
145	/// An error has occurred during the request, for example a timeout or the
146	/// remote has closed our socket.
147	///
148	/// The request is now considered destroyed. To retry the request you need
149	/// to construct it again.
150	IoError,
151	/// The passed ID is invalid in this context.
152	Invalid,
153	/// The request has finished with given status code.
154	Finished(u16),
155}
156
157impl From<HttpRequestStatus> for u32 {
158	fn from(status: HttpRequestStatus) -> Self {
159		match status {
160			HttpRequestStatus::Invalid => 0,
161			HttpRequestStatus::DeadlineReached => 10,
162			HttpRequestStatus::IoError => 20,
163			HttpRequestStatus::Finished(code) => u32::from(code),
164		}
165	}
166}
167
168impl TryFrom<u32> for HttpRequestStatus {
169	type Error = ();
170
171	fn try_from(status: u32) -> Result<Self, Self::Error> {
172		match status {
173			0 => Ok(HttpRequestStatus::Invalid),
174			10 => Ok(HttpRequestStatus::DeadlineReached),
175			20 => Ok(HttpRequestStatus::IoError),
176			100..=999 => u16::try_from(status).map(HttpRequestStatus::Finished).map_err(|_| ()),
177			_ => Err(()),
178		}
179	}
180}
181
182/// A blob to hold information about the local node's network state
183/// without committing to its format.
184#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByCodec)]
185#[cfg_attr(feature = "std", derive(Default))]
186pub struct OpaqueNetworkState {
187	/// PeerId of the local node in SCALE encoded.
188	pub peer_id: OpaquePeerId,
189	/// List of addresses the node knows it can be reached as.
190	pub external_addresses: Vec<OpaqueMultiaddr>,
191}
192
193/// Simple blob to hold a `Multiaddr` without committing to its format.
194#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)]
195pub struct OpaqueMultiaddr(pub Vec<u8>);
196
197impl OpaqueMultiaddr {
198	/// Create new `OpaqueMultiaddr`
199	pub fn new(vec: Vec<u8>) -> Self {
200		OpaqueMultiaddr(vec)
201	}
202}
203
204/// Opaque timestamp type
205#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode)]
206pub struct Timestamp(u64);
207
208/// Duration type
209#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode)]
210pub struct Duration(u64);
211
212impl Duration {
213	/// Create new duration representing given number of milliseconds.
214	pub const fn from_millis(millis: u64) -> Self {
215		Duration(millis)
216	}
217
218	/// Returns number of milliseconds this Duration represents.
219	pub fn millis(&self) -> u64 {
220		self.0
221	}
222}
223
224impl Timestamp {
225	/// Creates new `Timestamp` given unix timestamp in milliseconds.
226	pub fn from_unix_millis(millis: u64) -> Self {
227		Timestamp(millis)
228	}
229
230	/// Increase the timestamp by given `Duration`.
231	pub fn add(&self, duration: Duration) -> Timestamp {
232		Timestamp(self.0.saturating_add(duration.0))
233	}
234
235	/// Decrease the timestamp by given `Duration`
236	pub fn sub(&self, duration: Duration) -> Timestamp {
237		Timestamp(self.0.saturating_sub(duration.0))
238	}
239
240	/// Returns a saturated difference (Duration) between two Timestamps.
241	pub fn diff(&self, other: &Self) -> Duration {
242		Duration(self.0.saturating_sub(other.0))
243	}
244
245	/// Return number of milliseconds since UNIX epoch.
246	pub fn unix_millis(&self) -> u64 {
247		self.0
248	}
249}
250
251/// Execution context extra capabilities.
252#[derive(Debug, PartialEq, Eq, Clone, Copy)]
253#[repr(u8)]
254pub enum Capability {
255	/// Access to transaction pool.
256	TransactionPool = 1,
257	/// External http calls.
258	Http = 2,
259	/// Keystore access.
260	Keystore = 4,
261	/// Randomness source.
262	Randomness = 8,
263	/// Access to opaque network state.
264	NetworkState = 16,
265	/// Access to offchain worker DB (read only).
266	OffchainWorkerDbRead = 32,
267	/// Access to offchain worker DB (writes).
268	OffchainWorkerDbWrite = 64,
269	/// Manage the authorized nodes
270	NodeAuthorization = 128,
271}
272
273/// A set of capabilities
274#[derive(Debug, PartialEq, Eq, Clone, Copy)]
275pub struct Capabilities(u8);
276
277impl Capabilities {
278	/// Return an object representing an empty set of capabilities.
279	pub fn none() -> Self {
280		Self(0)
281	}
282
283	/// Return an object representing all capabilities enabled.
284	pub fn all() -> Self {
285		Self(u8::max_value())
286	}
287
288	/// Return capabilities for rich offchain calls.
289	///
290	/// Those calls should be allowed to sign and submit transactions
291	/// and access offchain workers database (but read only!).
292	pub fn rich_offchain_call() -> Self {
293		[
294			Capability::TransactionPool,
295			Capability::Keystore,
296			Capability::OffchainWorkerDbRead,
297		][..].into()
298	}
299
300	/// Check if particular capability is enabled.
301	pub fn has(&self, capability: Capability) -> bool {
302		self.0 & capability as u8 != 0
303	}
304
305	/// Check if this capability object represents all capabilities.
306	pub fn has_all(&self) -> bool {
307		self == &Capabilities::all()
308	}
309}
310
311impl<'a> From<&'a [Capability]> for Capabilities {
312	fn from(list: &'a [Capability]) -> Self {
313		Capabilities(list.iter().fold(0_u8, |a, b| a | *b as u8))
314	}
315}
316
317/// An extended externalities for offchain workers.
318pub trait Externalities: Send {
319	/// Returns if the local node is a potential validator.
320	///
321	/// Even if this function returns `true`, it does not mean that any keys are configured
322	/// and that the validator is registered in the chain.
323	fn is_validator(&self) -> bool;
324
325	/// Returns information about the local node's network state.
326	fn network_state(&self) -> Result<OpaqueNetworkState, ()>;
327
328	/// Returns current UNIX timestamp (in millis)
329	fn timestamp(&mut self) -> Timestamp;
330
331	/// Pause the execution until `deadline` is reached.
332	fn sleep_until(&mut self, deadline: Timestamp);
333
334	/// Returns a random seed.
335	///
336	/// This is a truly random non deterministic seed generated by host environment.
337	/// Obviously fine in the off-chain worker context.
338	fn random_seed(&mut self) -> [u8; 32];
339
340	/// Sets a value in the local storage.
341	///
342	/// Note this storage is not part of the consensus, it's only accessible by
343	/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
344	fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]);
345
346	/// Removes a value in the local storage.
347	///
348	/// Note this storage is not part of the consensus, it's only accessible by
349	/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
350	fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]);
351
352	/// Sets a value in the local storage if it matches current value.
353	///
354	/// Since multiple offchain workers may be running concurrently, to prevent
355	/// data races use CAS to coordinate between them.
356	///
357	/// Returns `true` if the value has been set, `false` otherwise.
358	///
359	/// Note this storage is not part of the consensus, it's only accessible by
360	/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
361	fn local_storage_compare_and_set(
362		&mut self,
363		kind: StorageKind,
364		key: &[u8],
365		old_value: Option<&[u8]>,
366		new_value: &[u8],
367	) -> bool;
368
369	/// Gets a value from the local storage.
370	///
371	/// If the value does not exist in the storage `None` will be returned.
372	/// Note this storage is not part of the consensus, it's only accessible by
373	/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
374	fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>>;
375
376	/// Initiates a http request given HTTP verb and the URL.
377	///
378	/// Meta is a future-reserved field containing additional, tetsy-scale-codec encoded parameters.
379	/// Returns the id of newly started request.
380	///
381	/// Returns an error if:
382	/// - No new request identifier could be allocated.
383	/// - The method or URI contain invalid characters.
384	///
385	fn http_request_start(
386		&mut self,
387		method: &str,
388		uri: &str,
389		meta: &[u8]
390	) -> Result<HttpRequestId, ()>;
391
392	/// Append header to the request.
393	///
394	/// Calling this function multiple times with the same header name continues appending new
395	/// headers. In other words, headers are never replaced.
396	///
397	/// Returns an error if:
398	/// - The request identifier is invalid.
399	/// - You have called `http_request_write_body` on that request.
400	/// - The name or value contain invalid characters.
401	///
402	/// An error doesn't poison the request, and you can continue as if the call had never been
403	/// made.
404	///
405	fn http_request_add_header(
406		&mut self,
407		request_id: HttpRequestId,
408		name: &str,
409		value: &str
410	) -> Result<(), ()>;
411
412	/// Write a chunk of request body.
413	///
414	/// Calling this function with a non-empty slice may or may not start the
415	/// HTTP request. Calling this function with an empty chunks finalizes the
416	/// request and always starts it. It is no longer valid to write more data
417	/// afterwards.
418	/// Passing `None` as deadline blocks forever.
419	///
420	/// Returns an error if:
421	/// - The request identifier is invalid.
422	/// - `http_response_wait` has already been called on this request.
423	/// - The deadline is reached.
424	/// - An I/O error has happened, for example the remote has closed our
425	///   request. The request is then considered invalid.
426	///
427	fn http_request_write_body(
428		&mut self,
429		request_id: HttpRequestId,
430		chunk: &[u8],
431		deadline: Option<Timestamp>
432	) -> Result<(), HttpError>;
433
434	/// Block and wait for the responses for given requests.
435	///
436	/// Returns a vector of request statuses (the len is the same as ids).
437	/// Note that if deadline is not provided the method will block indefinitely,
438	/// otherwise unready responses will produce `DeadlineReached` status.
439	///
440	/// If a response returns an `IoError`, it is then considered destroyed.
441	/// Its id is then invalid.
442	///
443	/// Passing `None` as deadline blocks forever.
444	fn http_response_wait(
445		&mut self,
446		ids: &[HttpRequestId],
447		deadline: Option<Timestamp>
448	) -> Vec<HttpRequestStatus>;
449
450	/// Read all response headers.
451	///
452	/// Returns a vector of pairs `(HeaderKey, HeaderValue)`.
453	///
454	/// Dispatches the request if it hasn't been done yet. It is no longer
455	/// valid to modify the headers or write data to the request.
456	///
457	/// Returns an empty list if the identifier is unknown/invalid, hasn't
458	/// received a response, or has finished.
459	fn http_response_headers(
460		&mut self,
461		request_id: HttpRequestId
462	) -> Vec<(Vec<u8>, Vec<u8>)>;
463
464	/// Read a chunk of body response to given buffer.
465	///
466	/// Dispatches the request if it hasn't been done yet. It is no longer
467	/// valid to modify the headers or write data to the request.
468	///
469	/// Returns the number of bytes written or an error in case a deadline
470	/// is reached or server closed the connection.
471	/// Passing `None` as a deadline blocks forever.
472	///
473	/// If `Ok(0)` or `Err(IoError)` is returned, the request is considered
474	/// destroyed. Doing another read or getting the response's headers, for
475	/// example, is then invalid.
476	///
477	/// Returns an error if:
478	/// - The request identifier is invalid.
479	/// - The deadline is reached.
480	/// - An I/O error has happened, for example the remote has closed our
481	///   request. The request is then considered invalid.
482	///
483	fn http_response_read_body(
484		&mut self,
485		request_id: HttpRequestId,
486		buffer: &mut [u8],
487		deadline: Option<Timestamp>
488	) -> Result<usize, HttpError>;
489
490	/// Set the authorized nodes from runtime.
491	///
492	/// In a permissioned network, the connections between nodes need to reach a
493	/// consensus between participants.
494	///
495	/// - `nodes`: a set of nodes which are allowed to connect for the local node.
496	/// each one is identified with an `OpaquePeerId`, here it just use plain bytes
497	/// without any encoding. Invalid `OpaquePeerId`s are silently ignored.
498	/// - `authorized_only`: if true, only the authorized nodes are allowed to connect,
499	/// otherwise unauthorized nodes can also be connected through other mechanism.
500	fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool);
501}
502
503impl<T: Externalities + ?Sized> Externalities for Box<T> {
504	fn is_validator(&self) -> bool {
505		(& **self).is_validator()
506	}
507
508	fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
509		(& **self).network_state()
510	}
511
512	fn timestamp(&mut self) -> Timestamp {
513		(&mut **self).timestamp()
514	}
515
516	fn sleep_until(&mut self, deadline: Timestamp) {
517		(&mut **self).sleep_until(deadline)
518	}
519
520	fn random_seed(&mut self) -> [u8; 32] {
521		(&mut **self).random_seed()
522	}
523
524	fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
525		(&mut **self).local_storage_set(kind, key, value)
526	}
527
528	fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
529		(&mut **self).local_storage_clear(kind, key)
530	}
531
532	fn local_storage_compare_and_set(
533		&mut self,
534		kind: StorageKind,
535		key: &[u8],
536		old_value: Option<&[u8]>,
537		new_value: &[u8],
538	) -> bool {
539		(&mut **self).local_storage_compare_and_set(kind, key, old_value, new_value)
540	}
541
542	fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
543		(&mut **self).local_storage_get(kind, key)
544	}
545
546	fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<HttpRequestId, ()> {
547		(&mut **self).http_request_start(method, uri, meta)
548	}
549
550	fn http_request_add_header(&mut self, request_id: HttpRequestId, name: &str, value: &str) -> Result<(), ()> {
551		(&mut **self).http_request_add_header(request_id, name, value)
552	}
553
554	fn http_request_write_body(
555		&mut self,
556		request_id: HttpRequestId,
557		chunk: &[u8],
558		deadline: Option<Timestamp>
559	) -> Result<(), HttpError> {
560		(&mut **self).http_request_write_body(request_id, chunk, deadline)
561	}
562
563	fn http_response_wait(&mut self, ids: &[HttpRequestId], deadline: Option<Timestamp>) -> Vec<HttpRequestStatus> {
564		(&mut **self).http_response_wait(ids, deadline)
565	}
566
567	fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
568		(&mut **self).http_response_headers(request_id)
569	}
570
571	fn http_response_read_body(
572		&mut self,
573		request_id: HttpRequestId,
574		buffer: &mut [u8],
575		deadline: Option<Timestamp>
576	) -> Result<usize, HttpError> {
577		(&mut **self).http_response_read_body(request_id, buffer, deadline)
578	}
579
580	fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
581		(&mut **self).set_authorized_nodes(nodes, authorized_only)
582	}
583}
584
585/// An `OffchainExternalities` implementation with limited capabilities.
586pub struct LimitedExternalities<T> {
587	capabilities: Capabilities,
588	externalities: T,
589}
590
591impl<T> LimitedExternalities<T> {
592	/// Create new externalities limited to given `capabilities`.
593	pub fn new(capabilities: Capabilities, externalities: T) -> Self {
594		Self {
595			capabilities,
596			externalities,
597		}
598	}
599
600	/// Check if given capability is allowed.
601	///
602	/// Panics in case it is not.
603	fn check(&self, capability: Capability, name: &'static str) {
604		if !self.capabilities.has(capability) {
605			panic!("Accessing a forbidden API: {}. No: {:?} capability.", name, capability);
606		}
607	}
608}
609
610impl<T: Externalities> Externalities for LimitedExternalities<T> {
611	fn is_validator(&self) -> bool {
612		self.check(Capability::Keystore, "is_validator");
613		self.externalities.is_validator()
614	}
615
616	fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
617		self.check(Capability::NetworkState, "network_state");
618		self.externalities.network_state()
619	}
620
621	fn timestamp(&mut self) -> Timestamp {
622		self.check(Capability::Http, "timestamp");
623		self.externalities.timestamp()
624	}
625
626	fn sleep_until(&mut self, deadline: Timestamp) {
627		self.check(Capability::Http, "sleep_until");
628		self.externalities.sleep_until(deadline)
629	}
630
631	fn random_seed(&mut self) -> [u8; 32] {
632		self.check(Capability::Randomness, "random_seed");
633		self.externalities.random_seed()
634	}
635
636	fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
637		self.check(Capability::OffchainWorkerDbWrite, "local_storage_set");
638		self.externalities.local_storage_set(kind, key, value)
639	}
640
641	fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
642		self.check(Capability::OffchainWorkerDbWrite, "local_storage_clear");
643		self.externalities.local_storage_clear(kind, key)
644	}
645
646	fn local_storage_compare_and_set(
647		&mut self,
648		kind: StorageKind,
649		key: &[u8],
650		old_value: Option<&[u8]>,
651		new_value: &[u8],
652	) -> bool {
653		self.check(Capability::OffchainWorkerDbWrite, "local_storage_compare_and_set");
654		self.externalities.local_storage_compare_and_set(kind, key, old_value, new_value)
655	}
656
657	fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
658		self.check(Capability::OffchainWorkerDbRead, "local_storage_get");
659		self.externalities.local_storage_get(kind, key)
660	}
661
662	fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<HttpRequestId, ()> {
663		self.check(Capability::Http, "http_request_start");
664		self.externalities.http_request_start(method, uri, meta)
665	}
666
667	fn http_request_add_header(&mut self, request_id: HttpRequestId, name: &str, value: &str) -> Result<(), ()> {
668		self.check(Capability::Http, "http_request_add_header");
669		self.externalities.http_request_add_header(request_id, name, value)
670	}
671
672	fn http_request_write_body(
673		&mut self,
674		request_id: HttpRequestId,
675		chunk: &[u8],
676		deadline: Option<Timestamp>
677	) -> Result<(), HttpError> {
678		self.check(Capability::Http, "http_request_write_body");
679		self.externalities.http_request_write_body(request_id, chunk, deadline)
680	}
681
682	fn http_response_wait(&mut self, ids: &[HttpRequestId], deadline: Option<Timestamp>) -> Vec<HttpRequestStatus> {
683		self.check(Capability::Http, "http_response_wait");
684		self.externalities.http_response_wait(ids, deadline)
685	}
686
687	fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
688		self.check(Capability::Http, "http_response_headers");
689		self.externalities.http_response_headers(request_id)
690	}
691
692	fn http_response_read_body(
693		&mut self,
694		request_id: HttpRequestId,
695		buffer: &mut [u8],
696		deadline: Option<Timestamp>
697	) -> Result<usize, HttpError> {
698		self.check(Capability::Http, "http_response_read_body");
699		self.externalities.http_response_read_body(request_id, buffer, deadline)
700	}
701
702	fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
703		self.check(Capability::NodeAuthorization, "set_authorized_nodes");
704		self.externalities.set_authorized_nodes(nodes, authorized_only)
705	}
706}
707
708#[cfg(feature = "std")]
709externalities::decl_extension! {
710	/// The offchain extension that will be registered at the Tetcore externalities.
711	pub struct OffchainExt(Box<dyn Externalities>);
712}
713
714#[cfg(feature = "std")]
715impl OffchainExt {
716	/// Create a new instance of `Self`.
717	pub fn new<O: Externalities + 'static>(offchain: O) -> Self {
718		Self(Box::new(offchain))
719	}
720}
721
722/// Abstraction over transaction pool.
723///
724/// This trait is currently used within the `ExternalitiesExtension`
725/// to provide offchain calls with access to the transaction pool without
726/// tight coupling with any pool implementation.
727#[cfg(feature = "std")]
728pub trait TransactionPool {
729	/// Submit transaction.
730	///
731	/// The transaction will end up in the pool and be propagated to others.
732	fn submit_transaction(&mut self, extrinsic: Vec<u8>) -> Result<(), ()>;
733}
734
735#[cfg(feature = "std")]
736externalities::decl_extension! {
737	/// An externalities extension to submit transactions to the pool.
738	pub struct TransactionPoolExt(Box<dyn TransactionPool + Send>);
739}
740
741#[cfg(feature = "std")]
742impl TransactionPoolExt {
743	/// Create a new instance of `TransactionPoolExt`.
744	pub fn new<O: TransactionPool + Send + 'static>(pool: O) -> Self {
745		Self(Box::new(pool))
746	}
747}
748
749/// Change to be applied to the offchain worker db in regards to a key.
750#[derive(Debug, Clone, Hash, Eq, PartialEq)]
751pub enum OffchainOverlayedChange {
752	/// Remove the data associated with the key
753	Remove,
754	/// Overwrite the value of an associated key
755	SetValue(Vec<u8>),
756}
757
758#[cfg(test)]
759mod tests {
760	use super::*;
761
762	#[test]
763	fn timestamp_ops() {
764		let t = Timestamp(5);
765		assert_eq!(t.add(Duration::from_millis(10)), Timestamp(15));
766		assert_eq!(t.sub(Duration::from_millis(10)), Timestamp(0));
767		assert_eq!(t.diff(&Timestamp(3)), Duration(2));
768	}
769
770	#[test]
771	fn capabilities() {
772		let none = Capabilities::none();
773		let all = Capabilities::all();
774		let some = Capabilities::from(&[Capability::Keystore, Capability::Randomness][..]);
775
776		assert!(!none.has(Capability::Keystore));
777		assert!(all.has(Capability::Keystore));
778		assert!(some.has(Capability::Keystore));
779		assert!(!none.has(Capability::TransactionPool));
780		assert!(all.has(Capability::TransactionPool));
781		assert!(!some.has(Capability::TransactionPool));
782	}
783}