Skip to content

Commit ebf2b71

Browse files
committed
feat: add SQLite database for payjoin session persistence
- Create db.rs with Database, SenderPersister and ReceiverPersister - Implement SessionPersister trait for both sender and receiver - Add session ID management and query methods - Add input deduplication tracking to prevent probing attacks - Add timestamp formatting utilities
1 parent 46e1ab7 commit ebf2b71

File tree

2 files changed

+462
-0
lines changed

2 files changed

+462
-0
lines changed

src/error.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ pub enum BDKCliError {
140140
#[cfg(feature = "payjoin")]
141141
#[error("Payjoin create request error: {0}")]
142142
PayjoinCreateRequest(#[from] payjoin::send::v2::CreateRequestError),
143+
144+
#[cfg(feature = "payjoin")]
145+
#[error("Payjoin database error: {0}")]
146+
PayjoinDb(#[from] PayjoinDbError),
143147
}
144148

145149
impl From<ExtractTxError> for BDKCliError {
@@ -168,3 +172,64 @@ impl From<bdk_wallet::rusqlite::Error> for BDKCliError {
168172
BDKCliError::RusqliteError(Box::new(err))
169173
}
170174
}
175+
176+
/// Error type for payjoin database operations
177+
#[cfg(feature = "payjoin")]
178+
#[derive(Debug)]
179+
pub enum PayjoinDbError {
180+
/// SQLite database error
181+
Rusqlite(bdk_wallet::rusqlite::Error),
182+
/// JSON serialization error
183+
Serialize(serde_json::Error),
184+
/// JSON deserialization error
185+
Deserialize(serde_json::Error),
186+
}
187+
188+
#[cfg(feature = "payjoin")]
189+
impl std::fmt::Display for PayjoinDbError {
190+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191+
match self {
192+
PayjoinDbError::Rusqlite(e) => write!(f, "Database operation failed: {e}"),
193+
PayjoinDbError::Serialize(e) => write!(f, "Serialization failed: {e}"),
194+
PayjoinDbError::Deserialize(e) => write!(f, "Deserialization failed: {e}"),
195+
}
196+
}
197+
}
198+
199+
#[cfg(feature = "payjoin")]
200+
impl std::error::Error for PayjoinDbError {
201+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
202+
match self {
203+
PayjoinDbError::Rusqlite(e) => Some(e),
204+
PayjoinDbError::Serialize(e) => Some(e),
205+
PayjoinDbError::Deserialize(e) => Some(e),
206+
}
207+
}
208+
}
209+
210+
#[cfg(feature = "payjoin")]
211+
impl From<bdk_wallet::rusqlite::Error> for PayjoinDbError {
212+
fn from(error: bdk_wallet::rusqlite::Error) -> Self {
213+
PayjoinDbError::Rusqlite(error)
214+
}
215+
}
216+
217+
#[cfg(feature = "payjoin")]
218+
impl From<PayjoinDbError> for payjoin::ImplementationError {
219+
fn from(error: PayjoinDbError) -> Self {
220+
payjoin::ImplementationError::new(error)
221+
}
222+
}
223+
224+
#[cfg(feature = "payjoin")]
225+
impl<ApiErr, StorageErr, ErrorState>
226+
From<payjoin::persist::PersistedError<ApiErr, StorageErr, ErrorState>> for BDKCliError
227+
where
228+
ApiErr: std::error::Error,
229+
StorageErr: std::error::Error,
230+
ErrorState: std::fmt::Debug,
231+
{
232+
fn from(e: payjoin::persist::PersistedError<ApiErr, StorageErr, ErrorState>) -> Self {
233+
BDKCliError::Generic(e.to_string())
234+
}
235+
}

0 commit comments

Comments
 (0)