Skip to main content

p2panda_core/
extensions.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
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 derived
8//! from other header material, such as `VerifingKey` 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 any
18//! 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 collection.
20//!
21//! Extensions are encoded on a header and sent over the wire. We need to satisfy all trait
22//! requirements that `Header` requires, including `Serialize` and `Deserialize`.
23//!
24//! //! ## Example
25//!
26//! ```
27//! use p2panda_core::{Body, Hash, Extension, Header, SigningKey, Timestamp};
28//! use serde::{Serialize, Deserialize};
29//!
30//! #[derive(Clone, Debug, Serialize, Deserialize)]
31//! struct LogId(Hash);
32//!
33//! #[derive(Clone, Debug, Default, Serialize, Deserialize)]
34//! struct Expiry(u64);
35//!
36//! #[derive(Clone, Debug, Serialize, Deserialize)]
37//! struct CustomExtensions {
38//!     log_id: Option<LogId>,
39//!     expires: Expiry,
40//! }
41//!
42//! impl Extension<LogId> for CustomExtensions {
43//!     fn extract(header: &Header<Self>) -> Option<LogId> {
44//!         if header.seq_num == 0 {
45//!             return Some(LogId(header.hash()));
46//!         };
47//!
48//!         header.extensions.log_id.clone()
49//!     }
50//! }
51//!
52//! impl Extension<Expiry> for CustomExtensions {
53//!     fn extract(header: &Header<Self>) -> Option<Expiry> {
54//!        Some(header.extensions.expires.clone())
55//!     }
56//! }
57//!
58//! let extensions = CustomExtensions {
59//!     log_id: None,
60//!     expires: Expiry(0123456),
61//! };
62//!
63//! let signing_key = SigningKey::generate();
64//! let body: Body = Body::new("Hello, Sloth!".as_bytes());
65//!
66//! let mut header = Header {
67//!     version: 1,
68//!     verifying_key: signing_key.verifying_key(),
69//!     signature: None,
70//!     payload_size: body.size(),
71//!     payload_hash: Some(body.hash()),
72//!     timestamp: Timestamp::now(),
73//!     seq_num: 0,
74//!     backlink: None,
75//!     extensions: extensions.clone(),
76//! };
77//!
78//! header.sign(&signing_key);
79//!
80//! let log_id: LogId = header.extension().unwrap();
81//! let expiry: Expiry = header.extension().unwrap();
82//!
83//! assert_eq!(header.hash(), log_id.0);
84//! assert_eq!(extensions.expires.0, expiry.0);
85//! ```
86use std::fmt::Debug;
87
88use serde::{Deserialize, Serialize};
89
90use crate::Header;
91
92/// Trait definition of a single header extension type.
93pub trait Extension<T>: Extensions {
94    /// Extract the extension value from a header.
95    fn extract(_header: &Header<Self>) -> Option<T> {
96        None
97    }
98}
99
100/// Super-trait defining trait bounds required by custom extensions types.
101pub trait Extensions: Clone + Debug + for<'de> Deserialize<'de> + Serialize {}
102
103/// Blanket implementation of `Extensions` trait any type with the required bounds satisfied.
104impl<T> Extensions for T where T: Clone + Debug + for<'de> Deserialize<'de> + Serialize {}