Skip to content

Commit f7a83ef

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 593471b commit f7a83ef

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
@@ -482,7 +482,7 @@ impl Store {
482482
bytes[4..].copy_from_slice(&anchor_block.hash.to_byte_array());
483483
table.insert((txid.to_byte_array(), bytes), &anchor.metadata())?;
484484
} else {
485-
panic!("txn corresponding to anchor must exist");
485+
return Err(StoreError::TxMissing(*txid));
486486
}
487487
}
488488
Ok(())
@@ -505,7 +505,7 @@ impl Store {
505505
if txs_table.get(txid.to_byte_array())?.is_some() || found {
506506
table.insert(txid.to_byte_array(), *last_seen_time)?;
507507
} else {
508-
panic!("txn must exist before persisting last_seen");
508+
return Err(StoreError::TxMissing(*txid));
509509
}
510510
}
511511
Ok(())
@@ -528,7 +528,7 @@ impl Store {
528528
if txs_table.get(txid.to_byte_array())?.is_some() || found {
529529
table.insert(txid.to_byte_array(), last_evicted_time)?;
530530
} else {
531-
panic!("txn must exist before persisting last_evicted");
531+
return Err(StoreError::TxMissing(*txid));
532532
}
533533
}
534534
Ok(())
@@ -551,7 +551,7 @@ impl Store {
551551
if txs_table.get(txid.to_byte_array())?.is_some() || found {
552552
table.insert(txid.to_byte_array(), first_seen_time)?;
553553
} else {
554-
panic!("txn must exist before persisting first_seen");
554+
return Err(StoreError::TxMissing(*txid));
555555
}
556556
}
557557
Ok(())
@@ -1160,28 +1160,39 @@ mod test {
11601160
}
11611161

11621162
#[test]
1163-
#[should_panic]
11641163
fn test_last_seen_missing_txn() {
11651164
// to hit the branch for the panic case in persist_last_seen
11661165
let tmpfile = NamedTempFile::new().unwrap();
11671166
let db = create_db(tmpfile.path());
11681167
let store = create_test_store(Arc::new(db), "wallet1");
11691168

1170-
let last_seen: BTreeMap<Txid, u64> =
1171-
[(hash!("B"), 100), (hash!("T"), 120), (hash!("C"), 121)].into();
1169+
let tx1 = Arc::new(create_one_inp_one_out_tx(
1170+
Txid::from_byte_array([0; 32]),
1171+
30_000,
1172+
));
1173+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
1174+
1175+
let last_seen: BTreeMap<Txid, u64> = [
1176+
(hash!("B"), 100),
1177+
(tx1.compute_txid(), 120),
1178+
(tx2.compute_txid(), 121),
1179+
]
1180+
.into();
11721181

11731182
let write_tx = store.db.begin_write().unwrap();
11741183
let _ = write_tx.open_table(store.txs_table_defn()).unwrap();
11751184
let _ = write_tx.open_table(store.last_seen_defn()).unwrap();
11761185
write_tx.commit().unwrap();
11771186

1178-
let txs: BTreeSet<Arc<Transaction>> = BTreeSet::new();
1187+
let txs: BTreeSet<Arc<Transaction>> = [tx1, tx2].into();
11791188

11801189
let write_tx = store.db.begin_write().unwrap();
11811190
let read_tx = store.db.begin_read().unwrap();
1182-
store
1183-
.persist_last_seen(&write_tx, &read_tx, &last_seen, &txs)
1184-
.unwrap();
1191+
match store.persist_last_seen(&write_tx, &read_tx, &last_seen, &txs) {
1192+
Ok(_) => panic!("should give error since tx missing"),
1193+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1194+
Err(_) => panic!("error should only be due to missing tx"),
1195+
}
11851196
write_tx.commit().unwrap();
11861197
}
11871198

@@ -1257,15 +1268,24 @@ mod test {
12571268
}
12581269

12591270
#[test]
1260-
#[should_panic]
12611271
fn test_last_evicted_missing_txs() {
12621272
// to hit the branch for the panic case in persist_last_evicted
12631273
let tmpfile = NamedTempFile::new().unwrap();
12641274
let db = create_db(tmpfile.path());
12651275
let store = create_test_store(Arc::new(db), "wallet1");
12661276

1267-
let last_evicted: BTreeMap<Txid, u64> =
1268-
[(hash!("B"), 100), (hash!("D"), 120), (hash!("K"), 132)].into();
1277+
let tx1 = Arc::new(create_one_inp_one_out_tx(
1278+
Txid::from_byte_array([0; 32]),
1279+
30_000,
1280+
));
1281+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
1282+
1283+
let last_evicted: BTreeMap<Txid, u64> = [
1284+
(hash!("B"), 100),
1285+
(tx1.compute_txid(), 120),
1286+
(tx2.compute_txid(), 132),
1287+
]
1288+
.into();
12691289

12701290
let write_tx = store.db.begin_write().unwrap();
12711291
let _ = write_tx.open_table(store.txs_table_defn()).unwrap();
@@ -1274,13 +1294,15 @@ mod test {
12741294
.unwrap();
12751295
write_tx.commit().unwrap();
12761296

1277-
let txs: BTreeSet<Arc<Transaction>> = BTreeSet::new();
1297+
let txs: BTreeSet<Arc<Transaction>> = [tx1, tx2].into();
12781298

12791299
let write_tx = store.db.begin_write().unwrap();
12801300
let read_tx = store.db.begin_read().unwrap();
1281-
store
1282-
.persist_last_evicted(&write_tx, &read_tx, &last_evicted, &txs)
1283-
.unwrap();
1301+
match store.persist_last_evicted(&write_tx, &read_tx, &last_evicted, &txs) {
1302+
Ok(_) => panic!("should give error since tx missing"),
1303+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1304+
Err(_) => panic!("error should only be due to missing tx"),
1305+
}
12841306
write_tx.commit().unwrap();
12851307
}
12861308

@@ -1352,28 +1374,39 @@ mod test {
13521374
}
13531375

13541376
#[test]
1355-
#[should_panic]
13561377
fn test_first_seen_missing_tx() {
13571378
// to hit the branch for the panic case persist_first_seen
13581379
let tmpfile = NamedTempFile::new().unwrap();
13591380
let db = create_db(tmpfile.path());
13601381
let store = create_test_store(Arc::new(db), "wallet1");
13611382

1362-
let first_seen: BTreeMap<Txid, u64> =
1363-
[(hash!("B"), 100), (hash!("T"), 120), (hash!("C"), 121)].into();
1383+
let tx1 = Arc::new(create_one_inp_one_out_tx(
1384+
Txid::from_byte_array([0; 32]),
1385+
30_000,
1386+
));
1387+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
1388+
1389+
let first_seen: BTreeMap<Txid, u64> = [
1390+
(hash!("B"), 100),
1391+
(tx1.compute_txid(), 120),
1392+
(tx2.compute_txid(), 121),
1393+
]
1394+
.into();
13641395

13651396
let write_tx = store.db.begin_write().unwrap();
13661397
let _ = write_tx.open_table(store.txs_table_defn()).unwrap();
13671398
let _ = write_tx.open_table(store.first_seen_table_defn()).unwrap();
13681399
write_tx.commit().unwrap();
13691400

1370-
let txs: BTreeSet<Arc<Transaction>> = BTreeSet::new();
1401+
let txs: BTreeSet<Arc<Transaction>> = [tx1, tx2].into();
13711402

13721403
let write_tx = store.db.begin_write().unwrap();
13731404
let read_tx = store.db.begin_read().unwrap();
1374-
store
1375-
.persist_first_seen(&write_tx, &read_tx, &first_seen, &txs)
1376-
.unwrap();
1405+
match store.persist_first_seen(&write_tx, &read_tx, &first_seen, &txs) {
1406+
Ok(_) => panic!("should give error since tx missing"),
1407+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1408+
Err(_) => panic!("error should only be due to missing tx"),
1409+
}
13771410
write_tx.commit().unwrap();
13781411
}
13791412

@@ -1572,7 +1605,6 @@ mod test {
15721605
}
15731606

15741607
#[test]
1575-
#[should_panic]
15761608
fn test_anchors_missing_tx() {
15771609
// to hit the branch for the panic case in persist_anchors
15781610
let tmpfile = NamedTempFile::new().unwrap();
@@ -1597,7 +1629,11 @@ mod test {
15971629

15981630
let write_tx = store.db.begin_write().unwrap();
15991631
let read_tx = store.db.begin_read().unwrap();
1600-
let _ = store.persist_anchors(&write_tx, &read_tx, &anchors_missing_txs, &txs);
1632+
match store.persist_anchors(&write_tx, &read_tx, &anchors_missing_txs, &txs) {
1633+
Ok(_) => panic!("should give error since tx missing"),
1634+
Err(StoreError::TxMissing(txid)) => assert_eq!(txid, hash!("B")),
1635+
Err(_) => panic!("error should only be due to missing tx"),
1636+
}
16011637
read_tx.close().unwrap();
16021638
write_tx.commit().unwrap();
16031639
}

0 commit comments

Comments
 (0)