p2panda_core/
extensions.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3//! Traits required for defining custom extensions.
4//!
5//! User-defined extensions can be added to an operation's `Header` in order to extend the basic
6//! functionality of the core p2panda data types or to encode application-specific fields which
7//! should not be contained in the [`Body`](crate::Body). Extension values can themselves be
8//! derived from other header material, such as `PublicKey` or a headers' `Hash`.
9//!
10//! At a lower level this might be information relating to capabilities or group encryption schemes
11//! which is required to enforce access-control restrictions during sync. Alternatively, extensions
12//! might be used to set expiration timestamps and deletion flags in order to facilitate garbage
13//! collection of stale data from the network. The core p2panda data types intentionally don't
14//! enforce a single approach to such areas where there are rightly many different approaches, with
15//! the most suitable being dependent on specific use-case requirements.
16//!
17//! Interfaces which use p2panda core data types can require certain extensions to be present on
18//! any headers that their APIs accept using trait bounds. `p2panda-stream`, for example, uses the
19//! [`PruneFlag`](crate::PruneFlag) in order to implement automatic network-wide garbage
20//! collection.
21//!
22//! Extensions are encoded on a header and sent over the wire. We need to satisfy all trait
23//! requirements that `Header` requires, including `Serialize` and `Deserialize`.
24//!
25//! //! ## Example
26//!
27//! ```
28//! use p2panda_core::{Body, Hash, Extension, Header, PrivateKey};
29//! use serde::{Serialize, Deserialize};
30//!
31//! #[derive(Clone, Debug, Serialize, Deserialize)]
32//! struct LogId(Hash);
33//!
34//! #[derive(Clone, Debug, Default, Serialize, Deserialize)]
35//! struct Expiry(u64);
36//!
37//! #[derive(Clone, Debug, Serialize, Deserialize)]
38//! struct CustomExtensions {
39//!     log_id: Option<LogId>,
40//!     expires: Expiry,
41//! }
42//!
43//! impl Extension<LogId> for CustomExtensions {
44//!     fn extract(header: &Header<Self>) -> Option<LogId> {
45//!         if header.seq_num == 0 {
46//!             return Some(LogId(header.hash()));
47//!         };
48//!
49//!         let Some(extensions) = header.extensions.as_ref() else {
50//!             return None;
51//!         };
52//!
53//!         extensions.log_id.clone()
54//!     }
55//! }
56//!
57//! impl Extension<Expiry> for CustomExtensions {
58//!     fn extract(header: &Header<Self>) -> Option<Expiry> {
59//!         header
60//!             .extensions
61//!             .as_ref()
62//!             .map(|extensions| extensions.expires.clone())
63//!     }
64//! }
65//!
66//! let extensions = CustomExtensions {
67//!     log_id: None,
68//!     expires: Expiry(0123456),
69//! };
70//!
71//! let private_key = PrivateKey::new();
72//! let body: Body = Body::new("Hello, Sloth!".as_bytes());
73//!
74//! let mut header = Header {
75//!     version: 1,
76//!     public_key: private_key.public_key(),
77//!     signature: None,
78//!     payload_size: body.size(),
79//!     payload_hash: Some(body.hash()),
80//!     timestamp: 0,
81//!     seq_num: 0,
82//!     backlink: None,
83//!     previous: vec![],
84//!     extensions: Some(extensions.clone()),
85//! };
86//!
87//! header.sign(&private_key);
88//!
89//! let log_id: LogId = header.extension().unwrap();
90//! let expiry: Expiry = header.extension().unwrap();
91//!
92//! assert_eq!(header.hash(), log_id.0);
93//! assert_eq!(extensions.expires.0, expiry.0);
94//! ```
95use std::fmt::Debug;
96
97use serde::{Deserialize, Serialize};
98
99use crate::Header;
100
101/// Trait definition of a single header extension type.
102pub trait Extension<T>: Extensions {
103    /// Extract the extension value from a header.
104    fn extract(_header: &Header<Self>) -> Option<T> {
105        None
106    }
107}
108
109/// Super-trait defining trait bounds required by custom extensions types.
110pub trait Extensions: Clone + Debug + for<'de> Deserialize<'de> + Serialize {}
111
112/// Blanket implementation of `Extensions` trait any type with the required bounds satisfied.
113impl<T> Extensions for T where T: Clone + Debug + for<'de> Deserialize<'de> + Serialize {}