p2panda_store/
lib.rs

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