fail-fast: false
matrix:
platform: [ ubuntu-latest, windows-latest, macos-latest ]
- toolchain: [ stable, beta ]
- include:
- - toolchain: stable
- platform: ubuntu-latest
- # 1.48.0 is the MSRV for all crates except lightning-transaction-sync and Win/Mac
- - toolchain: 1.48.0
- platform: ubuntu-latest
- # Windows requires 1.49.0 because that's the MSRV for supported Tokio
- - toolchain: 1.49.0
- platform: windows-latest
- # MacOS-latest requires 1.54.0 because that's what's required for linking to work properly
- - toolchain: 1.54.0
- platform: macos-latest
+ toolchain: [ stable, beta, 1.63.0 ] # 1.63.0 is the MSRV for all crates.
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout source code
run: |
rustup target add thumbv7m-none-eabi
sudo apt-get -y install gcc-arm-none-eabi
+ - name: Check for unknown cfg tags
+ run: ci/check-cfg-flags.py
- name: shellcheck the CI script
if: "matrix.platform == 'ubuntu-latest'"
run: |
run: |
cargo check --release
cargo check --no-default-features --features=no-std --release
- cargo check --no-default-features --features=futures --release
+ cargo check --no-default-features --features=futures,std --release
cargo doc --release
- name: Run cargo check for Taproot build.
run: |
cargo check --release
cargo check --no-default-features --features=no-std --release
- cargo check --no-default-features --features=futures --release
+ cargo check --no-default-features --features=futures,std --release
cargo doc --release
env:
RUSTFLAGS: '--cfg=taproot'
When refactoring, structure your PR to make it easy to review and don't
hesitate to split it into multiple small, focused PRs.
-The Minimum Supported Rust Version (MSRV) currently is 1.48.0 (enforced by
+The Minimum Supported Rust Version (MSRV) currently is 1.63.0 (enforced by
our GitHub Actions). We support reading serialized LDK objects written by any
version of LDK 0.0.99 and above. We support LDK versions 0.0.113 and above
reading serialized LDK objects written by modern LDK. Any expected issues with
[workspace]
+resolver = "2"
members = [
"lightning",
"lightning-net-tokio",
"lightning-persister",
"lightning-background-processor",
- "lightning-rapid-gossip-sync"
+ "lightning-rapid-gossip-sync",
+ "lightning-custom-message",
]
exclude = [
- "lightning-custom-message",
"lightning-transaction-sync",
"no-std-check",
"msrv-no-dev-deps-check",
name = "lightning-bench"
version = "0.0.1"
authors = ["Matt Corallo"]
-edition = "2018"
+edition = "2021"
[[bench]]
name = "bench"
--- /dev/null
+#!/usr/bin/env python3
+# Rust is fairly relaxed in checking the validity of arguments passed to #[cfg].
+# While it should probably be more strict when checking features, it cannot be
+# strict when checking loose cfg tags, because those can be anything and are
+# simply passed to rustc via unconstrained arguments.
+#
+# Thus, we do it for rustc manually, but scanning all our source and checking
+# that all our cfg tags match a known cfg tag.
+import sys, glob, re
+
+def check_feature(feature):
+ if feature == "std":
+ pass
+ elif feature == "no-std":
+ pass
+ elif feature == "hashbrown":
+ pass
+ elif feature == "backtrace":
+ pass
+ elif feature == "grind_signatures":
+ pass
+ elif feature == "unsafe_revoked_tx_signing":
+ pass
+ elif feature == "futures":
+ pass
+ elif feature == "tokio":
+ pass
+ elif feature == "rest-client":
+ pass
+ elif feature == "rpc-client":
+ pass
+ elif feature == "serde":
+ pass
+ elif feature == "esplora-blocking":
+ pass
+ elif feature == "esplora-async":
+ pass
+ elif feature == "async-interface":
+ pass
+ elif feature == "electrum":
+ pass
+ elif feature == "_test_utils":
+ pass
+ elif feature == "_test_vectors":
+ pass
+ elif feature == "afl":
+ pass
+ elif feature == "honggfuzz":
+ pass
+ elif feature == "libfuzzer_fuzz":
+ pass
+ elif feature == "stdin_fuzz":
+ pass
+ elif feature == "max_level_off":
+ pass
+ elif feature == "max_level_error":
+ pass
+ elif feature == "max_level_warn":
+ pass
+ elif feature == "max_level_info":
+ pass
+ elif feature == "max_level_debug":
+ pass
+ elif feature == "max_level_trace":
+ pass
+ else:
+ print("Bad feature: " + feature)
+ assert False
+
+def check_target_os(os):
+ if os == "windows":
+ pass
+ else:
+ assert False
+
+def check_cfg_tag(cfg):
+ if cfg == "fuzzing":
+ pass
+ elif cfg == "test":
+ pass
+ elif cfg == "debug_assertions":
+ pass
+ elif cfg == "c_bindings":
+ pass
+ elif cfg == "ldk_bench":
+ pass
+ elif cfg == "taproot":
+ pass
+ elif cfg == "require_route_graph_test":
+ pass
+ else:
+ print("Bad cfg tag: " + cfg)
+ assert False
+
+def check_cfg_args(cfg):
+ if cfg.startswith("all(") or cfg.startswith("any(") or cfg.startswith("not("):
+ brackets = 1
+ pos = 4
+ while pos < len(cfg):
+ if cfg[pos] == "(":
+ brackets += 1
+ elif cfg[pos] == ")":
+ brackets -= 1
+ if brackets == 0:
+ check_cfg_args(cfg[4:pos])
+ if pos + 1 != len(cfg):
+ assert cfg[pos + 1] == ","
+ check_cfg_args(cfg[pos + 2:].strip())
+ return
+ pos += 1
+ assert False
+ assert(cfg.endswith(")"))
+ check_cfg_args(cfg[4:len(cfg)-1])
+ else:
+ parts = [part.strip() for part in cfg.split(",", 1)]
+ if len(parts) > 1:
+ for part in parts:
+ check_cfg_args(part)
+ elif cfg.startswith("feature") or cfg.startswith("target_os") or cfg.startswith("target_pointer_width"):
+ arg = cfg
+ if cfg.startswith("feature"):
+ arg = arg[7:].strip()
+ elif cfg.startswith("target_os"):
+ arg = arg[9:].strip()
+ else:
+ arg = arg[20:].strip()
+ assert arg.startswith("=")
+ arg = arg[1:].strip()
+ assert arg.startswith("\"")
+ assert arg.endswith("\"")
+ arg = arg[1:len(arg)-1]
+ assert not "\"" in arg
+ if cfg.startswith("feature"):
+ check_feature(arg)
+ elif cfg.startswith("target_os"):
+ check_target_os(arg)
+ else:
+ assert arg == "32" or arg == "64"
+ else:
+ check_cfg_tag(cfg.strip())
+
+cfg_regex = re.compile("#\[cfg\((.*)\)\]")
+for path in glob.glob(sys.path[0] + "/../**/*.rs", recursive = True):
+ with open(path, "r") as file:
+ while True:
+ line = file.readline()
+ if not line:
+ break
+ if "#[cfg(" in line:
+ if not line.strip().startswith("//"):
+ cfg_part = cfg_regex.match(line.strip()).group(1)
+ check_cfg_args(cfg_part)
# which we do here.
# Further crates which appear only as dev-dependencies are pinned further down.
function PIN_RELEASE_DEPS {
- # Tokio MSRV on versions 1.17 through 1.26 is rustc 1.49. Above 1.26 MSRV is 1.56.
- [ "$RUSTC_MINOR_VERSION" -lt 49 ] && cargo update -p tokio --precise "1.14.1" --verbose
- [[ "$RUSTC_MINOR_VERSION" -gt 48 && "$RUSTC_MINOR_VERSION" -lt 56 ]] && cargo update -p tokio --precise "1.25.1" --verbose
-
- # Sadly the log crate is always a dependency of tokio until 1.20, and has no reasonable MSRV guarantees
- [ "$RUSTC_MINOR_VERSION" -lt 49 ] && cargo update -p log --precise "0.4.18" --verbose
-
- # The serde_json crate switched to Rust edition 2021 starting with v1.0.101, i.e., has MSRV of 1.56
- [ "$RUSTC_MINOR_VERSION" -lt 56 ] && cargo update -p serde_json --precise "1.0.100" --verbose
-
return 0 # Don't fail the script if our rustc is higher than the last check
}
-PIN_RELEASE_DEPS # pin the release dependencies in our main workspace
-
-# The addr2line v0.20 crate (a dependency of `backtrace` starting with 0.3.68) relies on 1.55+
-[ "$RUSTC_MINOR_VERSION" -lt 55 ] && cargo update -p backtrace --precise "0.3.67" --verbose
-
-# The quote crate switched to Rust edition 2021 starting with v1.0.31, i.e., has MSRV of 1.56
-[ "$RUSTC_MINOR_VERSION" -lt 56 ] && cargo update -p quote --precise "1.0.30" --verbose
+# The tests of `lightning-transaction-sync` require `electrs` and `bitcoind`
+# binaries. Here, we download the binaries, validate them, and export their
+# location via `ELECTRS_EXE`/`BITCOIND_EXE` which will be used by the
+# `electrsd`/`bitcoind` crates in our tests.
+function DOWNLOAD_ELECTRS_AND_BITCOIND {
+ ELECTRS_DL_ENDPOINT="https://github.com/RCasatta/electrsd/releases/download/electrs_releases"
+ ELECTRS_VERSION="esplora_a33e97e1a1fc63fa9c20a116bb92579bbf43b254"
+ BITCOIND_DL_ENDPOINT="https://bitcoincore.org/bin/"
+ BITCOIND_VERSION="25.1"
+ if [[ "$HOST_PLATFORM" == *linux* ]]; then
+ ELECTRS_DL_FILE_NAME=electrs_linux_"$ELECTRS_VERSION".zip
+ ELECTRS_DL_HASH="865e26a96e8df77df01d96f2f569dcf9622fc87a8d99a9b8fe30861a4db9ddf1"
+ BITCOIND_DL_FILE_NAME=bitcoin-"$BITCOIND_VERSION"-x86_64-linux-gnu.tar.gz
+ BITCOIND_DL_HASH="a978c407b497a727f0444156e397b50491ce862d1f906fef9b521415b3611c8b"
+ elif [[ "$HOST_PLATFORM" == *darwin* ]]; then
+ ELECTRS_DL_FILE_NAME=electrs_macos_"$ELECTRS_VERSION".zip
+ ELECTRS_DL_HASH="2d5ff149e8a2482d3658e9b386830dfc40c8fbd7c175ca7cbac58240a9505bcd"
+ BITCOIND_DL_FILE_NAME=bitcoin-"$BITCOIND_VERSION"-x86_64-apple-darwin.tar.gz
+ BITCOIND_DL_HASH="1acfde0ec3128381b83e3e5f54d1c7907871d324549129592144dd12a821eff1"
+ else
+ echo -e "\n\nUnsupported platform. Exiting.."
+ exit 1
+ fi
+
+ DL_TMP_DIR=$(mktemp -d)
+ trap 'rm -rf -- "$DL_TMP_DIR"' EXIT
+
+ pushd "$DL_TMP_DIR"
+ ELECTRS_DL_URL="$ELECTRS_DL_ENDPOINT"/"$ELECTRS_DL_FILE_NAME"
+ curl -L -o "$ELECTRS_DL_FILE_NAME" "$ELECTRS_DL_URL"
+ echo "$ELECTRS_DL_HASH $ELECTRS_DL_FILE_NAME"|shasum -a 256 -c
+ unzip "$ELECTRS_DL_FILE_NAME"
+ export ELECTRS_EXE="$DL_TMP_DIR"/electrs
+ chmod +x "$ELECTRS_EXE"
+
+ BITCOIND_DL_URL="$BITCOIND_DL_ENDPOINT"/bitcoin-core-"$BITCOIND_VERSION"/"$BITCOIND_DL_FILE_NAME"
+ curl -L -o "$BITCOIND_DL_FILE_NAME" "$BITCOIND_DL_URL"
+ echo "$BITCOIND_DL_HASH $BITCOIND_DL_FILE_NAME"|shasum -a 256 -c
+ tar xzf "$BITCOIND_DL_FILE_NAME"
+ export BITCOIND_EXE="$DL_TMP_DIR"/bitcoin-"$BITCOIND_VERSION"/bin/bitcoind
+ chmod +x "$BITCOIND_EXE"
+ popd
+}
-# The syn crate depends on too-new proc-macro2 starting with v2.0.33, i.e., has MSRV of 1.56
-if [ "$RUSTC_MINOR_VERSION" -lt 56 ]; then
- SYN_2_DEP=$(grep -o '"syn 2.*' Cargo.lock | tr -d '",' | tr ' ' ':')
- cargo update -p "$SYN_2_DEP" --precise "2.0.32" --verbose
-fi
+PIN_RELEASE_DEPS # pin the release dependencies in our main workspace
-# The proc-macro2 crate switched to Rust edition 2021 starting with v1.0.66, i.e., has MSRV of 1.56
-[ "$RUSTC_MINOR_VERSION" -lt 56 ] && cargo update -p proc-macro2 --precise "1.0.65" --verbose
+# Starting with version 1.10.0, the `regex` crate has an MSRV of rustc 1.65.0.
+[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p regex --precise "1.9.6" --verbose
-# The memchr crate switched to an MSRV of 1.60 starting with v2.6.0
-[ "$RUSTC_MINOR_VERSION" -lt 60 ] && cargo update -p memchr --precise "2.5.0" --verbose
+# The addr2line v0.21 crate (a dependency of `backtrace` starting with 0.3.69) relies on rustc 1.65
+[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p backtrace --precise "0.3.68" --verbose
export RUST_BACKTRACE=1
cargo check --verbose --color always --features rpc-client,rest-client,tokio
popd
-if [[ $RUSTC_MINOR_VERSION -gt 67 && "$HOST_PLATFORM" != *windows* ]]; then
+if [[ "$HOST_PLATFORM" != *windows* ]]; then
echo -e "\n\nBuilding and testing Transaction Sync Clients with features"
pushd lightning-transaction-sync
- cargo test --verbose --color always --features esplora-blocking
- cargo check --verbose --color always --features esplora-blocking
- cargo test --verbose --color always --features esplora-async
- cargo check --verbose --color always --features esplora-async
- cargo test --verbose --color always --features esplora-async-https
- cargo check --verbose --color always --features esplora-async-https
- cargo test --verbose --color always --features electrum
- cargo check --verbose --color always --features electrum
+
+ # reqwest 0.11.21 had a regression that broke its 1.63.0 MSRV
+ [ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p reqwest --precise "0.11.20" --verbose
+ # Starting with version 1.10.0, the `regex` crate has an MSRV of rustc 1.65.0.
+ [ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p regex --precise "1.9.6" --verbose
+
+ DOWNLOAD_ELECTRS_AND_BITCOIND
+
+ RUSTFLAGS="--cfg no_download" cargo test --verbose --color always --features esplora-blocking
+ RUSTFLAGS="--cfg no_download" cargo check --verbose --color always --features esplora-blocking
+ RUSTFLAGS="--cfg no_download" cargo test --verbose --color always --features esplora-async
+ RUSTFLAGS="--cfg no_download" cargo check --verbose --color always --features esplora-async
+ RUSTFLAGS="--cfg no_download" cargo test --verbose --color always --features esplora-async-https
+ RUSTFLAGS="--cfg no_download" cargo check --verbose --color always --features esplora-async-https
+ RUSTFLAGS="--cfg no_download" cargo test --verbose --color always --features electrum
+ RUSTFLAGS="--cfg no_download" cargo check --verbose --color always --features electrum
+
popd
fi
cargo test --verbose --color always --features futures
popd
-if [ "$RUSTC_MINOR_VERSION" -gt 55 ]; then
- echo -e "\n\nTest Custom Message Macros"
- pushd lightning-custom-message
- cargo test --verbose --color always
- [ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
- popd
-fi
+echo -e "\n\nTest Custom Message Macros"
+pushd lightning-custom-message
+cargo test --verbose --color always
+[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
+popd
-if [ "$RUSTC_MINOR_VERSION" -gt 51 ]; then # Current `object` MSRV, subject to change
- echo -e "\n\nTest backtrace-debug builds"
- pushd lightning
- cargo test --verbose --color always --features backtrace
- popd
-fi
+echo -e "\n\nTest backtrace-debug builds"
+pushd lightning
+cargo test --verbose --color always --features backtrace
+popd
echo -e "\n\nBuilding with all Log-Limiting features"
pushd lightning
echo -e "\n\nTesting no-std flags in various combinations"
for DIR in lightning lightning-invoice lightning-rapid-gossip-sync; do
- [ "$RUSTC_MINOR_VERSION" -gt 50 ] && cargo test -p $DIR --verbose --color always --no-default-features --features no-std
+ cargo test -p $DIR --verbose --color always --no-default-features --features no-std
# check if there is a conflict between no-std and the default std feature
- [ "$RUSTC_MINOR_VERSION" -gt 50 ] && cargo test -p $DIR --verbose --color always --features no-std
+ cargo test -p $DIR --verbose --color always --features no-std
done
+
for DIR in lightning lightning-invoice lightning-rapid-gossip-sync; do
# check if there is a conflict between no-std and the c_bindings cfg
- [ "$RUSTC_MINOR_VERSION" -gt 50 ] && RUSTFLAGS="--cfg=c_bindings" cargo test -p $DIR --verbose --color always --no-default-features --features=no-std
+ RUSTFLAGS="--cfg=c_bindings" cargo test -p $DIR --verbose --color always --no-default-features --features=no-std
done
RUSTFLAGS="--cfg=c_bindings" cargo test --verbose --color always
echo -e "\n\nTesting no-std build on a downstream no-std crate"
# check no-std compatibility across dependencies
pushd no-std-check
-if [[ $RUSTC_MINOR_VERSION -gt 67 ]]; then
- # lightning-transaction-sync's MSRV is 1.67
- cargo check --verbose --color always --features lightning-transaction-sync
-else
- # The memchr crate switched to an MSRV of 1.60 starting with v2.6.0
- # This is currently only a release dependency via core2, which we intend to work with
- # rust-bitcoin to remove soon.
- [ "$RUSTC_MINOR_VERSION" -lt 60 ] && cargo update -p memchr --precise "2.5.0" --verbose
- cargo check --verbose --color always
-fi
+cargo check --verbose --color always --features lightning-transaction-sync
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
popd
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
-edition = "2018"
+edition = "2021"
# Because the function is unused it gets dropped before we link lightning, so
# we have to duplicate build.rs here. Note that this is only required for
# fuzzing mode.
description = """
Utilities to perform required background tasks for Rust Lightning.
"""
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
all-features = true
//! running properly, and (2) either can or should be run in the background. See docs for
//! [`BackgroundProcessor`] for more details on the nitty-gritty.
-// 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![cfg_attr(not(feature = "futures"), deny(unsafe_code))]
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs::{ChannelMessageHandler, Init};
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
- use lightning::routing::gossip::{NetworkGraph, NodeId, P2PGossipSync};
+ use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::scoring::{ChannelUsage, ScoreUpdate, ScoreLookUp, LockableScore};
use lightning::routing::router::{DefaultRouter, Path, RouteHop, CandidateRouteHop};
use lightning::util::config::UserConfig;
description = """
Utilities to fetch the chain data from a block source and feed them into Rust Lightning.
"""
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
all-features = true
bitcoin = "0.30.2"
hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
lightning = { version = "0.0.118", path = "../lightning" }
-tokio = { version = "1.0", features = [ "io-util", "net", "time" ], optional = true }
+tokio = { version = "1.0", features = [ "io-util", "net", "time", "rt" ], optional = true }
serde_json = { version = "1.0", optional = true }
chunked_transfer = { version = "1.4", optional = true }
//! 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![deny(unsafe_code)]
keywords = [ "lightning", "bitcoin", "invoice", "BOLT11" ]
readme = "README.md"
repository = "https://github.com/lightningdevkit/rust-lightning/"
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
all-features = true
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
-edition = "2018"
+edition = "2021"
[package.metadata]
cargo-fuzz = true
for (idx, c) in hex.as_bytes().iter().filter(|&&c| c != b'\n').enumerate() {
b <<= 4;
match *c {
- b'A'...b'F' => b |= c - b'A' + 10,
- b'a'...b'f' => b |= c - b'a' + 10,
- b'0'...b'9' => b |= c - b'0',
+ b'A'..=b'F' => b |= c - b'A' + 10,
+ b'a'..=b'f' => b |= c - b'a' + 10,
+ b'0'..=b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
-// 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![deny(non_upper_case_globals)]
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the required network stack, especially for those already using Tokio.
"""
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
all-features = true
//!
//! [`PeerManager`]: lightning::ln::peer_handler::PeerManager
-// 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
description = """
Utilities for LDK data persistence and retrieval.
"""
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
all-features = true
//! Provides utilities for LDK data persistence and retrieval.
-//
-// TODO: 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
authors = ["Arik Sosman <git@arik.io>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning"
-edition = "2018"
+edition = "2021"
description = """
Utility to process gossip routing data from Rapid Gossip Sync Server.
"""
-// 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![deny(unsafe_code)]
description = """
Utilities for syncing LDK via the transaction-based `Confirm` interface.
"""
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
all-features = true
async-interface = []
[dependencies]
-lightning = { version = "0.0.118", path = "../lightning", default-features = false }
+lightning = { version = "0.0.118", path = "../lightning", default-features = false, features = ["std"] }
bitcoin = { version = "0.30.2", default-features = false }
bdk-macros = "0.6"
futures = { version = "0.3", optional = true }
electrum-client = { version = "0.18.0", optional = true }
[dev-dependencies]
-lightning = { version = "0.0.118", path = "../lightning", features = ["std", "_test_utils"] }
-electrsd = { version = "0.26.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_25_0"] }
+lightning = { version = "0.0.118", path = "../lightning", default-features = false, features = ["std", "_test_utils"] }
tokio = { version = "1.14.0", features = ["full"] }
+
+[target.'cfg(not(no_download))'.dev-dependencies]
+electrsd = { version = "0.26.0", default-features = false, features = ["legacy", "esplora_a33e97e1", "bitcoind_25_0"] }
+
+[target.'cfg(no_download)'.dev-dependencies]
+electrsd = { version = "0.26.0", default-features = false, features = ["legacy"] }
//! [`ChainMonitor`]: lightning::chain::chainmonitor::ChainMonitor
//! [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
-// 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![deny(unsafe_code)]
Does most of the hard work, without implying a specific runtime, requiring clients implement basic network logic, chain interactions and disk storage.
Still missing tons of error-handling. See GitHub issues for suggested projects if you want to contribute. Don't have to bother telling you not to use this for anything serious, because you'd have to build a client around it to even try.
"""
-edition = "2018"
+edition = "2021"
[package.metadata.docs.rs]
features = ["std"]
/// be sure to manage both cases correctly.
///
/// Bitcoin transaction packages are defined in BIP 331 and here:
- /// https://github.com/bitcoin/bitcoin/blob/master/doc/policy/packages.md
+ /// <https://github.com/bitcoin/bitcoin/blob/master/doc/policy/packages.md>
fn broadcast_transactions(&self, txs: &[&Transaction]);
}
outpoint: OutPoint { txid, index: idx as u16 },
script_pubkey: output.script_pubkey,
};
- chain_source.register_output(output)
+ log_trace!(logger, "Adding monitoring for spends of outpoint {} to the filter", output.outpoint);
+ chain_source.register_output(output);
}
}
}
},
}
if let Some(ref chain_source) = self.chain_source {
- monitor.load_outputs_to_watch(chain_source);
+ monitor.load_outputs_to_watch(chain_source , &self.logger);
}
entry.insert(MonitorHolder {
monitor,
/// Loads the funding txo and outputs to watch into the given `chain::Filter` by repeatedly
/// calling `chain::Filter::register_output` and `chain::Filter::register_tx` until all outputs
/// have been registered.
- pub fn load_outputs_to_watch<F: Deref>(&self, filter: &F) where F::Target: chain::Filter {
+ pub fn load_outputs_to_watch<F: Deref, L: Deref>(&self, filter: &F, logger: &L)
+ where
+ F::Target: chain::Filter, L::Target: Logger,
+ {
let lock = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*lock);
+ log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo().0);
filter.register_tx(&lock.get_funding_txo().0.txid, &lock.get_funding_txo().1);
for (txid, outputs) in lock.get_outputs_to_watch().iter() {
for (index, script_pubkey) in outputs.iter() {
assert!(*index <= u16::max_value() as u32);
+ let outpoint = OutPoint { txid: *txid, index: *index as u16 };
+ log_trace!(logger, "Registering outpoint {} with the filter for monitoring spends", outpoint);
filter.register_output(WatchedOutput {
block_hash: None,
- outpoint: OutPoint { txid: *txid, index: *index as u16 },
+ outpoint,
script_pubkey: script_pubkey.clone(),
});
}
// broadcastable commitment transaction has the HTLC in it, but it
// cannot currently change after channel initialization, so we don't
// need to here.
- let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
+ let confirmed_htlcs_iter: &mut dyn Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
let mut matched_htlc = false;
for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
if height > self.best_block.height() {
self.best_block = BestBlock::new(block_hash, height);
+ log_trace!(logger, "Connecting new block {} at height {}", block_hash, height);
self.block_confirmed(height, block_hash, vec![], vec![], vec![], &broadcaster, &fee_estimator, logger)
} else if block_hash != self.best_block.block_hash() {
self.best_block = BestBlock::new(block_hash, height);
+ log_trace!(logger, "Best block re-orged, replaced with new block {} at height {}", block_hash, height);
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger);
Vec::new()
let mut claimable_outpoints = Vec::new();
'tx_iter: for tx in &txn_matched {
let txid = tx.txid();
+ log_trace!(logger, "Transaction {} confirmed in block {}", txid , block_hash);
// If a transaction has already been confirmed, ensure we don't bother processing it duplicatively.
if Some(txid) == self.funding_spend_confirmed {
log_debug!(logger, "Skipping redundant processing of funding-spend tx {} as it was previously confirmed", txid);
pub script_pubkey: ScriptBuf,
}
-impl<T: Listen> Listen for core::ops::Deref<Target = T> {
+impl<T: Listen> Listen for dyn core::ops::Deref<Target = T> {
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
(**self).filtered_block_connected(header, txdata, height);
}
use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::consensus::Encodable;
+use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::ecdsa::Signature;
) -> Result<CoinSelection, ()>;
/// Signs and provides the full witness for all inputs within the transaction known to the
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
- fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()>;
+ ///
+ /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
+ /// unsigned transaction and then sign it with your wallet.
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
}
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
/// the transaction known to the wallet (i.e., any provided via
/// [`WalletSource::list_confirmed_utxos`]).
- fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()>;
+ ///
+ /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
+ /// unsigned transaction and then sign it with your wallet.
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
}
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
.or_else(|_| do_coin_selection(true, true))
}
- fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()> {
- self.source.sign_tx(tx)
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
+ self.source.sign_psbt(psbt)
}
}
}
/// Updates a transaction with the result of a successful coin selection attempt.
- fn process_coin_selection(&self, tx: &mut Transaction, mut coin_selection: CoinSelection) {
- for utxo in coin_selection.confirmed_utxos.drain(..) {
+ fn process_coin_selection(&self, tx: &mut Transaction, coin_selection: &CoinSelection) {
+ for utxo in coin_selection.confirmed_utxos.iter() {
tx.input.push(TxIn {
previous_output: utxo.outpoint,
script_sig: ScriptBuf::new(),
witness: Witness::new(),
});
}
- if let Some(change_output) = coin_selection.change_output.take() {
+ if let Some(change_output) = coin_selection.change_output.clone() {
tx.output.push(change_output);
} else if tx.output.is_empty() {
// We weren't provided a change output, likely because the input set was a perfect
log_debug!(self.logger, "Peforming coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
package_target_feerate_sat_per_1000_weight);
- let coin_selection = self.utxo_source.select_confirmed_utxos(
+ let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
)?;
let total_input_amount = must_spend_amount +
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
- self.process_coin_selection(&mut anchor_tx, coin_selection);
+ self.process_coin_selection(&mut anchor_tx, &coin_selection);
let anchor_txid = anchor_tx.txid();
- debug_assert_eq!(anchor_tx.output.len(), 1);
+ // construct psbt
+ let mut anchor_psbt = PartiallySignedTransaction::from_unsigned_tx(anchor_tx).unwrap();
+ // add witness_utxo to anchor input
+ anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
+ // add witness_utxo to remaining inputs
+ for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
+ // add 1 to skip the anchor input
+ let index = idx + 1;
+ debug_assert_eq!(anchor_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
+ if utxo.output.script_pubkey.is_witness_program() {
+ anchor_psbt.inputs[index].witness_utxo = Some(utxo.output);
+ }
+ }
+
+ debug_assert_eq!(anchor_psbt.unsigned_tx.output.len(), 1);
#[cfg(debug_assertions)]
- let unsigned_tx_weight = anchor_tx.weight().to_wu() - (anchor_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
+ let unsigned_tx_weight = anchor_psbt.unsigned_tx.weight().to_wu() - (anchor_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid);
- anchor_tx = self.utxo_source.sign_tx(anchor_tx)?;
+ anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?;
let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider);
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
#[cfg(debug_assertions)]
let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<u64>();
- let coin_selection = self.utxo_source.select_confirmed_utxos(
+ let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
claim_id, must_spend, &htlc_tx.output, target_feerate_sat_per_1000_weight,
)?;
let total_input_amount = must_spend_amount +
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
- self.process_coin_selection(&mut htlc_tx, coin_selection);
+ self.process_coin_selection(&mut htlc_tx, &coin_selection);
+
+ // construct psbt
+ let mut htlc_psbt = PartiallySignedTransaction::from_unsigned_tx(htlc_tx).unwrap();
+ // add witness_utxo to htlc inputs
+ for (i, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
+ debug_assert_eq!(htlc_psbt.unsigned_tx.input[i].previous_output, htlc_descriptor.outpoint());
+ htlc_psbt.inputs[i].witness_utxo = Some(htlc_descriptor.previous_utxo(&self.secp));
+ }
+ // add witness_utxo to remaining inputs
+ for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
+ // offset to skip the htlc inputs
+ let index = idx + htlc_descriptors.len();
+ debug_assert_eq!(htlc_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
+ if utxo.output.script_pubkey.is_witness_program() {
+ htlc_psbt.inputs[index].witness_utxo = Some(utxo.output);
+ }
+ }
#[cfg(debug_assertions)]
- let unsigned_tx_weight = htlc_tx.weight().to_wu() - (htlc_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
+ let unsigned_tx_weight = htlc_psbt.unsigned_tx.weight().to_wu() - (htlc_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
- log_debug!(self.logger, "Signing HTLC transaction {}", htlc_tx.txid());
- htlc_tx = self.utxo_source.sign_tx(htlc_tx)?;
+ log_debug!(self.logger, "Signing HTLC transaction {}", htlc_psbt.unsigned_tx.txid());
+ htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
let mut signers = BTreeMap::new();
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
#![cfg_attr(not(any(test, fuzzing, feature = "_test_utils")), deny(missing_docs))]
#![cfg_attr(not(any(test, feature = "_test_utils")), forbid(unsafe_code))]
-// 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(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
// In general, rust is absolutely horrid at supporting users doing things like,
// for example, compiling Rust code for real environments. Disable useless lints
// larger. If we don't know that time has moved forward, we can just set it to the last
// time we saw and it will be ignored.
let best_time = self.context.update_time_counter;
- match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&NodeSigner, &UserConfig)>, logger) {
+ match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&dyn NodeSigner, &UserConfig)>, logger) {
Ok((channel_ready, timed_out_htlcs, announcement_sigs)) => {
assert!(channel_ready.is_none(), "We can't generate a funding with 0 confirmations?");
assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?");
assert_eq!(decoded_chan.context.holding_cell_htlc_updates, holding_cell_htlc_updates);
}
- #[cfg(feature = "_test_vectors")]
+ #[cfg(all(feature = "_test_vectors", not(feature = "grind_signatures")))]
#[test]
fn outbound_commitment_test() {
use bitcoin::sighash;
// Test vectors from BOLT 3 Appendices C and F (anchors):
let feeest = TestFeeEstimator{fee_est: 15000};
- let logger : Arc<Logger> = Arc::new(test_utils::TestLogger::new());
+ let logger : Arc<dyn Logger> = Arc::new(test_utils::TestLogger::new());
let secp_ctx = Secp256k1::new();
let mut signer = InMemorySigner::new(
// then waiting ANTI_REORG_DELAY to be reorg-safe on the outbound HLTC and
// failing the corresponding htlc backward, and us now seeing the last block of ANTI_REORG_DELAY before
// LATENCY_GRACE_PERIOD_BLOCKS.
-#[deny(const_err)]
#[allow(dead_code)]
const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - CLTV_CLAIM_BUFFER - ANTI_REORG_DELAY - LATENCY_GRACE_PERIOD_BLOCKS;
// Check for ability of an attacker to make us fail on-chain by delaying an HTLC claim. See
// ChannelMonitor::should_broadcast_holder_commitment_txn for a description of why this is needed.
-#[deny(const_err)]
#[allow(dead_code)]
const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER;
// 0.0.102+
for (_, monitor) in args.channel_monitors.iter() {
let counterparty_opt = id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id());
- let chan_id = monitor.get_funding_txo().0.to_channel_id();
if counterparty_opt.is_none() {
let logger = WithChannelMonitor::from(&args.logger, monitor);
for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() {
// We really want std::thread::scope, but its not stable until 1.63. Until then, we get unsafe.
let node_ref = NodePtr::from_node(&nodes[0]);
move || {
+ let _ = &node_ref;
let node_a = unsafe { &*node_ref.0 };
while Instant::now() < end_time {
node_a.node.get_and_clear_pending_events(); // wipe the PendingHTLCsForwardable
// broadcast_node_announcement panics) of the maximum-length addresses would fit in a 64KB
// message...
const HALF_MESSAGE_IS_ADDRS: u32 = ::core::u16::MAX as u32 / (SocketAddress::MAX_LEN as u32 + 1) / 2;
- #[deny(const_err)]
#[allow(dead_code)]
// ...by failing to compile if the number of addresses that would be half of a message is
// smaller than 100:
}
struct WrapperLog {
- logger: Arc<Logger>
+ logger: Arc<dyn Logger>
}
impl WrapperLog {
- fn new(logger: Arc<Logger>) -> WrapperLog {
+ fn new(logger: Arc<dyn Logger>) -> WrapperLog {
WrapperLog {
logger,
}
fn test_logging_macros() {
let mut logger = TestLogger::new();
logger.enable(Level::Gossip);
- let logger : Arc<Logger> = Arc::new(logger);
+ let logger : Arc<dyn Logger> = Arc::new(logger);
let wrapper = WrapperLog::new(Arc::clone(&logger));
wrapper.call_macros();
}
#[inline]
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
match self.0 {
- 0...0xFC => {
+ 0..=0xFC => {
(self.0 as u8).write(writer)
},
- 0xFD...0xFFFF => {
+ 0xFD..=0xFFFF => {
0xFDu8.write(writer)?;
(self.0 as u16).write(writer)
},
- 0x10000...0xFFFFFFFF => {
+ 0x10000..=0xFFFFFFFF => {
0xFEu8.write(writer)?;
(self.0 as u32).write(writer)
},
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
+use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::Sequence;
pub fn pubkey(byte: u8) -> PublicKey {
pub added_monitors: Mutex<Vec<(OutPoint, channelmonitor::ChannelMonitor<TestChannelSigner>)>>,
pub monitor_updates: Mutex<HashMap<ChannelId, Vec<channelmonitor::ChannelMonitorUpdate>>>,
pub latest_monitor_update_id: Mutex<HashMap<ChannelId, (OutPoint, u64, MonitorUpdateId)>>,
- pub chain_monitor: chainmonitor::ChainMonitor<TestChannelSigner, &'a TestChainSource, &'a chaininterface::BroadcasterInterface, &'a TestFeeEstimator, &'a TestLogger, &'a chainmonitor::Persist<TestChannelSigner>>,
+ pub chain_monitor: chainmonitor::ChainMonitor<TestChannelSigner, &'a TestChainSource, &'a dyn chaininterface::BroadcasterInterface, &'a TestFeeEstimator, &'a TestLogger, &'a dyn chainmonitor::Persist<TestChannelSigner>>,
pub keys_manager: &'a TestKeysInterface,
/// If this is set to Some(), the next update_channel call (not watch_channel) must be a
/// ChannelForceClosed event for the given channel_id with should_broadcast set to the given
pub expect_monitor_round_trip_fail: Mutex<Option<ChannelId>>,
}
impl<'a> TestChainMonitor<'a> {
- pub fn new(chain_source: Option<&'a TestChainSource>, broadcaster: &'a chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator, persister: &'a chainmonitor::Persist<TestChannelSigner>, keys_manager: &'a TestKeysInterface) -> Self {
+ pub fn new(chain_source: Option<&'a TestChainSource>, broadcaster: &'a dyn chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator, persister: &'a dyn chainmonitor::Persist<TestChannelSigner>, keys_manager: &'a TestKeysInterface) -> Self {
Self {
added_monitors: Mutex::new(Vec::new()),
monitor_updates: Mutex::new(HashMap::new()),
Ok(ScriptBuf::new_p2pkh(&public_key.pubkey_hash()))
}
- fn sign_tx(&self, mut tx: Transaction) -> Result<Transaction, ()> {
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
+ let mut tx = psbt.extract_tx();
let utxos = self.utxos.borrow();
for i in 0..tx.input.len() {
if let Some(utxo) = utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output) {
[package]
name = "msrv-check"
version = "0.1.0"
-edition = "2018"
+edition = "2021"
[dependencies]
lightning = { path = "../lightning" }
lightning-persister = { path = "../lightning-persister" }
lightning-background-processor = { path = "../lightning-background-processor", features = ["futures"] }
lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync" }
+lightning-custom-message = { path = "../lightning-custom-message" }
[package]
name = "no-std-check"
version = "0.1.0"
-edition = "2018"
+edition = "2021"
[features]
default = ["lightning/no-std", "lightning-invoice/no-std", "lightning-rapid-gossip-sync/no-std"]