Skip to content

Commit 2e74115

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 368b8b4 commit 2e74115

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,10 +140,75 @@ 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 {
146150
fn from(value: ExtractTxError) -> Self {
147151
BDKCliError::PsbtExtractTxError(Box::new(value))
148152
}
149153
}
154+
155+
/// Error type for payjoin database operations
156+
#[cfg(feature = "payjoin")]
157+
#[derive(Debug)]
158+
pub enum PayjoinDbError {
159+
/// SQLite database error
160+
Rusqlite(bdk_wallet::rusqlite::Error),
161+
/// JSON serialization error
162+
Serialize(serde_json::Error),
163+
/// JSON deserialization error
164+
Deserialize(serde_json::Error),
165+
}
166+
167+
#[cfg(feature = "payjoin")]
168+
impl std::fmt::Display for PayjoinDbError {
169+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170+
match self {
171+
PayjoinDbError::Rusqlite(e) => write!(f, "Database operation failed: {e}"),
172+
PayjoinDbError::Serialize(e) => write!(f, "Serialization failed: {e}"),
173+
PayjoinDbError::Deserialize(e) => write!(f, "Deserialization failed: {e}"),
174+
}
175+
}
176+
}
177+
178+
#[cfg(feature = "payjoin")]
179+
impl std::error::Error for PayjoinDbError {
180+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
181+
match self {
182+
PayjoinDbError::Rusqlite(e) => Some(e),
183+
PayjoinDbError::Serialize(e) => Some(e),
184+
PayjoinDbError::Deserialize(e) => Some(e),
185+
}
186+
}
187+
}
188+
189+
#[cfg(feature = "payjoin")]
190+
impl From<bdk_wallet::rusqlite::Error> for PayjoinDbError {
191+
fn from(error: bdk_wallet::rusqlite::Error) -> Self {
192+
PayjoinDbError::Rusqlite(error)
193+
}
194+
}
195+
196+
#[cfg(feature = "payjoin")]
197+
impl From<PayjoinDbError> for payjoin::ImplementationError {
198+
fn from(error: PayjoinDbError) -> Self {
199+
payjoin::ImplementationError::new(error)
200+
}
201+
}
202+
203+
#[cfg(feature = "payjoin")]
204+
impl<ApiErr, StorageErr, ErrorState>
205+
From<payjoin::persist::PersistedError<ApiErr, StorageErr, ErrorState>> for BDKCliError
206+
where
207+
ApiErr: std::error::Error,
208+
StorageErr: std::error::Error,
209+
ErrorState: std::fmt::Debug,
210+
{
211+
fn from(e: payjoin::persist::PersistedError<ApiErr, StorageErr, ErrorState>) -> Self {
212+
BDKCliError::Generic(e.to_string())
213+
}
214+
}

0 commit comments

Comments
 (0)