]> Untitled Git - bdk/commitdiff
feat!: introduce `bdk_core`
author志宇 <hello@evanlinjin.me>
Fri, 23 Aug 2024 15:08:55 +0000 (15:08 +0000)
committer志宇 <hello@evanlinjin.me>
Sat, 24 Aug 2024 16:12:56 +0000 (16:12 +0000)
This is an initial version with `chain_data` types ported over.

Types ported over include `BlockId`, `ConfirmationBlockTime`. The impls
for `Anchor` and `AnchorFromBlockPosition` of these types are moved to
where the traits are defined.

14 files changed:
Cargo.toml
crates/chain/Cargo.toml
crates/chain/src/chain_data.rs
crates/chain/src/example_utils.rs
crates/chain/src/lib.rs
crates/chain/src/rusqlite_impl.rs
crates/chain/src/spk_client.rs
crates/chain/src/tx_data_traits.rs
crates/chain/src/tx_graph.rs
crates/chain/tests/test_tx_graph.rs
crates/core/Cargo.toml [new file with mode: 0644]
crates/core/src/chain_data.rs [new file with mode: 0644]
crates/core/src/lib.rs [new file with mode: 0644]
example-crates/example_cli/src/lib.rs

index 1c29bbaf5dd8f1f481deed63eba53e11baca7d2b..ef6d55e92cad2d5224bfb094b208a35bcbdfee2d 100644 (file)
@@ -3,6 +3,7 @@ resolver = "2"
 members = [
     "crates/wallet",
     "crates/chain",
+    "crates/core",
     "crates/file_store",
     "crates/electrum",
     "crates/esplora",
index 7261bdfa25dd16d1322299bc3bd5fd95e34e4cc4..42a77441fd4560f4b984145ebe3968f80614b7d5 100644 (file)
@@ -14,10 +14,8 @@ readme = "README.md"
 
 [dependencies]
 bitcoin = { version = "0.32.0", default-features = false }
+bdk_core = { path = "../core", version = "0.1", default-features = false }
 serde = { version = "1", optional = true, features = ["derive", "rc"] }
-
-# Use hashbrown as a feature flag to have HashSet and HashMap from it.
-hashbrown = { version = "0.9.1", optional = true, features = ["serde"] }
 miniscript = { version = "12.0.0", optional = true, default-features = false }
 
 # Feature dependencies
@@ -30,6 +28,7 @@ proptest = "1.2.0"
 
 [features]
 default = ["std", "miniscript"]
-std = ["bitcoin/std", "miniscript?/std"]
-serde = ["dep:serde", "bitcoin/serde", "miniscript?/serde"]
+std = ["bitcoin/std", "miniscript?/std", "bdk_core/std"]
+serde = ["dep:serde", "bitcoin/serde", "miniscript?/serde", "bdk_core/serde"]
+hashbrown = ["bdk_core/hashbrown"]
 rusqlite = ["std", "dep:rusqlite", "serde", "serde_json"]
index 8ce6e31a4b5fed8f19f9feeb2d89cb0b89a677c7..ce6076c515adbf7ecc1b1f284efb6b6509094759 100644 (file)
@@ -1,6 +1,7 @@
-use bitcoin::{hashes::Hash, BlockHash, OutPoint, TxOut, Txid};
+use crate::ConfirmationBlockTime;
+use bitcoin::{OutPoint, TxOut, Txid};
 
-use crate::{Anchor, AnchorFromBlockPosition, COINBASE_MATURITY};
+use crate::{Anchor, COINBASE_MATURITY};
 
 /// Represents the observed position of some chain data.
 ///
@@ -82,92 +83,6 @@ impl From<ChainPosition<ConfirmationBlockTime>> for ConfirmationTime {
     }
 }
 
-/// A reference to a block in the canonical chain.
-///
-/// `BlockId` implements [`Anchor`]. When a transaction is anchored to `BlockId`, the confirmation
-/// block and anchor block are the same block.
-#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct BlockId {
-    /// The height of the block.
-    pub height: u32,
-    /// The hash of the block.
-    pub hash: BlockHash,
-}
-
-impl Anchor for BlockId {
-    fn anchor_block(&self) -> Self {
-        *self
-    }
-}
-
-impl AnchorFromBlockPosition for BlockId {
-    fn from_block_position(_block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
-        block_id
-    }
-}
-
-impl Default for BlockId {
-    fn default() -> Self {
-        Self {
-            height: Default::default(),
-            hash: BlockHash::all_zeros(),
-        }
-    }
-}
-
-impl From<(u32, BlockHash)> for BlockId {
-    fn from((height, hash): (u32, BlockHash)) -> Self {
-        Self { height, hash }
-    }
-}
-
-impl From<BlockId> for (u32, BlockHash) {
-    fn from(block_id: BlockId) -> Self {
-        (block_id.height, block_id.hash)
-    }
-}
-
-impl From<(&u32, &BlockHash)> for BlockId {
-    fn from((height, hash): (&u32, &BlockHash)) -> Self {
-        Self {
-            height: *height,
-            hash: *hash,
-        }
-    }
-}
-
-/// An [`Anchor`] implementation that also records the exact confirmation time of the transaction.
-///
-/// Refer to [`Anchor`] for more details.
-#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct ConfirmationBlockTime {
-    /// The anchor block.
-    pub block_id: BlockId,
-    /// The confirmation time of the transaction being anchored.
-    pub confirmation_time: u64,
-}
-
-impl Anchor for ConfirmationBlockTime {
-    fn anchor_block(&self) -> BlockId {
-        self.block_id
-    }
-
-    fn confirmation_height_upper_bound(&self) -> u32 {
-        self.block_id.height
-    }
-}
-
-impl AnchorFromBlockPosition for ConfirmationBlockTime {
-    fn from_block_position(block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
-        Self {
-            block_id,
-            confirmation_time: block.header.time as _,
-        }
-    }
-}
-
 /// A `TxOut` with as much data as we can retrieve about it
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub struct FullTxOut<A> {
@@ -244,6 +159,8 @@ impl<A: Anchor> FullTxOut<A> {
 
 #[cfg(test)]
 mod test {
+    use crate::BlockId;
+
     use super::*;
 
     #[test]
index 8077e21185dd7f4c392e8c93009991a17adef467..c71b6cfefb191c61b9de8ed684ee352515c5d09e 100644 (file)
@@ -1,4 +1,5 @@
 #![allow(unused)]
+use crate::BlockId;
 use alloc::vec::Vec;
 use bitcoin::{
     consensus,
@@ -6,8 +7,6 @@ use bitcoin::{
     Transaction,
 };
 
-use crate::BlockId;
-
 pub const RAW_TX_1: &str = "0200000000010116d6174da7183d70d0a7d4dc314d517a7d135db79ad63515028b293a76f4f9d10000000000feffffff023a21fc8350060000160014531c405e1881ef192294b8813631e258bf98ea7a1027000000000000225120a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c024730440220591b1a172a122da49ba79a3e79f98aaa03fd7a372f9760da18890b6a327e6010022013e82319231da6c99abf8123d7c07e13cf9bd8d76e113e18dc452e5024db156d012102318a2d558b2936c52e320decd6d92a88d7f530be91b6fe0af5caf41661e77da3ef2e0100";
 pub const RAW_TX_2: &str = "02000000000101a688607020cfae91a61e7c516b5ef1264d5d77f17200c3866826c6c808ebf1620000000000feffffff021027000000000000225120a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c20fd48ff530600001600146886c525e41d4522042bd0b159dfbade2504a6bb024730440220740ff7e665cd20565d4296b549df8d26b941be3f1e3af89a0b60e50c0dbeb69a02206213ab7030cf6edc6c90d4ccf33010644261e029950a688dc0b1a9ebe6ddcc5a012102f2ac6b396a97853cb6cd62242c8ae4842024742074475023532a51e9c53194253e760100";
 pub const RAW_TX_3: &str = "0200000000010135d67ee47b557e68b8c6223958f597381965ed719f1207ee2b9e20432a24a5dc0100000000feffffff021027000000000000225120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb62215a5055060000160014070df7671dea67a50c4799a744b5c9be8f4bac690247304402207ebf8d29f71fd03e7e6977b3ea78ca5fcc5c49a42ae822348fc401862fdd766c02201d7e4ff0684ecb008b6142f36ead1b0b4d615524c4f58c261113d361f4427e25012103e6a75e2fab85e5ecad641afc4ffba7222f998649d9f18cac92f0fcc8618883b3ee760100";
index 029eedc28282da3ad9c8530d66c9d70869cea0b3..c1c555961a1d60d81ab1190c4db25872c14f725b 100644 (file)
@@ -38,8 +38,8 @@ pub use indexer::spk_txout;
 pub use indexer::Indexer;
 pub mod local_chain;
 mod tx_data_traits;
-pub mod tx_graph;
 pub use tx_data_traits::*;
+pub mod tx_graph;
 pub use tx_graph::TxGraph;
 mod chain_oracle;
 pub use chain_oracle::*;
@@ -63,6 +63,9 @@ pub use spk_iter::*;
 pub mod rusqlite_impl;
 pub mod spk_client;
 
+pub extern crate bdk_core;
+pub use bdk_core::*;
+
 #[allow(unused_imports)]
 #[macro_use]
 extern crate alloc;
@@ -75,37 +78,6 @@ pub extern crate serde;
 #[macro_use]
 extern crate std;
 
-#[cfg(all(not(feature = "std"), feature = "hashbrown"))]
-extern crate hashbrown;
-
-// When no-std use `alloc`'s Hash collections. This is activated by default
-#[cfg(all(not(feature = "std"), not(feature = "hashbrown")))]
-#[doc(hidden)]
-pub mod collections {
-    #![allow(dead_code)]
-    pub type HashSet<K> = alloc::collections::BTreeSet<K>;
-    pub type HashMap<K, V> = alloc::collections::BTreeMap<K, V>;
-    pub use alloc::collections::{btree_map as hash_map, *};
-}
-
-// When we have std, use `std`'s all collections
-#[cfg(all(feature = "std", not(feature = "hashbrown")))]
-#[doc(hidden)]
-pub mod collections {
-    pub use std::collections::{hash_map, *};
-}
-
-// With this special feature `hashbrown`, use `hashbrown`'s hash collections, and else from `alloc`.
-#[cfg(feature = "hashbrown")]
-#[doc(hidden)]
-pub mod collections {
-    #![allow(dead_code)]
-    pub type HashSet<K> = hashbrown::HashSet<K>;
-    pub type HashMap<K, V> = hashbrown::HashMap<K, V>;
-    pub use alloc::collections::*;
-    pub use hashbrown::hash_map;
-}
-
 /// How many confirmations are needed f or a coinbase output to be spent.
 pub const COINBASE_MATURITY: u32 = 100;
 
@@ -137,3 +109,27 @@ impl<T> core::ops::Deref for Impl<T> {
         &self.0
     }
 }
+
+/// A wrapper that we use to impl remote traits for types in our crate or dependency crates that impl [`Anchor`].
+pub struct AnchorImpl<T>(pub T);
+
+impl<T> AnchorImpl<T> {
+    /// Returns the inner `T`.
+    pub fn into_inner(self) -> T {
+        self.0
+    }
+}
+
+impl<T> From<T> for AnchorImpl<T> {
+    fn from(value: T) -> Self {
+        Self(value)
+    }
+}
+
+impl<T> core::ops::Deref for AnchorImpl<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
index d8ef65c42b04a702fecd5c64c63791519b096987..2cc4481c9e62555d8ff9cb7d19449c6edafcb7fc 100644 (file)
@@ -157,15 +157,15 @@ impl ToSql for Impl<bitcoin::Amount> {
     }
 }
 
-impl<A: Anchor + serde::de::DeserializeOwned> FromSql for Impl<A> {
+impl<A: Anchor + serde::de::DeserializeOwned> FromSql for AnchorImpl<A> {
     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
         serde_json::from_str(value.as_str()?)
-            .map(Impl)
+            .map(AnchorImpl)
             .map_err(from_sql_error)
     }
 }
 
-impl<A: Anchor + serde::Serialize> ToSql for Impl<A> {
+impl<A: Anchor + serde::Serialize> ToSql for AnchorImpl<A> {
     fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
         serde_json::to_string(&self.0)
             .map(Into::into)
@@ -319,12 +319,12 @@ where
         ))?;
         let row_iter = statement.query_map([], |row| {
             Ok((
-                row.get::<_, Impl<A>>("json(anchor)")?,
+                row.get::<_, AnchorImpl<A>>("json(anchor)")?,
                 row.get::<_, Impl<bitcoin::Txid>>("txid")?,
             ))
         })?;
         for row in row_iter {
-            let (Impl(anchor), Impl(txid)) = row?;
+            let (AnchorImpl(anchor), Impl(txid)) = row?;
             changeset.anchors.insert((anchor, txid));
         }
 
@@ -381,7 +381,7 @@ where
                 ":txid": Impl(*txid),
                 ":block_height": anchor_block.height,
                 ":block_hash": Impl(anchor_block.hash),
-                ":anchor": Impl(anchor.clone()),
+                ":anchor": AnchorImpl(anchor.clone()),
             })?;
         }
 
index e31b431dd8be97a9d45d15d1c585d2ea5e8b3843..75fb669848836e826f5b8f50fd2dac16c03c1cf2 100644 (file)
@@ -3,8 +3,9 @@ use crate::{
     alloc::{boxed::Box, collections::VecDeque, vec::Vec},
     collections::BTreeMap,
     local_chain::CheckPoint,
-    ConfirmationBlockTime, Indexed,
+    Indexed,
 };
+use bdk_core::ConfirmationBlockTime;
 use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
 
 type InspectSync<I> = dyn FnMut(SyncItem<I>, SyncProgress) + Send + 'static;
index 8a324f6a5c0c79652087cbca5e53440dc1ef9080..d3d562bf30c72ba70af6290598097c26b3d61969 100644 (file)
@@ -1,6 +1,5 @@
-use crate::collections::BTreeMap;
-use crate::collections::BTreeSet;
-use crate::BlockId;
+use crate::collections::{BTreeMap, BTreeSet};
+use crate::{BlockId, ConfirmationBlockTime};
 use alloc::vec::Vec;
 
 /// Trait that "anchors" blockchain data to a specific block of height and hash.
@@ -85,6 +84,22 @@ impl<'a, A: Anchor> Anchor for &'a A {
     }
 }
 
+impl Anchor for BlockId {
+    fn anchor_block(&self) -> Self {
+        *self
+    }
+}
+
+impl Anchor for ConfirmationBlockTime {
+    fn anchor_block(&self) -> BlockId {
+        self.block_id
+    }
+
+    fn confirmation_height_upper_bound(&self) -> u32 {
+        self.block_id.height
+    }
+}
+
 /// An [`Anchor`] that can be constructed from a given block, block height and transaction position
 /// within the block.
 pub trait AnchorFromBlockPosition: Anchor {
@@ -92,6 +107,21 @@ pub trait AnchorFromBlockPosition: Anchor {
     fn from_block_position(block: &bitcoin::Block, block_id: BlockId, tx_pos: usize) -> Self;
 }
 
+impl AnchorFromBlockPosition for BlockId {
+    fn from_block_position(_block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
+        block_id
+    }
+}
+
+impl AnchorFromBlockPosition for ConfirmationBlockTime {
+    fn from_block_position(block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
+        Self {
+            block_id,
+            confirmation_time: block.header.time as _,
+        }
+    }
+}
+
 /// Trait that makes an object mergeable.
 pub trait Merge: Default {
     /// Merge another object of the same type onto `self`.
index e953580ce2b7a6e708960e75f018ece73eb0c977..206aaf11e9afafc18ea9110b543d15def0077c45 100644 (file)
@@ -92,9 +92,9 @@
 //! [`try_get_chain_position`]: TxGraph::try_get_chain_position
 //! [`insert_txout`]: TxGraph::insert_txout
 
-use crate::{
-    collections::*, Anchor, Balance, BlockId, ChainOracle, ChainPosition, FullTxOut, Merge,
-};
+use crate::collections::*;
+use crate::BlockId;
+use crate::{Anchor, Balance, ChainOracle, ChainPosition, FullTxOut, Merge};
 use alloc::collections::vec_deque::VecDeque;
 use alloc::sync::Arc;
 use alloc::vec::Vec;
index df5e6a621befd72a8046e1e349fb0c0207be91f7..4ce6772bbd4098499e898bd4f1f8cb98e60fd5f3 100644 (file)
@@ -2,12 +2,12 @@
 
 #[macro_use]
 mod common;
-use bdk_chain::tx_graph::{self, CalculateFeeError};
+use bdk_chain::{collections::*, BlockId, ConfirmationBlockTime};
 use bdk_chain::{
-    collections::*,
     local_chain::LocalChain,
+    tx_graph::{self, CalculateFeeError},
     tx_graph::{ChangeSet, TxGraph},
-    Anchor, BlockId, ChainOracle, ChainPosition, ConfirmationBlockTime, Merge,
+    Anchor, ChainOracle, ChainPosition, Merge,
 };
 use bitcoin::{
     absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, SignedAmount,
diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml
new file mode 100644 (file)
index 0000000..d74cf90
--- /dev/null
@@ -0,0 +1,21 @@
+[package]
+name = "bdk_core"
+version = "0.1.0"
+edition = "2021"
+rust-version = "1.63"
+homepage = "https://bitcoindevkit.org"
+repository = "https://github.com/bitcoindevkit/bdk"
+documentation = "https://docs.rs/bdk_core"
+description = "Collection of core structures for Bitcoin Dev Kit."
+license = "MIT OR Apache-2.0"
+readme = "README.md"
+
+[dependencies]
+bitcoin = { version = "0.32", default-features = false }
+serde = { version = "1", optional = true, features = ["derive", "rc"] }
+hashbrown = { version = "0.9.1", optional = true }
+
+[features]
+default = ["std"]
+std = ["bitcoin/std"]
+serde = ["dep:serde", "bitcoin/serde", "hashbrown?/serde"]
diff --git a/crates/core/src/chain_data.rs b/crates/core/src/chain_data.rs
new file mode 100644 (file)
index 0000000..2e64c9c
--- /dev/null
@@ -0,0 +1,51 @@
+use bitcoin::{hashes::Hash, BlockHash};
+
+/// A reference to a block in the canonical chain.
+#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct BlockId {
+    /// The height of the block.
+    pub height: u32,
+    /// The hash of the block.
+    pub hash: BlockHash,
+}
+
+impl Default for BlockId {
+    fn default() -> Self {
+        Self {
+            height: Default::default(),
+            hash: BlockHash::all_zeros(),
+        }
+    }
+}
+
+impl From<(u32, BlockHash)> for BlockId {
+    fn from((height, hash): (u32, BlockHash)) -> Self {
+        Self { height, hash }
+    }
+}
+
+impl From<BlockId> for (u32, BlockHash) {
+    fn from(block_id: BlockId) -> Self {
+        (block_id.height, block_id.hash)
+    }
+}
+
+impl From<(&u32, &BlockHash)> for BlockId {
+    fn from((height, hash): (&u32, &BlockHash)) -> Self {
+        Self {
+            height: *height,
+            hash: *hash,
+        }
+    }
+}
+
+/// Represents the confirmation block and time of a transaction.
+#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct ConfirmationBlockTime {
+    /// The anchor block.
+    pub block_id: BlockId,
+    /// The confirmation time of the transaction being anchored.
+    pub confirmation_time: u64,
+}
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
new file mode 100644 (file)
index 0000000..1c53584
--- /dev/null
@@ -0,0 +1,58 @@
+//! This crate is a collection of core structures for [Bitcoin Dev Kit].
+
+// only enables the `doc_cfg` feature when the `docsrs` configuration attribute is defined
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![cfg_attr(
+    docsrs,
+    doc(html_logo_url = "https://github.com/bitcoindevkit/bdk/raw/master/static/bdk.png")
+)]
+#![no_std]
+#![warn(missing_docs)]
+
+pub use bitcoin;
+
+#[allow(unused_imports)]
+#[macro_use]
+extern crate alloc;
+
+#[allow(unused_imports)]
+#[cfg(feature = "std")]
+#[macro_use]
+extern crate std;
+
+#[cfg(feature = "serde")]
+pub extern crate serde;
+
+#[cfg(all(not(feature = "std"), feature = "hashbrown"))]
+extern crate hashbrown;
+
+// When no-std use `alloc`'s Hash collections. This is activated by default
+#[cfg(all(not(feature = "std"), not(feature = "hashbrown")))]
+#[doc(hidden)]
+pub mod collections {
+    #![allow(dead_code)]
+    pub type HashSet<K> = alloc::collections::BTreeSet<K>;
+    pub type HashMap<K, V> = alloc::collections::BTreeMap<K, V>;
+    pub use alloc::collections::{btree_map as hash_map, *};
+}
+
+// When we have std, use `std`'s all collections
+#[cfg(all(feature = "std", not(feature = "hashbrown")))]
+#[doc(hidden)]
+pub mod collections {
+    pub use std::collections::{hash_map, *};
+}
+
+// With this special feature `hashbrown`, use `hashbrown`'s hash collections, and else from `alloc`.
+#[cfg(feature = "hashbrown")]
+#[doc(hidden)]
+pub mod collections {
+    #![allow(dead_code)]
+    pub type HashSet<K> = hashbrown::HashSet<K>;
+    pub type HashMap<K, V> = hashbrown::HashMap<K, V>;
+    pub use alloc::collections::*;
+    pub use hashbrown::hash_map;
+}
+
+mod chain_data;
+pub use chain_data::*;
index 393f9d3fbda04e2fd9979c9ccfb26df03ea746e6..6a97252fcfa2042f39b7eac636f6f97e23f108df 100644 (file)
@@ -1,4 +1,3 @@
-use bdk_chain::ConfirmationBlockTime;
 use serde_json::json;
 use std::cmp;
 use std::collections::HashMap;
@@ -20,6 +19,7 @@ use bdk_chain::miniscript::{
     psbt::PsbtExt,
     Descriptor, DescriptorPublicKey,
 };
+use bdk_chain::ConfirmationBlockTime;
 use bdk_chain::{
     indexed_tx_graph,
     indexer::keychain_txout::{self, KeychainTxOutIndex},