Merge bitcoindevkit/bdk#1325: Add map_anchors
for TxGraph
5489f905a434ecc06867603c7c421e3e50d993ca feat(chain): add `map_anchors` for `TxGraph` and `ChangeSet` (Antonio Yang) 022d5a21cff6c46fb869f8fd538b4026e531ee57 test(chain) use `Anchor` generic on `init_graph` (Antonio Yang) Pull request description: ### Description Fix #1295 ### Checklists #### All Submissions: * [X] I've signed all my commits * [X] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [X] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [ ] I've added tests for the new feature * [ ] I've added docs for the new feature #### Bugfixes: * [ ] This pull request breaks the existing API * [ ] I've added tests to reproduce the issue which are now passing * [ ] I'm linking the issue being fixed by this PR ACKs for top commit: evanlinjin: ACK 5489f905a434ecc06867603c7c421e3e50d993ca LLFourn: ACK 5489f905a434ecc06867603c7c421e3e50d993ca Tree-SHA512: c8327f2e7035a46208eb32c6da1f9f0bc3e8625168450c5b0b39f695268e42b0b9053b6eb97805b116328195d77af7ca9edb1f12206c50513fbe295dded542e7
This commit is contained in:
commit
2c324d3759
@ -454,6 +454,21 @@ impl<A> TxGraph<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Clone + Ord> TxGraph<A> {
|
||||
/// Transform the [`TxGraph`] to have [`Anchor`]s of another type.
|
||||
///
|
||||
/// This takes in a closure of signature `FnMut(A) -> A2` which is called for each [`Anchor`] to
|
||||
/// transform it.
|
||||
pub fn map_anchors<A2: Clone + Ord, F>(self, f: F) -> TxGraph<A2>
|
||||
where
|
||||
F: FnMut(A) -> A2,
|
||||
{
|
||||
let mut new_graph = TxGraph::<A2>::default();
|
||||
new_graph.apply_changeset(self.initial_changeset().map_anchors(f));
|
||||
new_graph
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Clone + Ord> TxGraph<A> {
|
||||
/// Construct a new [`TxGraph`] from a list of transactions.
|
||||
pub fn new(txs: impl IntoIterator<Item = Transaction>) -> Self {
|
||||
@ -1294,6 +1309,26 @@ impl<A: Ord> Append for ChangeSet<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> ChangeSet<A> {
|
||||
/// Transform the [`ChangeSet`] to have [`Anchor`]s of another type.
|
||||
///
|
||||
/// This takes in a closure of signature `FnMut(A) -> A2` which is called for each [`Anchor`] to
|
||||
/// transform it.
|
||||
pub fn map_anchors<A2: Ord, F>(self, mut f: F) -> ChangeSet<A2>
|
||||
where
|
||||
F: FnMut(A) -> A2,
|
||||
{
|
||||
ChangeSet {
|
||||
txs: self.txs,
|
||||
txouts: self.txouts,
|
||||
anchors: BTreeSet::<(A2, Txid)>::from_iter(
|
||||
self.anchors.into_iter().map(|(a, txid)| (f(a), txid)),
|
||||
),
|
||||
last_seen: self.last_seen,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
|
||||
fn as_ref(&self) -> &TxGraph<A> {
|
||||
self
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bdk_chain::{tx_graph::TxGraph, BlockId, SpkTxOutIndex};
|
||||
use bdk_chain::{tx_graph::TxGraph, Anchor, SpkTxOutIndex};
|
||||
use bitcoin::{
|
||||
locktime::absolute::LockTime, secp256k1::Secp256k1, OutPoint, ScriptBuf, Sequence, Transaction,
|
||||
TxIn, TxOut, Txid, Witness,
|
||||
@ -49,11 +49,11 @@ impl TxOutTemplate {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn init_graph<'a>(
|
||||
tx_templates: impl IntoIterator<Item = &'a TxTemplate<'a, BlockId>>,
|
||||
) -> (TxGraph<BlockId>, SpkTxOutIndex<u32>, HashMap<&'a str, Txid>) {
|
||||
pub fn init_graph<'a, A: Anchor + Clone + 'a>(
|
||||
tx_templates: impl IntoIterator<Item = &'a TxTemplate<'a, A>>,
|
||||
) -> (TxGraph<A>, SpkTxOutIndex<u32>, HashMap<&'a str, Txid>) {
|
||||
let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/0/*)").unwrap();
|
||||
let mut graph = TxGraph::<BlockId>::default();
|
||||
let mut graph = TxGraph::<A>::default();
|
||||
let mut spk_index = SpkTxOutIndex::default();
|
||||
(0..10).for_each(|index| {
|
||||
spk_index.insert_spk(
|
||||
@ -126,7 +126,7 @@ pub fn init_graph<'a>(
|
||||
spk_index.scan(&tx);
|
||||
let _ = graph.insert_tx(tx.clone());
|
||||
for anchor in tx_tmp.anchors.iter() {
|
||||
let _ = graph.insert_anchor(tx.txid(), *anchor);
|
||||
let _ = graph.insert_anchor(tx.txid(), anchor.clone());
|
||||
}
|
||||
if let Some(seen_at) = tx_tmp.last_seen {
|
||||
let _ = graph.insert_seen_at(tx.txid(), seen_at);
|
||||
|
@ -10,7 +10,9 @@ use bdk_chain::{
|
||||
use bitcoin::{
|
||||
absolute, hashes::Hash, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid,
|
||||
};
|
||||
use common::*;
|
||||
use core::iter;
|
||||
use rand::RngCore;
|
||||
use std::vec;
|
||||
|
||||
#[test]
|
||||
@ -1178,3 +1180,86 @@ fn test_missing_blocks() {
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// The `map_anchors` allow a caller to pass a function to reconstruct the [`TxGraph`] with any [`Anchor`],
|
||||
/// even though the function is non-deterministic.
|
||||
fn call_map_anchors_with_non_deterministic_anchor() {
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
|
||||
/// A non-deterministic anchor
|
||||
pub struct NonDeterministicAnchor {
|
||||
pub anchor_block: BlockId,
|
||||
pub non_deterministic_field: u32,
|
||||
}
|
||||
|
||||
let template = [
|
||||
TxTemplate {
|
||||
tx_name: "tx1",
|
||||
inputs: &[TxInTemplate::Bogus],
|
||||
outputs: &[TxOutTemplate::new(10000, Some(1))],
|
||||
anchors: &[block_id!(1, "A")],
|
||||
last_seen: None,
|
||||
},
|
||||
TxTemplate {
|
||||
tx_name: "tx2",
|
||||
inputs: &[TxInTemplate::PrevTx("tx1", 0)],
|
||||
outputs: &[TxOutTemplate::new(20000, Some(2))],
|
||||
anchors: &[block_id!(2, "B")],
|
||||
..Default::default()
|
||||
},
|
||||
TxTemplate {
|
||||
tx_name: "tx3",
|
||||
inputs: &[TxInTemplate::PrevTx("tx2", 0)],
|
||||
outputs: &[TxOutTemplate::new(30000, Some(3))],
|
||||
anchors: &[block_id!(3, "C"), block_id!(4, "D")],
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
let (graph, _, _) = init_graph(&template);
|
||||
let new_graph = graph.clone().map_anchors(|a| NonDeterministicAnchor {
|
||||
anchor_block: a,
|
||||
// A non-deterministic value
|
||||
non_deterministic_field: rand::thread_rng().next_u32(),
|
||||
});
|
||||
|
||||
// Check all the details in new_graph reconstruct as well
|
||||
|
||||
let mut full_txs_vec: Vec<_> = graph.full_txs().collect();
|
||||
full_txs_vec.sort();
|
||||
let mut new_txs_vec: Vec<_> = new_graph.full_txs().collect();
|
||||
new_txs_vec.sort();
|
||||
let mut new_txs = new_txs_vec.iter();
|
||||
|
||||
for tx_node in full_txs_vec.iter() {
|
||||
let new_txnode = new_txs.next().unwrap();
|
||||
assert_eq!(new_txnode.txid, tx_node.txid);
|
||||
assert_eq!(new_txnode.tx, tx_node.tx);
|
||||
assert_eq!(
|
||||
new_txnode.last_seen_unconfirmed,
|
||||
tx_node.last_seen_unconfirmed
|
||||
);
|
||||
assert_eq!(new_txnode.anchors.len(), tx_node.anchors.len());
|
||||
|
||||
let mut new_anchors: Vec<_> = new_txnode.anchors.iter().map(|a| a.anchor_block).collect();
|
||||
new_anchors.sort();
|
||||
let mut old_anchors: Vec<_> = tx_node.anchors.iter().copied().collect();
|
||||
old_anchors.sort();
|
||||
assert_eq!(new_anchors, old_anchors);
|
||||
}
|
||||
assert!(new_txs.next().is_none());
|
||||
|
||||
let new_graph_anchors: Vec<_> = new_graph
|
||||
.all_anchors()
|
||||
.iter()
|
||||
.map(|i| i.0.anchor_block)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
new_graph_anchors,
|
||||
vec![
|
||||
block_id!(1, "A"),
|
||||
block_id!(2, "B"),
|
||||
block_id!(3, "C"),
|
||||
block_id!(4, "D"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user