Skip to content

Commit 32d785f

Browse files
committed
fix: remove panics in tx_graph persistence fns
Since rusqlite also returns an error. Also refactored the tests to check if correct error is being returned.
1 parent 430d742 commit 32d785f

File tree

2 files changed

+68
-27
lines changed

2 files changed

+68
-27
lines changed

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![warn(missing_docs)]
22
//! This module contains the crate's error type.
3+
use bdk_chain::bitcoin;
34
use std::io::Error as IoError;
45

56
#[derive(Debug, thiserror::Error)]
@@ -34,4 +35,8 @@ pub enum StoreError {
3435
/// [`BlockHash`]: <https://docs.rs/bitcoin/latest/bitcoin/struct.BlockHash.html>
3536
#[error("BlockHash deserialization error: {0}")]
3637
BlockHashFromSlice(#[from] bdk_chain::bitcoin::hashes::FromSliceError),
38+
/// Error thrown when tx corresponding to txid is not found while persisting
39+
/// anchors, last_seen, last_evicted or first_seen.
40+
#[error("Tx corresponding to txid is missing")]
41+
TxMissing(bitcoin::Txid),
3742
}

src/lib.rs

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ impl Store {
481481
bytes[4..].copy_from_slice(&anchor_block.hash.to_byte_array());
482482
table.insert((txid.to_byte_array(), bytes), &anchor.metadata())?;
483483
} else {
484-
panic!("txn corresponding to anchor must exist");
484+
return Err(StoreError::TxMissing(*txid));
485485
}
486486
}
487487
Ok(())
@@ -504,7 +504,7 @@ impl Store {
504504
if txs_table.get(txid.to_byte_array())?.is_some() || found {
505505
table.insert(txid.to_byte_array(), *last_seen_time)?;
506506
} else {
507-
panic!("txn must exist before persisting last_seen");
507+
return Err(StoreError::TxMissing(*txid));
508508
}
509509
}
510510
Ok(())
@@ -527,7 +527,7 @@ impl Store {
527527
if txs_table.get(txid.to_byte_array())?.is_some() || found {
528528
table.insert(txid.to_byte_array(), last_evicted_time)?;
529529
} else {
530-
panic!("txn must exist before persisting last_evicted");
530+
return Err(StoreError::TxMissing(*txid));
531531
}
532532
}
533533
Ok(())
@@ -550,7 +550,7 @@ impl Store {
550550
if txs_table.get(txid.to_byte_array())?.is_some() || found {
551551
table.insert(txid.to_byte_array(), first_seen_time)?;
552552
} else {
553-
panic!("txn must exist before persisting first_seen");
553+
return Err(StoreError::TxMissing(*txid));
554554
}
555555
}
556556
Ok(())
@@ -1165,28 +1165,39 @@ mod test {
11651165
}
11661166

11671167
#[test]
1168-
#[should_panic]
11691168
fn test_last_seen_missing_txn() {
11701169
// to hit the branch for the panic case in persist_last_seen
11711170
let tmpfile = NamedTempFile::new().unwrap();
11721171
let db = create_db(tmpfile.path());
11731172
let store = create_test_store(Arc::new(db), "wallet1");
11741173

1175-
let last_seen: BTreeMap<Txid, u64> =
1176-
[(hash!("B"), 100), (hash!("T"), 120), (hash!("C"), 121)].into();
1174+
let tx1 = Arc::new(create_one_inp_one_out_tx(
1175+
Txid::from_byte_array([0; 32]),
1176+
30_000,
1177+
));
1178+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
1179+
1180+
let last_seen: BTreeMap<Txid, u64> = [
1181+
(hash!("B"), 100),
1182+
(tx1.compute_txid(), 120),
1183+
(tx2.compute_txid(), 121),
1184+
]
1185+
.into();
11771186

11781187
let write_tx = store.db.begin_write().unwrap();
11791188
let _ = write_tx.open_table(store.txs_table_defn()).unwrap();
11801189
let _ = write_tx.open_table(store.last_seen_defn()).unwrap();
11811190
write_tx.commit().unwrap();
11821191

1183-
let txs: BTreeSet<Arc<Transaction>> = BTreeSet::new();
1192+
let txs: BTreeSet<Arc<Transaction>> = [tx1, tx2].into();
11841193

11851194
let write_tx = store.db.begin_write().unwrap();
11861195
let read_tx = store.db.begin_read().unwrap();
1187-
store
1188-
.persist_last_seen(&write_tx, &read_tx, &last_seen, &txs)
1189-
.unwrap();
1196+
match store.persist_last_seen(&write_tx, &read_tx, &last_seen, &txs) {
1197+
Ok(_) => panic!("should give error since tx missing"),
1198+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1199+
Err(_) => panic!("error should only be due to missing tx"),
1200+
}
11901201
write_tx.commit().unwrap();
11911202
}
11921203

@@ -1262,15 +1273,24 @@ mod test {
12621273
}
12631274

12641275
#[test]
1265-
#[should_panic]
12661276
fn test_last_evicted_missing_txs() {
12671277
// to hit the branch for the panic case in persist_last_evicted
12681278
let tmpfile = NamedTempFile::new().unwrap();
12691279
let db = create_db(tmpfile.path());
12701280
let store = create_test_store(Arc::new(db), "wallet1");
12711281

1272-
let last_evicted: BTreeMap<Txid, u64> =
1273-
[(hash!("B"), 100), (hash!("D"), 120), (hash!("K"), 132)].into();
1282+
let tx1 = Arc::new(create_one_inp_one_out_tx(
1283+
Txid::from_byte_array([0; 32]),
1284+
30_000,
1285+
));
1286+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
1287+
1288+
let last_evicted: BTreeMap<Txid, u64> = [
1289+
(hash!("B"), 100),
1290+
(tx1.compute_txid(), 120),
1291+
(tx2.compute_txid(), 132),
1292+
]
1293+
.into();
12741294

12751295
let write_tx = store.db.begin_write().unwrap();
12761296
let _ = write_tx.open_table(store.txs_table_defn()).unwrap();
@@ -1279,13 +1299,15 @@ mod test {
12791299
.unwrap();
12801300
write_tx.commit().unwrap();
12811301

1282-
let txs: BTreeSet<Arc<Transaction>> = BTreeSet::new();
1302+
let txs: BTreeSet<Arc<Transaction>> = [tx1, tx2].into();
12831303

12841304
let write_tx = store.db.begin_write().unwrap();
12851305
let read_tx = store.db.begin_read().unwrap();
1286-
store
1287-
.persist_last_evicted(&write_tx, &read_tx, &last_evicted, &txs)
1288-
.unwrap();
1306+
match store.persist_last_evicted(&write_tx, &read_tx, &last_evicted, &txs) {
1307+
Ok(_) => panic!("should give error since tx missing"),
1308+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1309+
Err(_) => panic!("error should only be due to missing tx"),
1310+
}
12891311
write_tx.commit().unwrap();
12901312
}
12911313

@@ -1357,28 +1379,39 @@ mod test {
13571379
}
13581380

13591381
#[test]
1360-
#[should_panic]
13611382
fn test_first_seen_missing_tx() {
13621383
// to hit the branch for the panic case persist_first_seen
13631384
let tmpfile = NamedTempFile::new().unwrap();
13641385
let db = create_db(tmpfile.path());
13651386
let store = create_test_store(Arc::new(db), "wallet1");
13661387

1367-
let first_seen: BTreeMap<Txid, u64> =
1368-
[(hash!("B"), 100), (hash!("T"), 120), (hash!("C"), 121)].into();
1388+
let tx1 = Arc::new(create_one_inp_one_out_tx(
1389+
Txid::from_byte_array([0; 32]),
1390+
30_000,
1391+
));
1392+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
1393+
1394+
let first_seen: BTreeMap<Txid, u64> = [
1395+
(hash!("B"), 100),
1396+
(tx1.compute_txid(), 120),
1397+
(tx2.compute_txid(), 121),
1398+
]
1399+
.into();
13691400

13701401
let write_tx = store.db.begin_write().unwrap();
13711402
let _ = write_tx.open_table(store.txs_table_defn()).unwrap();
13721403
let _ = write_tx.open_table(store.first_seen_table_defn()).unwrap();
13731404
write_tx.commit().unwrap();
13741405

1375-
let txs: BTreeSet<Arc<Transaction>> = BTreeSet::new();
1406+
let txs: BTreeSet<Arc<Transaction>> = [tx1, tx2].into();
13761407

13771408
let write_tx = store.db.begin_write().unwrap();
13781409
let read_tx = store.db.begin_read().unwrap();
1379-
store
1380-
.persist_first_seen(&write_tx, &read_tx, &first_seen, &txs)
1381-
.unwrap();
1410+
match store.persist_first_seen(&write_tx, &read_tx, &first_seen, &txs) {
1411+
Ok(_) => panic!("should give error since tx missing"),
1412+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1413+
Err(_) => panic!("error should only be due to missing tx"),
1414+
}
13821415
write_tx.commit().unwrap();
13831416
}
13841417

@@ -1577,7 +1610,6 @@ mod test {
15771610
}
15781611

15791612
#[test]
1580-
#[should_panic]
15811613
fn test_anchors_missing_tx() {
15821614
// to hit the branch for the panic case in persist_anchors
15831615
let tmpfile = NamedTempFile::new().unwrap();
@@ -1602,7 +1634,11 @@ mod test {
16021634

16031635
let write_tx = store.db.begin_write().unwrap();
16041636
let read_tx = store.db.begin_read().unwrap();
1605-
let _ = store.persist_anchors(&write_tx, &read_tx, &anchors_missing_txs, &txs);
1637+
match store.persist_anchors(&write_tx, &read_tx, &anchors_missing_txs, &txs) {
1638+
Ok(_) => panic!("should give error since tx missing"),
1639+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1640+
Err(_) => panic!("error should only be due to missing tx"),
1641+
}
16061642
read_tx.close().unwrap();
16071643
write_tx.commit().unwrap();
16081644
}

0 commit comments

Comments
 (0)