From 9860646e73e3043d96f172b8797f578b439256f5 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Mon, 11 Jan 2021 10:50:54 -0800 Subject: [PATCH] Add lightning-block-sync package and library 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 | 38 +++++++++++- Cargo.toml | 1 + lightning-block-sync/Cargo.toml | 13 ++++ lightning-block-sync/src/lib.rs | 105 ++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 lightning-block-sync/Cargo.toml create mode 100644 lightning-block-sync/src/lib.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84fc50946..fd219f101 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: | diff --git a/Cargo.toml b/Cargo.toml index c43e79275..96f4b1d17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 index 000000000..547b04d71 --- /dev/null +++ b/lightning-block-sync/Cargo.toml @@ -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 index 000000000..b2315e907 --- /dev/null +++ b/lightning-block-sync/src/lib.rs @@ -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) -> 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)>; +} + +/// Result type for `BlockSource` requests. +type BlockSourceResult = Result; + +// 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> + '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, +} + +/// 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(error: E) -> Self + where E: Into> { + Self { + kind: BlockSourceErrorKind::Persistent, + error: error.into(), + } + } + + /// Creates a new transient error originated from the given error. + pub fn transient(error: E) -> Self + where E: Into> { + 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 { + 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, +} -- 2.39.5