Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/0fc-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ jobs:
echo "ELECTRS_EXE=$( pwd )/bin/electrs-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV"
- name: Test with 0FC enabled
run: |
RUSTFLAGS="--cfg no_download --cfg cycle_tests --cfg tokio_unstable --cfg zero_fee_commitment_tests" cargo test -- --test-threads=1
RUSTFLAGS="--cfg no_download --cfg cycle_tests --cfg tokio_unstable --cfg zero_fee_commitment_tests" cargo test -- --test-threads=1 --no-capture
15 changes: 14 additions & 1 deletion .github/workflows/eclair-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,18 @@ concurrency:

jobs:
check-eclair:
name: check-eclair (${{ matrix.name }})
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: standard
eclair_extra_java_opts: ""
rustflags: "--cfg eclair_test"
- name: zero-fee-commitments
eclair_extra_java_opts: "-Declair.features.zero_fee_commitments=optional"
rustflags: "--cfg eclair_test --cfg zero_fee_commitment_tests"
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down Expand Up @@ -37,6 +47,8 @@ jobs:
docker compose -p ldk-node -f tests/docker/docker-compose-eclair.yml exec bitcoin bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass createwallet ldk_node_test

- name: Start Eclair
env:
ECLAIR_EXTRA_JAVA_OPTS: ${{ matrix.eclair_extra_java_opts }}
run: docker compose -p ldk-node -f tests/docker/docker-compose-eclair.yml up -d eclair

- name: Wait for Eclair to be ready
Expand All @@ -54,4 +66,5 @@ jobs:
exit 1

- name: Run Eclair integration tests
run: RUSTFLAGS="--cfg eclair_test" cargo test --test integration_tests_eclair -- --show-output --test-threads=1
run: |
RUSTFLAGS="${{ matrix.rustflags }}" cargo test --test integration_tests_eclair -- --show-output --test-threads=1
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- `EsploraSyncConfig` and `ElectrumSyncConfig` now support `force_wallet_full_scan`. When set,
the on-chain wallet keeps using BDK `full_scan` instead of incremental sync until a full scan
succeeds, allowing restored wallets to rediscover funds sent to previously-unknown addresses.
- The `ChannelDetails` returned by `Node::list_channels` now exposes the negotiated
`ChannelTypeFeatures`.
- `Config::anchor_channels_config` is no longer optional, hence anchor channels can no longer be
disabled. We still negotiate legacy channels if the peer does not support anchor channels.

Expand Down
122 changes: 121 additions & 1 deletion src/ffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ pub use lightning_liquidity::lsps0::ser::LSPSDateTime;
pub use lightning_liquidity::lsps1::msgs::{
LSPS1ChannelInfo, LSPS1OrderId, LSPS1OrderParams, LSPS1PaymentState,
};
use lightning_types::features::{InitFeatures as LdkInitFeatures, NodeFeatures as LdkNodeFeatures};
use lightning_types::features::{
ChannelTypeFeatures as LdkChannelTypeFeatures, InitFeatures as LdkInitFeatures,
NodeFeatures as LdkNodeFeatures,
};
pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
pub use lightning_types::string::UntrustedString;
use vss_client::headers::{
Expand Down Expand Up @@ -1817,6 +1820,102 @@ impl From<LdkNodeFeatures> for NodeFeatures {
}
}

#[derive(Debug, Clone, PartialEq, Eq, uniffi::Object)]
#[uniffi::export(Debug, Eq)]
pub struct ChannelTypeFeatures {
pub(crate) inner: LdkChannelTypeFeatures,
}

#[uniffi::export]
impl ChannelTypeFeatures {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex:

  • [P2] Expose all channel-type feature flags to bindings — /home/tnull/worktrees/ldk-node/pr-992-latest-20260724/src/ffi/types.rs:1830

    ChannelTypeFeatures omits typed accessors for option_scid_alias and option_zeroconf, although both are valid ChannelTypeContext features supported by the underlying type. Rust callers retain those methods, but UniFFI users must manually decode to_bytes().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done below.

/// Constructs channel type features from big-endian BOLT 9 encoded bytes.
#[uniffi::constructor]
pub fn from_bytes(bytes: &[u8]) -> Self {
Self { inner: LdkChannelTypeFeatures::from_be_bytes(bytes.to_vec()) }
}

/// Returns the BOLT 9 big-endian encoded representation of these features.
pub fn to_bytes(&self) -> Vec<u8> {
self.inner.encode()
}

/// Whether this channel type advertises support for `option_static_remotekey`.
pub fn supports_static_remote_key(&self) -> bool {
self.inner.supports_static_remote_key()
}

/// Whether this channel type requires `option_static_remotekey`.
pub fn requires_static_remote_key(&self) -> bool {
self.inner.requires_static_remote_key()
}

/// Whether this channel type advertises support for `option_anchors_zero_fee_htlc_tx`.
pub fn supports_anchors_zero_fee_htlc_tx(&self) -> bool {
self.inner.supports_anchors_zero_fee_htlc_tx()
}

/// Whether this channel type requires `option_anchors_zero_fee_htlc_tx`.
pub fn requires_anchors_zero_fee_htlc_tx(&self) -> bool {
self.inner.requires_anchors_zero_fee_htlc_tx()
}

/// Whether this channel type advertises support for `option_anchors_nonzero_fee_htlc_tx`.
pub fn supports_anchors_nonzero_fee_htlc_tx(&self) -> bool {
self.inner.supports_anchors_nonzero_fee_htlc_tx()
}

/// Whether this channel type requires `option_anchors_nonzero_fee_htlc_tx`.
pub fn requires_anchors_nonzero_fee_htlc_tx(&self) -> bool {
self.inner.requires_anchors_nonzero_fee_htlc_tx()
}

/// Whether this channel type advertises support for `option_taproot`.
pub fn supports_taproot(&self) -> bool {
self.inner.supports_taproot()
}

/// Whether this channel type requires `option_taproot`.
pub fn requires_taproot(&self) -> bool {
self.inner.requires_taproot()
}

/// Whether this channel type advertises support for `option_scid_alias`.
pub fn supports_scid_privacy(&self) -> bool {
self.inner.supports_scid_privacy()
}

/// Whether this channel type requires `option_scid_alias`.
pub fn requires_scid_privacy(&self) -> bool {
self.inner.requires_scid_privacy()
}

/// Whether this channel type advertises support for `option_zeroconf`.
pub fn supports_zero_conf(&self) -> bool {
self.inner.supports_zero_conf()
}

/// Whether this channel type requires `option_zeroconf`.
pub fn requires_zero_conf(&self) -> bool {
self.inner.requires_zero_conf()
}

/// Whether this channel type advertises support for `option_zero_fee_commitments`.
pub fn supports_anchor_zero_fee_commitments(&self) -> bool {
self.inner.supports_anchor_zero_fee_commitments()
}

/// Whether this channel type requires `option_zero_fee_commitments`.
pub fn requires_anchor_zero_fee_commitments(&self) -> bool {
self.inner.requires_anchor_zero_fee_commitments()
}
}

impl From<LdkChannelTypeFeatures> for ChannelTypeFeatures {
fn from(features: LdkChannelTypeFeatures) -> Self {
Self { inner: features }
}
}

#[derive(Debug, Clone, PartialEq, Eq, uniffi::Object)]
#[uniffi::export(Debug, Eq)]
pub struct InitFeatures {
Expand Down Expand Up @@ -2191,6 +2290,27 @@ mod tests {
(ldk_invoice, wrapped_invoice)
}

#[test]
fn test_channel_type_feature_accessors() {
let mut optional = LdkChannelTypeFeatures::empty();
optional.set_scid_privacy_optional();
optional.set_zero_conf_optional();
let optional = ChannelTypeFeatures::from(optional);
assert!(optional.supports_scid_privacy());
assert!(!optional.requires_scid_privacy());
assert!(optional.supports_zero_conf());
assert!(!optional.requires_zero_conf());

let mut required = LdkChannelTypeFeatures::empty();
required.set_scid_privacy_required();
required.set_zero_conf_required();
let required = ChannelTypeFeatures::from(required);
assert!(required.supports_scid_privacy());
assert!(required.requires_scid_privacy());
assert!(required.supports_zero_conf());
assert!(required.requires_zero_conf());
}

#[test]
fn test_invoice_description_conversion() {
let hash = "09d08d4865e8af9266f6cc7c0ae23a1d6bf868207cf8f7c5979b9f6ed850dfb0".to_string();
Expand Down
Loading
Loading