Skip to main content

p2panda_store/
operations.rs

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