//! Both features support either blocking I/O using `std::net::TcpStream` or, with feature `tokio`,
//! non-blocking I/O using `tokio::net::TcpStream` from inside a Tokio runtime.
+// Prefix these with `rustdoc::` when we update our MSRV to be >= 1.52 to remove warnings.
#![deny(broken_intra_doc_links)]
+#![deny(private_intra_doc_links)]
+
#![deny(missing_docs)]
#![deny(unsafe_code)]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+
#[cfg(any(feature = "rest-client", feature = "rpc-client"))]
pub mod http;
pub mod init;
pub mod poll;
+pub mod gossip;
+
#[cfg(feature = "rest-client")]
pub mod rest;
use crate::poll::{ChainTip, Poll, ValidatedBlockHeader};
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::{Block, Header};
use bitcoin::hash_types::BlockHash;
-use bitcoin::util::uint::Uint256;
+use bitcoin::pow::Work;
use lightning::chain;
use lightning::chain::Listen;
///
/// Implementations that cannot find headers based on the hash should return a `Transient` error
/// when `height_hint` is `None`.
- fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData>;
+ fn get_header<'a>(&'a self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData>;
/// Returns the block for a given hash. A headers-only block source should return a `Transient`
/// error.
- fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block>;
+ fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, BlockData>;
/// Returns the hash of the best block and, optionally, its height.
///
/// to allow for a more efficient lookup.
///
/// [`get_header`]: Self::get_header
- fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
+ fn get_best_block<'a>(&'a self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
}
/// Result type for `BlockSource` requests.
}
/// The kind of `BlockSourceError`, either persistent or transient.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlockSourceErrorKind {
/// Indicates an error that won't resolve when retrying a request (e.g., invalid data).
Persistent,
}
/// Converts the error into the underlying error.
+ ///
+ /// May contain an [`std::io::Error`] from the [`BlockSource`]. See implementations for further
+ /// details, if any.
pub fn into_inner(self) -> Box<dyn std::error::Error + Send + Sync> {
self.error
}
/// A block header and some associated data. This information should be available from most block
/// sources (and, notably, is available in Bitcoin Core's RPC and REST interfaces).
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BlockHeaderData {
/// The block header itself.
- pub header: BlockHeader,
+ pub header: Header,
/// The block height where the genesis block has height 0.
pub height: u32,
- /// The total chain work in expected number of double-SHA256 hashes required to build a chain
- /// of equivalent weight.
- pub chainwork: Uint256,
+ /// The total chain work required to build a chain of equivalent weight.
+ pub chainwork: Work,
+}
+
+/// A block including either all its transactions or only the block header.
+///
+/// [`BlockSource`] may be implemented to either always return full blocks or, in the case of
+/// compact block filters (BIP 157/158), return header-only blocks when no pertinent transactions
+/// match. See [`chain::Filter`] for details on how to notify a source of such transactions.
+pub enum BlockData {
+ /// A block containing all its transactions.
+ FullBlock(Block),
+ /// A block header for when the block does not contain any pertinent transactions.
+ HeaderOnly(Header),
}
/// A lightweight client for keeping a listener in sync with the chain, allowing for Simplified
chain_poller: &mut P,
) -> Result<(), (BlockSourceError, Option<ValidatedBlockHeader>)> {
for header in connected_blocks.drain(..).rev() {
- let block = chain_poller
+ let height = header.height;
+ let block_data = chain_poller
.fetch_block(&header).await
- .or_else(|e| Err((e, Some(new_tip))))?;
- debug_assert_eq!(block.block_hash, header.block_hash);
+ .map_err(|e| (e, Some(new_tip)))?;
+ debug_assert_eq!(block_data.block_hash, header.block_hash);
+
+ match block_data.deref() {
+ BlockData::FullBlock(block) => {
+ self.chain_listener.block_connected(block, height);
+ },
+ BlockData::HeaderOnly(header) => {
+ self.chain_listener.filtered_block_connected(header, &[], height);
+ },
+ }
self.header_cache.block_connected(header.block_hash, header);
- self.chain_listener.block_connected(&block, header.height);
new_tip = header;
}
Ok(_) => panic!("Expected error"),
}
}
+
+ #[tokio::test]
+ async fn sync_from_chain_with_filtered_blocks() {
+ let mut chain = Blockchain::default().with_height(3).filtered_blocks();
+
+ let new_tip = chain.tip();
+ let old_tip = chain.at_height(1);
+ let chain_listener = &MockChainListener::new()
+ .expect_filtered_block_connected(*chain.at_height(2))
+ .expect_filtered_block_connected(*new_tip);
+ let mut notifier = ChainNotifier {
+ header_cache: &mut chain.header_cache(0..=1),
+ chain_listener,
+ };
+ let mut poller = poll::ChainPoller::new(&mut chain, Network::Testnet);
+ match notifier.synchronize_listener(new_tip, &old_tip, &mut poller).await {
+ Err((e, _)) => panic!("Unexpected error: {:?}", e),
+ Ok(_) => {},
+ }
+ }
+
}