p2panda_store/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Interfaces and implementations of persistence layers for core p2panda data types.
4//!
5//! The provided APIs allow for efficient implementations of `Operation` and log stores. These
6//! persistence and query APIs are utilised by higher-level components of the p2panda stack, such
7//! as `p2panda-sync` and `p2panda-stream`. For detailed information concerning the `Operation`
8//! type, please consult the documentation for the `p2panda-core` crate.
9//!
10//! Logs in the context of `p2panda-store` are simply a collection of operations grouped under a
11//! common identifier. The precise type for which `LogId` is implemented is left up to the
12//! developer to decide according to their needs. With this in mind, the traits and implementations
13//! provided by `p2panda-store` do not perform any validation of log integrity. Developers using
14//! this crate must take steps to ensure their log design is fit for purpose and that all operations
15//! have been thoroughly validated before being persisted.
16//!
17//! Also note that the traits provided here are not intended to offer generic storage solutions for
18//! non-p2panda data types, nor are they intended to solve application-layer storage concerns.
19//!
20//! An in-memory storage solution is provided in the form of a `MemoryStore` which implements both
21//! `OperationStore` and `LogStore`. The store is gated by the `memory` feature flag and is enabled
22//! by default.
23#[cfg(feature = "memory")]
24pub mod memory_store;
25
26use std::fmt::{Debug, Display};
27
28#[cfg(feature = "memory")]
29pub use memory_store::MemoryStore;
30
31use p2panda_core::{Body, Hash, Header, PublicKey, RawOperation};
32
33/// Uniquely identify a single-author log.
34///
35/// The `LogId` exists purely to group a set of operations and is intended to be implemented for
36/// any type which meets the design requirements of a particular application.
37///
38/// A blanket implementation is provided for any type meeting the required trait bounds.
39///
40/// Here we briefly outline several implementation scenarios:
41///
42/// An application relying on a one-log-per-author design might choose to implement `LogId` for a thin
43/// wrapper around an Ed25519 public key; this effectively ties the log to the public key of the
44/// author. Secure Scuttlebutt (SSB) is an example of a protocol which relies on this model.
45///
46/// In an application where one author may produce operations grouped into multiple logs,
47/// `LogId` might be implemented for a `struct` which includes both the public key of the author
48/// and a unique number for each log instance.
49///
50/// Some applications might require semantic grouping of operations. For example, a chat
51/// application may choose to create a separate log for each author-channel pairing. In such a
52/// scenario, `LogId` might be implemented for a `struct` containing a `String` representation of
53/// the channel name.
54///
55/// Finally, please note that implementers of `LogId` must take steps to ensure their log design is
56/// fit for purpose and that all operations have been thoroughly validated before being persisted.
57/// No such validation checks are provided by `p2panda-store`.
58pub trait LogId: Clone + Debug + Eq + std::hash::Hash {}
59
60impl<T> LogId for T where T: Clone + Debug + Eq + std::hash::Hash {}
61
62/// Interface for storing, deleting and querying operations.
63///
64/// Two variants of the trait are provided: one which is thread-safe (implementing `Sync`) and one
65/// which is purely intended for single-threaded execution contexts.
66#[trait_variant::make(OperationStore: Send)]
67pub trait LocalOperationStore<LogId, Extensions>: Clone {
68    type Error: Display + Debug;
69
70    /// Insert an operation.
71    ///
72    /// Returns `true` when the insert occurred, or `false` when the operation already existed and
73    /// no insertion occurred.
74    async fn insert_operation(
75        &mut self,
76        hash: Hash,
77        header: &Header<Extensions>,
78        body: Option<&Body>,
79        header_bytes: &[u8],
80        log_id: &LogId,
81    ) -> Result<bool, Self::Error>;
82
83    /// Get an operation.
84    async fn get_operation(
85        &self,
86        hash: Hash,
87    ) -> Result<Option<(Header<Extensions>, Option<Body>)>, Self::Error>;
88
89    /// Get the "raw" header and body bytes of an operation.
90    async fn get_raw_operation(&self, hash: Hash) -> Result<Option<RawOperation>, Self::Error>;
91
92    /// Query the existence of an operation.
93    ///
94    /// Returns `true` if the operation was found in the store and `false` if not.
95    async fn has_operation(&self, hash: Hash) -> Result<bool, Self::Error>;
96
97    /// Delete an operation.
98    ///
99    /// Returns `true` when the removal occurred and `false` when the operation was not found in
100    /// the store.
101    async fn delete_operation(&mut self, hash: Hash) -> Result<bool, Self::Error>;
102
103    /// Delete the payload of an operation.
104    ///
105    /// Returns `true` when the removal occurred and `false` when the operation was not found in
106    /// the store or the payload was already deleted.
107    async fn delete_payload(&mut self, hash: Hash) -> Result<bool, Self::Error>;
108}
109
110/// Interface for storing, deleting and querying logs.
111///
112/// Two variants of the trait are provided: one which is thread-safe (implementing `Sync`) and one
113/// which is purely intended for single-threaded execution contexts.
114#[trait_variant::make(LogStore: Send)]
115pub trait LocalLogStore<LogId, Extensions> {
116    type Error: Display + Debug;
117
118    /// Get operations from an authors' log ordered by sequence number.
119    ///
120    /// The `from` value will be used as the starting index for log retrieval, if supplied,
121    /// otherwise all operations will be returned.
122    ///
123    /// Returns `None` when either the author or a log with the requested id was not found.
124    async fn get_log(
125        &self,
126        public_key: &PublicKey,
127        log_id: &LogId,
128        from: Option<u64>,
129    ) -> Result<Option<Vec<(Header<Extensions>, Option<Body>)>>, Self::Error>;
130
131    /// Get "raw" header and body bytes from an authors' log ordered by sequence number.
132    ///
133    /// The `from` value will be used as the starting index for log retrieval, if supplied,
134    /// otherwise all operations will be returned.
135    ///
136    /// Returns `None` when either the author or a log with the requested id was not found.
137    async fn get_raw_log(
138        &self,
139        public_key: &PublicKey,
140        log_id: &LogId,
141        from: Option<u64>,
142    ) -> Result<Option<Vec<RawOperation>>, Self::Error>;
143
144    /// Get the log heights of all logs, by any author, which are stored under the passed log id.
145    async fn get_log_heights(&self, log_id: &LogId) -> Result<Vec<(PublicKey, u64)>, Self::Error>;
146
147    /// Get only the latest operation from an authors' log.
148    ///
149    /// Returns None when the author or a log with the requested id was not found.
150    async fn latest_operation(
151        &self,
152        public_key: &PublicKey,
153        log_id: &LogId,
154    ) -> Result<Option<(Header<Extensions>, Option<Body>)>, Self::Error>;
155
156    /// Delete all operations in a log before the given sequence number.
157    ///
158    /// Returns `true` when any operations were deleted, returns `false` when the author or log
159    /// could not be found, or no operations were deleted.
160    async fn delete_operations(
161        &mut self,
162        public_key: &PublicKey,
163        log_id: &LogId,
164        before: u64,
165    ) -> Result<bool, Self::Error>;
166
167    /// Delete a range of operation payloads in an authors' log.
168    ///
169    /// The range of deleted payloads includes it's lower bound `from` but excludes the upper bound
170    /// `to`.
171    ///
172    /// Returns `true` when operations within the requested range were deleted, or `false` when the
173    /// author or log could not be found, or no operations were deleted.
174    async fn delete_payloads(
175        &mut self,
176        public_key: &PublicKey,
177        log_id: &LogId,
178        from: u64,
179        to: u64,
180    ) -> Result<bool, Self::Error>;
181}