|
1 |
| -//! Module for stuff |
| 1 | +//! Support for persisting `bdk_chain` structures to SQLite using [`rusqlite`]. |
2 | 2 |
|
3 | 3 | use crate::*;
|
4 | 4 | use core::str::FromStr;
|
@@ -376,8 +376,15 @@ where
|
376 | 376 | "REPLACE INTO {}(txid, block_height, block_hash, anchor) VALUES(:txid, :block_height, :block_hash, jsonb(:anchor))",
|
377 | 377 | Self::ANCHORS_TABLE_NAME,
|
378 | 378 | ))?;
|
| 379 | + let mut statement_txid = db_tx.prepare_cached(&format!( |
| 380 | + "INSERT OR IGNORE INTO {}(txid) VALUES(:txid)", |
| 381 | + Self::TXS_TABLE_NAME, |
| 382 | + ))?; |
379 | 383 | for (anchor, txid) in &self.anchors {
|
380 | 384 | let anchor_block = anchor.anchor_block();
|
| 385 | + statement_txid.execute(named_params! { |
| 386 | + ":txid": Impl(*txid) |
| 387 | + })?; |
381 | 388 | statement.execute(named_params! {
|
382 | 389 | ":txid": Impl(*txid),
|
383 | 390 | ":block_height": anchor_block.height,
|
@@ -529,3 +536,70 @@ impl keychain_txout::ChangeSet {
|
529 | 536 | Ok(())
|
530 | 537 | }
|
531 | 538 | }
|
| 539 | + |
| 540 | +#[cfg(test)] |
| 541 | +mod test { |
| 542 | + use super::*; |
| 543 | + |
| 544 | + use bdk_testenv::{anyhow, hash}; |
| 545 | + use bitcoin::{absolute, transaction, TxIn, TxOut}; |
| 546 | + |
| 547 | + #[test] |
| 548 | + fn can_persist_anchors_and_txs_independently() -> anyhow::Result<()> { |
| 549 | + type ChangeSet = tx_graph::ChangeSet<BlockId>; |
| 550 | + let mut conn = rusqlite::Connection::open_in_memory()?; |
| 551 | + |
| 552 | + // init tables |
| 553 | + { |
| 554 | + let db_tx = conn.transaction()?; |
| 555 | + ChangeSet::init_sqlite_tables(&db_tx)?; |
| 556 | + db_tx.commit()?; |
| 557 | + } |
| 558 | + |
| 559 | + let tx = bitcoin::Transaction { |
| 560 | + version: transaction::Version::TWO, |
| 561 | + lock_time: absolute::LockTime::ZERO, |
| 562 | + input: vec![TxIn::default()], |
| 563 | + output: vec![TxOut::NULL], |
| 564 | + }; |
| 565 | + let tx = Arc::new(tx); |
| 566 | + let txid = tx.compute_txid(); |
| 567 | + let anchor = BlockId { |
| 568 | + height: 21, |
| 569 | + hash: hash!("anchor"), |
| 570 | + }; |
| 571 | + |
| 572 | + // First persist the anchor |
| 573 | + { |
| 574 | + let changeset = ChangeSet { |
| 575 | + anchors: [(anchor, txid)].into(), |
| 576 | + ..Default::default() |
| 577 | + }; |
| 578 | + let db_tx = conn.transaction()?; |
| 579 | + changeset.persist_to_sqlite(&db_tx)?; |
| 580 | + db_tx.commit()?; |
| 581 | + } |
| 582 | + |
| 583 | + // Now persist the tx |
| 584 | + { |
| 585 | + let changeset = ChangeSet { |
| 586 | + txs: [tx.clone()].into(), |
| 587 | + ..Default::default() |
| 588 | + }; |
| 589 | + let db_tx = conn.transaction()?; |
| 590 | + changeset.persist_to_sqlite(&db_tx)?; |
| 591 | + db_tx.commit()?; |
| 592 | + } |
| 593 | + |
| 594 | + // Loading changeset from sqlite should succeed |
| 595 | + { |
| 596 | + let db_tx = conn.transaction()?; |
| 597 | + let changeset = ChangeSet::from_sqlite(&db_tx)?; |
| 598 | + db_tx.commit()?; |
| 599 | + assert!(changeset.txs.contains(&tx)); |
| 600 | + assert!(changeset.anchors.contains(&(anchor, txid))); |
| 601 | + } |
| 602 | + |
| 603 | + Ok(()) |
| 604 | + } |
| 605 | +} |
0 commit comments