[bdk_chain_redesign] Introduce BlockAnchor trait

* Introduce `GraphedTx` struct to access transaction data of graphed
  transactions.
* Ability to insert/access anchors and "seen at" values for graphed
  transactions.
* `Additions` now records changes to anchors and last_seen_at.
This commit is contained in:
志宇
2023-03-24 09:23:36 +08:00
parent 82f9caddab
commit 5ae5fe30eb
21 changed files with 584 additions and 298 deletions

View File

@@ -1,14 +1,18 @@
#[macro_use]
mod common;
use std::collections::BTreeSet;
use bdk_chain::{
chain_graph::*,
collections::HashSet,
sparse_chain,
tx_graph::{self, TxGraph},
tx_graph::{self, GraphedTx, TxGraph},
BlockId, TxHeight,
};
use bitcoin::{OutPoint, PackedLockTime, Script, Sequence, Transaction, TxIn, TxOut, Witness};
use bitcoin::{
BlockHash, OutPoint, PackedLockTime, Script, Sequence, Transaction, TxIn, TxOut, Witness,
};
#[test]
fn test_spent_by() {
@@ -43,7 +47,7 @@ fn test_spent_by() {
output: vec![],
};
let mut cg1 = ChainGraph::default();
let mut cg1 = ChainGraph::<(u32, BlockHash), _>::default();
let _ = cg1
.insert_tx(tx1, TxHeight::Unconfirmed)
.expect("should insert");
@@ -124,7 +128,7 @@ fn update_evicts_conflicting_tx() {
cg
};
let changeset = ChangeSet::<TxHeight> {
let changeset = ChangeSet::<(u32, BlockHash), TxHeight> {
chain: sparse_chain::ChangeSet {
checkpoints: Default::default(),
txids: [
@@ -133,9 +137,10 @@ fn update_evicts_conflicting_tx() {
]
.into(),
},
graph: tx_graph::Additions {
graph: tx_graph::Additions::<(u32, BlockHash)> {
tx: [tx_b2.clone()].into(),
txout: [].into(),
..Default::default()
},
};
assert_eq!(
@@ -149,7 +154,7 @@ fn update_evicts_conflicting_tx() {
{
let cg1 = {
let mut cg = ChainGraph::default();
let mut cg = ChainGraph::<(u32, BlockHash), _>::default();
let _ = cg.insert_checkpoint(cp_a).expect("should insert cp");
let _ = cg.insert_checkpoint(cp_b).expect("should insert cp");
let _ = cg
@@ -203,7 +208,7 @@ fn update_evicts_conflicting_tx() {
cg
};
let changeset = ChangeSet::<TxHeight> {
let changeset = ChangeSet::<(u32, BlockHash), TxHeight> {
chain: sparse_chain::ChangeSet {
checkpoints: [(1, Some(h!("B'")))].into(),
txids: [
@@ -212,9 +217,10 @@ fn update_evicts_conflicting_tx() {
]
.into(),
},
graph: tx_graph::Additions {
graph: tx_graph::Additions::<(u32, BlockHash)> {
tx: [tx_b2].into(),
txout: [].into(),
..Default::default()
},
};
assert_eq!(
@@ -250,7 +256,7 @@ fn chain_graph_new_missing() {
(tx_b.txid(), TxHeight::Confirmed(0))
]
);
let mut graph = TxGraph::default();
let mut graph = TxGraph::<(u32, BlockHash)>::default();
let mut expected_missing = HashSet::new();
expected_missing.insert(tx_a.txid());
@@ -287,7 +293,7 @@ fn chain_graph_new_missing() {
let new_graph = ChainGraph::new(update.clone(), graph.clone()).unwrap();
let expected_graph = {
let mut cg = ChainGraph::<TxHeight>::default();
let mut cg = ChainGraph::<(u32, BlockHash), TxHeight>::default();
let _ = cg
.insert_checkpoint(update.latest_checkpoint().unwrap())
.unwrap();
@@ -342,7 +348,7 @@ fn chain_graph_new_conflicts() {
]
);
let graph = TxGraph::new([tx_a, tx_b, tx_b2]);
let graph = TxGraph::<(u32, BlockHash)>::new([tx_a, tx_b, tx_b2]);
assert!(matches!(
ChainGraph::new(chain, graph),
@@ -352,7 +358,7 @@ fn chain_graph_new_conflicts() {
#[test]
fn test_get_tx_in_chain() {
let mut cg = ChainGraph::default();
let mut cg = ChainGraph::<(u32, BlockHash), _>::default();
let tx = Transaction {
version: 0x01,
lock_time: PackedLockTime(0),
@@ -363,13 +369,21 @@ fn test_get_tx_in_chain() {
let _ = cg.insert_tx(tx.clone(), TxHeight::Unconfirmed).unwrap();
assert_eq!(
cg.get_tx_in_chain(tx.txid()),
Some((&TxHeight::Unconfirmed, &tx))
Some((
&TxHeight::Unconfirmed,
GraphedTx {
txid: tx.txid(),
tx: &tx,
anchors: &BTreeSet::new(),
last_seen: 0
}
))
);
}
#[test]
fn test_iterate_transactions() {
let mut cg = ChainGraph::default();
let mut cg = ChainGraph::<BlockId, _>::default();
let txs = (0..3)
.map(|i| Transaction {
version: i,
@@ -395,9 +409,18 @@ fn test_iterate_transactions() {
assert_eq!(
cg.transactions_in_chain().collect::<Vec<_>>(),
vec![
(&TxHeight::Confirmed(0), &txs[2]),
(&TxHeight::Confirmed(1), &txs[0]),
(&TxHeight::Unconfirmed, &txs[1]),
(
&TxHeight::Confirmed(0),
GraphedTx::from_tx(&txs[2], &BTreeSet::new())
),
(
&TxHeight::Confirmed(1),
GraphedTx::from_tx(&txs[0], &BTreeSet::new())
),
(
&TxHeight::Unconfirmed,
GraphedTx::from_tx(&txs[1], &BTreeSet::new())
),
]
);
}
@@ -457,7 +480,7 @@ fn test_apply_changes_reintroduce_tx() {
// block1, block2a, tx1, tx2a
let mut cg = {
let mut cg = ChainGraph::default();
let mut cg = ChainGraph::<(u32, BlockHash), _>::default();
let _ = cg.insert_checkpoint(block1).unwrap();
let _ = cg.insert_checkpoint(block2a).unwrap();
let _ = cg.insert_tx(tx1, TxHeight::Confirmed(1)).unwrap();
@@ -613,7 +636,7 @@ fn test_evict_descendants() {
let txid_conflict = tx_conflict.txid();
let cg = {
let mut cg = ChainGraph::<TxHeight>::default();
let mut cg = ChainGraph::<(u32, BlockHash), TxHeight>::default();
let _ = cg.insert_checkpoint(block_1);
let _ = cg.insert_checkpoint(block_2a);
let _ = cg.insert_tx(tx_1, TxHeight::Confirmed(1));
@@ -625,7 +648,7 @@ fn test_evict_descendants() {
};
let update = {
let mut cg = ChainGraph::<TxHeight>::default();
let mut cg = ChainGraph::<(u32, BlockHash), TxHeight>::default();
let _ = cg.insert_checkpoint(block_1);
let _ = cg.insert_checkpoint(block_2b);
let _ = cg.insert_tx(tx_conflict.clone(), TxHeight::Confirmed(2));

View File

@@ -1,19 +1,22 @@
#![cfg(feature = "miniscript")]
#[macro_use]
mod common;
use std::collections::BTreeSet;
use bdk_chain::{
keychain::{Balance, KeychainTracker},
miniscript::{
bitcoin::{secp256k1::Secp256k1, OutPoint, PackedLockTime, Transaction, TxOut},
Descriptor,
},
tx_graph::GraphedTx,
BlockId, ConfirmationTime, TxHeight,
};
use bitcoin::TxIn;
use bitcoin::{BlockHash, TxIn};
#[test]
fn test_insert_tx() {
let mut tracker = KeychainTracker::default();
let mut tracker = KeychainTracker::<_, BlockId, _>::default();
let secp = Secp256k1::new();
let (descriptor, _) = Descriptor::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
tracker.add_keychain((), descriptor.clone());
@@ -40,7 +43,10 @@ fn test_insert_tx() {
.chain_graph()
.transactions_in_chain()
.collect::<Vec<_>>(),
vec![(&ConfirmationTime::Unconfirmed, &tx)]
vec![(
&ConfirmationTime::Unconfirmed,
GraphedTx::from_tx(&tx, &BTreeSet::new())
)]
);
assert_eq!(
@@ -66,7 +72,7 @@ fn test_balance() {
One,
Two,
}
let mut tracker = KeychainTracker::<Keychain, TxHeight>::default();
let mut tracker = KeychainTracker::<Keychain, (u32, BlockHash), TxHeight>::default();
let one = Descriptor::from_str("tr([73c5da0a/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#rg247h69").unwrap();
let two = Descriptor::from_str("tr([73c5da0a/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/1/*)#ju05rz2a").unwrap();
tracker.add_keychain(Keychain::One, one);

View File

@@ -2,9 +2,12 @@
mod common;
use bdk_chain::{
collections::*,
tx_graph::{Additions, TxGraph},
tx_graph::{Additions, GraphedTx, TxGraph},
BlockId,
};
use bitcoin::{
hashes::Hash, BlockHash, OutPoint, PackedLockTime, Script, Transaction, TxIn, TxOut, Txid,
};
use bitcoin::{hashes::Hash, OutPoint, PackedLockTime, Script, Transaction, TxIn, TxOut, Txid};
use core::iter;
#[test]
@@ -35,7 +38,7 @@ fn insert_txouts() {
)];
let mut graph = {
let mut graph = TxGraph::default();
let mut graph = TxGraph::<(u32, BlockHash)>::default();
for (outpoint, txout) in &original_ops {
assert_eq!(
graph.insert_txout(*outpoint, txout.clone()),
@@ -69,6 +72,7 @@ fn insert_txouts() {
Additions {
tx: [].into(),
txout: update_ops.into(),
..Default::default()
}
);
@@ -90,7 +94,7 @@ fn insert_tx_graph_doesnt_count_coinbase_as_spent() {
output: vec![],
};
let mut graph = TxGraph::default();
let mut graph = TxGraph::<(u32, BlockHash)>::default();
let _ = graph.insert_tx(tx);
assert!(graph.outspends(OutPoint::null()).is_empty());
assert!(graph.tx_outspends(Txid::all_zeros()).next().is_none());
@@ -120,8 +124,8 @@ fn insert_tx_graph_keeps_track_of_spend() {
output: vec![],
};
let mut graph1 = TxGraph::default();
let mut graph2 = TxGraph::default();
let mut graph1 = TxGraph::<(u32, BlockHash)>::default();
let mut graph2 = TxGraph::<(u32, BlockHash)>::default();
// insert in different order
let _ = graph1.insert_tx(tx1.clone());
@@ -149,14 +153,17 @@ fn insert_tx_can_retrieve_full_tx_from_graph() {
output: vec![TxOut::default()],
};
let mut graph = TxGraph::default();
let mut graph = TxGraph::<BlockId>::default();
let _ = graph.insert_tx(tx.clone());
assert_eq!(graph.get_tx(tx.txid()), Some(&tx));
assert_eq!(
graph.get_tx(tx.txid()),
Some(GraphedTx::from_tx(&tx, &BTreeSet::new()))
);
}
#[test]
fn insert_tx_displaces_txouts() {
let mut tx_graph = TxGraph::default();
let mut tx_graph = TxGraph::<(u32, BlockHash)>::default();
let tx = Transaction {
version: 0x01,
lock_time: PackedLockTime(0),
@@ -212,7 +219,7 @@ fn insert_tx_displaces_txouts() {
#[test]
fn insert_txout_does_not_displace_tx() {
let mut tx_graph = TxGraph::default();
let mut tx_graph = TxGraph::<(u32, BlockHash)>::default();
let tx = Transaction {
version: 0x01,
lock_time: PackedLockTime(0),
@@ -268,7 +275,7 @@ fn insert_txout_does_not_displace_tx() {
#[test]
fn test_calculate_fee() {
let mut graph = TxGraph::default();
let mut graph = TxGraph::<(u32, BlockHash)>::default();
let intx1 = Transaction {
version: 0x01,
lock_time: PackedLockTime(0),
@@ -362,7 +369,7 @@ fn test_calculate_fee_on_coinbase() {
output: vec![TxOut::default()],
};
let graph = TxGraph::default();
let graph = TxGraph::<(u32, BlockHash)>::default();
assert_eq!(graph.calculate_fee(&tx), Some(0));
}
@@ -404,7 +411,7 @@ fn test_conflicting_descendants() {
let txid_a = tx_a.txid();
let txid_b = tx_b.txid();
let mut graph = TxGraph::default();
let mut graph = TxGraph::<(u32, BlockHash)>::default();
let _ = graph.insert_tx(tx_a);
let _ = graph.insert_tx(tx_b);
@@ -480,7 +487,7 @@ fn test_descendants_no_repeat() {
})
.collect::<Vec<_>>();
let mut graph = TxGraph::default();
let mut graph = TxGraph::<(u32, BlockHash)>::default();
let mut expected_txids = BTreeSet::new();
// these are NOT descendants of `tx_a`