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}