Add lightning-block-sync package and library
authorJeffrey Czyz <jkczyz@gmail.com>
Mon, 11 Jan 2021 18:50:54 +0000 (10:50 -0800)
committerJeffrey Czyz <jkczyz@gmail.com>
Thu, 4 Feb 2021 01:45:54 +0000 (17:45 -0800)
Defines an interface and related types for fetching block headers and
data from a block source (e.g., Bitcoin Core). Used to keep lightning in
sync with chain activity.

.github/workflows/build.yml
Cargo.toml
lightning-block-sync/Cargo.toml [new file with mode: 0644]
lightning-block-sync/src/lib.rs [new file with mode: 0644]

index 84fc509462f74dff8e341d4327db4f3f6a6039f5..fd219f101edb608f4d43d4fcc6d2148c5a341b1d 100644 (file)
@@ -13,7 +13,7 @@ jobs:
                      1.30.0,
                      # 1.34.2 is Debian stable
                      1.34.2,
-                     # 1.45.2 is MSRV for lightning-net-tokio and generates coverage
+                     # 1.45.2 is MSRV for lightning-net-tokio, lightning-block-sync, and coverage generation
                      1.45.2]
         include:
           - toolchain: stable
@@ -48,6 +48,24 @@ jobs:
       - name: Build on Rust ${{ matrix.toolchain }}
         if: "! matrix.build-net-tokio"
         run: cargo build --verbose  --color always -p lightning
+      - name: Build Block Sync Clients on Rust ${{ matrix.toolchain }} with features
+        if: "matrix.build-net-tokio && !matrix.coverage"
+        run: |
+          cd lightning-block-sync
+          cargo build --verbose --color always --features rest-client
+          cargo build --verbose --color always --features rpc-client
+          cargo build --verbose --color always --features rpc-client,rest-client
+          cargo build --verbose --color always --features rpc-client,rest-client,tokio
+          cd ..
+      - name: Build Block Sync Clients on Rust ${{ matrix.toolchain }} with features and full code-linking for coverage generation
+        if: matrix.coverage
+        run: |
+          cd lightning-block-sync
+          RUSTFLAGS="-C link-dead-code" cargo build --verbose --color always --features rest-client
+          RUSTFLAGS="-C link-dead-code" cargo build --verbose --color always --features rpc-client
+          RUSTFLAGS="-C link-dead-code" cargo build --verbose --color always --features rpc-client,rest-client
+          RUSTFLAGS="-C link-dead-code" cargo build --verbose --color always --features rpc-client,rest-client,tokio
+          cd ..
       - name: Test on Rust ${{ matrix.toolchain }} with net-tokio
         if: "matrix.build-net-tokio && !matrix.coverage"
         run: cargo test --verbose --color always
@@ -57,6 +75,24 @@ jobs:
       - name: Test on Rust ${{ matrix.toolchain }}
         if: "! matrix.build-net-tokio"
         run: cargo test --verbose --color always  -p lightning
+      - name: Test Block Sync Clients on Rust ${{ matrix.toolchain }} with features
+        if: "matrix.build-net-tokio && !matrix.coverage"
+        run: |
+          cd lightning-block-sync
+          cargo test --verbose --color always --features rest-client
+          cargo test --verbose --color always --features rpc-client
+          cargo test --verbose --color always --features rpc-client,rest-client
+          cargo test --verbose --color always --features rpc-client,rest-client,tokio
+          cd ..
+      - name: Test Block Sync Clients on Rust ${{ matrix.toolchain }} with features and full code-linking for coverage generation
+        if: matrix.coverage
+        run: |
+          cd lightning-block-sync
+          RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rest-client
+          RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client
+          RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client
+          RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client,tokio
+          cd ..
       - name: Install deps for kcov
         if: matrix.coverage
         run: |
index c43e7927581432e9834aed21a16329aba92f80b3..96f4b1d17700ec1cfee63c2a282b768b080ba053 100644 (file)
@@ -2,6 +2,7 @@
 
 members = [
     "lightning",
+    "lightning-block-sync",
     "lightning-net-tokio",
     "lightning-persister",
 ]
diff --git a/lightning-block-sync/Cargo.toml b/lightning-block-sync/Cargo.toml
new file mode 100644 (file)
index 0000000..547b04d
--- /dev/null
@@ -0,0 +1,13 @@
+[package]
+name = "lightning-block-sync"
+version = "0.0.1"
+authors = ["Jeffrey Czyz", "Matt Corallo"]
+license = "Apache-2.0"
+edition = "2018"
+description = """
+Utilities to fetch the chain data from a block source and feed them into Rust Lightning.
+"""
+
+[dependencies]
+bitcoin = "0.24"
+lightning = { version = "0.0.12", path = "../lightning" }
diff --git a/lightning-block-sync/src/lib.rs b/lightning-block-sync/src/lib.rs
new file mode 100644 (file)
index 0000000..b2315e9
--- /dev/null
@@ -0,0 +1,105 @@
+//! A lightweight client for keeping in sync with chain activity.
+//!
+//! Defines a [`BlockSource`] trait, which is an asynchronous interface for retrieving block headers
+//! and data.
+//!
+//! [`BlockSource`]: trait.BlockSource.html
+
+use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::hash_types::BlockHash;
+use bitcoin::util::uint::Uint256;
+
+use std::future::Future;
+use std::pin::Pin;
+
+/// Abstract type for retrieving block headers and data.
+pub trait BlockSource : Sync + Send {
+       /// Returns the header for a given hash. A height hint may be provided in case a block source
+       /// cannot easily find headers based on a hash. This is merely a hint and thus the returned
+       /// header must have the same hash as was requested. Otherwise, an error must be returned.
+       ///
+       /// 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>;
+
+       /// 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>;
+
+       // TODO: Phrase in terms of `Poll` once added.
+       /// Returns the hash of the best block and, optionally, its height. When polling a block source,
+       /// the height is passed to `get_header` to allow for a more efficient lookup.
+       fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
+}
+
+/// Result type for `BlockSource` requests.
+type BlockSourceResult<T> = Result<T, BlockSourceError>;
+
+// TODO: Replace with BlockSourceResult once `async` trait functions are supported. For details,
+// see: https://areweasyncyet.rs.
+/// Result type for asynchronous `BlockSource` requests.
+type AsyncBlockSourceResult<'a, T> = Pin<Box<dyn Future<Output = BlockSourceResult<T>> + 'a + Send>>;
+
+/// Error type for `BlockSource` requests.
+///
+/// Transient errors may be resolved when re-polling, but no attempt will be made to re-poll on
+/// persistent errors.
+pub struct BlockSourceError {
+       kind: BlockSourceErrorKind,
+       error: Box<dyn std::error::Error + Send + Sync>,
+}
+
+/// The kind of `BlockSourceError`, either persistent or transient.
+#[derive(Clone, Copy)]
+pub enum BlockSourceErrorKind {
+       /// Indicates an error that won't resolve when retrying a request (e.g., invalid data).
+       Persistent,
+
+       /// Indicates an error that may resolve when retrying a request (e.g., unresponsive).
+       Transient,
+}
+
+impl BlockSourceError {
+       /// Creates a new persistent error originated from the given error.
+       pub fn persistent<E>(error: E) -> Self
+       where E: Into<Box<dyn std::error::Error + Send + Sync>> {
+               Self {
+                       kind: BlockSourceErrorKind::Persistent,
+                       error: error.into(),
+               }
+       }
+
+       /// Creates a new transient error originated from the given error.
+       pub fn transient<E>(error: E) -> Self
+       where E: Into<Box<dyn std::error::Error + Send + Sync>> {
+               Self {
+                       kind: BlockSourceErrorKind::Transient,
+                       error: error.into(),
+               }
+       }
+
+       /// Returns the kind of error.
+       pub fn kind(&self) -> BlockSourceErrorKind {
+               self.kind
+       }
+
+       /// Converts the error into the underlying error.
+       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)]
+pub struct BlockHeaderData {
+       /// The block header itself.
+       pub header: BlockHeader,
+
+       /// 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,
+}