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}