• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payjoin / rust-payjoin / 23490459045

24 Mar 2026 12:54PM UTC coverage: 84.165% (+0.04%) from 84.125%
23490459045

Pull #1376

github

web-flow
Merge 39a1c67ad into 3c5a384f5
Pull Request #1376: Guard concurrent sends with exclusive DB lock and URI/RK checks

63 of 69 new or added lines in 4 files covered. (91.3%)

5 existing lines in 1 file now uncovered.

10705 of 12719 relevant lines covered (84.17%)

414.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/payjoin-cli/src/db/mod.rs
1
use std::path::Path;
2

3
use payjoin::bitcoin::consensus::encode::serialize;
4
use payjoin::bitcoin::OutPoint;
5
use r2d2::Pool;
6
use r2d2_sqlite::SqliteConnectionManager;
7
use rusqlite::{params, Connection};
8

9
pub(crate) mod error;
10
use error::*;
11

12
pub(crate) fn now() -> i64 {
22✔
13
    std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as i64
22✔
14
}
22✔
15

16
pub(crate) const DB_PATH: &str = "payjoin.sqlite";
17

18
#[derive(Debug)]
19
pub(crate) struct Database(Pool<SqliteConnectionManager>);
20

21
impl Database {
22
    pub(crate) fn create(path: impl AsRef<Path>) -> Result<Self> {
13✔
23
        let manager = SqliteConnectionManager::file(path.as_ref())
13✔
24
            .with_init(|conn| conn.execute_batch("PRAGMA locking_mode = EXCLUSIVE;"));
130✔
25
        let pool = Pool::new(manager)?;
13✔
26

27
        // Initialize database schema
28
        let conn = pool.get()?;
13✔
29
        Self::init_schema(&conn)?;
13✔
30

31
        Ok(Self(pool))
13✔
32
    }
13✔
33

34
    fn init_schema(conn: &Connection) -> Result<()> {
16✔
35
        // Enable foreign keys
36
        conn.execute("PRAGMA foreign_keys = ON", [])?;
16✔
37

38
        conn.execute(
16✔
39
            "CREATE TABLE IF NOT EXISTS send_sessions (
16✔
40
                session_id INTEGER PRIMARY KEY AUTOINCREMENT,
16✔
41
                pj_uri TEXT NOT NULL,
16✔
42
                receiver_pubkey BLOB NOT NULL,
16✔
43
                completed_at INTEGER
16✔
44
            )",
16✔
45
            [],
16✔
UNCOV
46
        )?;
×
47

48
        conn.execute(
16✔
49
            "CREATE TABLE IF NOT EXISTS receive_sessions (
16✔
50
                session_id INTEGER PRIMARY KEY AUTOINCREMENT,
16✔
51
                completed_at INTEGER
16✔
52
            )",
16✔
53
            [],
16✔
UNCOV
54
        )?;
×
55

56
        conn.execute(
16✔
57
            "CREATE TABLE IF NOT EXISTS send_session_events (
16✔
58
                id INTEGER PRIMARY KEY AUTOINCREMENT,
16✔
59
                session_id INTEGER NOT NULL,
16✔
60
                event_data TEXT NOT NULL,
16✔
61
                created_at INTEGER NOT NULL,
16✔
62
                FOREIGN KEY(session_id) REFERENCES send_sessions(session_id)
16✔
63
            )",
16✔
64
            [],
16✔
UNCOV
65
        )?;
×
66

67
        conn.execute(
16✔
68
            "CREATE TABLE IF NOT EXISTS receive_session_events (
16✔
69
                id INTEGER PRIMARY KEY AUTOINCREMENT,
16✔
70
                session_id INTEGER NOT NULL,
16✔
71
                event_data TEXT NOT NULL,
16✔
72
                created_at INTEGER NOT NULL,
16✔
73
                FOREIGN KEY(session_id) REFERENCES receive_sessions(session_id)
16✔
74
            )",
16✔
75
            [],
16✔
UNCOV
76
        )?;
×
77

78
        conn.execute(
16✔
79
            "CREATE TABLE IF NOT EXISTS inputs_seen (
16✔
80
                outpoint BLOB PRIMARY KEY,
16✔
81
                created_at INTEGER NOT NULL
16✔
82
            )",
16✔
83
            [],
16✔
UNCOV
84
        )?;
×
85

86
        Ok(())
16✔
87
    }
16✔
88

89
    pub(crate) fn get_connection(&self) -> Result<r2d2::PooledConnection<SqliteConnectionManager>> {
44✔
90
        Ok(self.0.get()?)
44✔
91
    }
44✔
92
    /// Inserts the input and returns true if the input was seen before, false otherwise.
93
    pub(crate) fn insert_input_seen_before(&self, input: OutPoint) -> Result<bool> {
4✔
94
        let conn = self.get_connection()?;
4✔
95
        let key = serialize(&input);
4✔
96

97
        let was_seen_before = conn.execute(
4✔
98
            "INSERT OR IGNORE INTO inputs_seen (outpoint, created_at) VALUES (?1, ?2)",
4✔
99
            params![key, now()],
4✔
100
        )? == 0;
4✔
101

102
        Ok(was_seen_before)
4✔
103
    }
4✔
104
}
105

106
#[cfg(feature = "v2")]
107
pub(crate) mod v2;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc