diff --git a/.github/workflows/eclair-integration.yml b/.github/workflows/eclair-integration.yml index daa4572ccd..f00d8943a8 100644 --- a/.github/workflows/eclair-integration.yml +++ b/.github/workflows/eclair-integration.yml @@ -8,8 +8,19 @@ concurrency: jobs: check-eclair: + name: check-eclair (${{ matrix.name }}) timeout-minutes: 60 runs-on: ubuntu-latest + strategy: + fail-fast: false + 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 @@ -37,6 +48,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 @@ -54,4 +67,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 diff --git a/CHANGELOG.md b/CHANGELOG.md index b231e8d1c4..07078cfa65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/chain/electrum.rs b/src/chain/electrum.rs index 59fa23a6ca..0d897272a7 100644 --- a/src/chain/electrum.rs +++ b/src/chain/electrum.rs @@ -19,7 +19,8 @@ use bdk_wallet::{KeychainKind as BdkKeyChainKind, Update as BdkUpdate}; use bitcoin::transaction::Version; use bitcoin::{FeeRate, Network, Script, ScriptBuf, Transaction, Txid}; use electrum_client::{ - Batch, Client as ElectrumClient, ConfigBuilder as ElectrumConfigBuilder, ElectrumApi, + Batch, BroadcastPackageRes, Client as ElectrumClient, ConfigBuilder as ElectrumConfigBuilder, + ElectrumApi, }; use lightning::chain::{Confirm, Filter, WatchedOutput}; use lightning::util::ser::Writeable; @@ -321,11 +322,6 @@ impl ElectrumChainSource { return Err(Error::ConnectionFailed); }; - // TODO: Use `protocol_version` API once shipped in - // https://github.com/bitcoindevkit/rust-electrum-client/pull/213. - // - // This could still accept an Electrum server running against Bitcoin Core v26 - // through v28, which does not relay ephemeral dust. let spawn_fut = electrum_client.runtime.spawn_blocking({ let electrum_client = Arc::clone(&electrum_client.electrum_client); move || electrum_client.transaction_broadcast_package(&super::dummy_package()) @@ -336,11 +332,19 @@ impl ElectrumChainSource { ); match timeout_fut.await { - Ok(Ok(Ok(_))) => Ok(()), - Ok(Ok(Err( - e @ (electrum_client::Error::Protocol(_) - | electrum_client::Error::AllAttemptsErrored(_)), - ))) => { + Ok(Ok(Ok(result))) => { + if dummy_submit_package_result_matches_v29_or_later(&result) { + Ok(()) + } else { + log_error!( + self.logger, + "Electrum server does not support submitpackage: {:?}", + result + ); + Err(Error::ChainSourceNotSupported) + } + }, + Ok(Ok(Err(e))) if electrum_submitpackage_error_implies_unsupported(&e) => { log_error!(self.logger, "Electrum server does not support submitpackage: {:?}", e); Err(Error::ChainSourceNotSupported) }, @@ -377,6 +381,22 @@ impl ElectrumChainSource { } } +fn electrum_submitpackage_error_implies_unsupported(e: &electrum_client::Error) -> bool { + matches!(e, electrum_client::Error::Protocol(_) | electrum_client::Error::AllAttemptsErrored(_)) +} + +fn dummy_submit_package_result_matches_v29_or_later(result: &BroadcastPackageRes) -> bool { + if result.success { + return false; + } + + super::dummy_package_txids().iter().all(|expected_txid| { + result.errors.iter().any(|error| { + &error.txid == expected_txid && error.error == super::DUMMY_PACKAGE_EXPECTED_ERROR + }) + }) +} + impl Filter for ElectrumChainSource { fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { self.electrum_runtime_status.write().expect("lock").register_tx(txid, script_pubkey) diff --git a/src/chain/esplora.rs b/src/chain/esplora.rs index 21205bd252..465b7d124e 100644 --- a/src/chain/esplora.rs +++ b/src/chain/esplora.rs @@ -13,7 +13,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use bdk_esplora::EsploraAsyncExt; use bitcoin::transaction::Version; use bitcoin::{FeeRate, Network, Script, Txid}; -use esplora_client::AsyncClient as EsploraAsyncClient; +use esplora_client::{AsyncClient as EsploraAsyncClient, SubmitPackageResult}; use lightning::chain::{Confirm, Filter, WatchedOutput}; use lightning::util::ser::Writeable; use lightning_transaction_sync::EsploraSyncClient; @@ -86,16 +86,13 @@ impl EsploraChainSource { } pub(super) async fn validate_zero_fee_commitments_support(&self) -> Result<(), Error> { - // This could still accept an Esplora server running against Bitcoin Core v26 - // through v28, which does not relay ephemeral dust. - self.esplora_client.submit_package(&super::dummy_package(), None, None).await.map_err( - |e| { - if let esplora_client::Error::HttpResponse { status: 404, message } = e { - log_error!( - self.logger, - "Esplora server does not support submitpackage: {}", - message - ); + let result = self + .esplora_client + .submit_package(&super::dummy_package(), None, None) + .await + .map_err(|e| { + if esplora_submitpackage_error_implies_unsupported(&e) { + log_error!(self.logger, "Esplora server does not support submitpackage: {}", e); Error::ChainSourceNotSupported } else { log_error!( @@ -105,8 +102,11 @@ impl EsploraChainSource { ); Error::ConnectionFailed } - }, - )?; + })?; + if !dummy_submit_package_result_matches_v29_or_later(&result) { + log_error!(self.logger, "Esplora server does not support submitpackage: {:?}", result); + return Err(Error::ChainSourceNotSupported); + } Ok(()) } @@ -521,6 +521,23 @@ impl EsploraChainSource { } } +fn esplora_submitpackage_error_implies_unsupported(e: &esplora_client::Error) -> bool { + matches!(e, esplora_client::Error::HttpResponse { status: 400 | 404, .. }) +} + +fn dummy_submit_package_result_matches_v29_or_later(result: &SubmitPackageResult) -> bool { + if result.package_msg != "transaction failed" { + return false; + } + + super::dummy_package_txids().iter().all(|expected_txid| { + result.tx_results.values().any(|tx_result| { + &tx_result.txid == expected_txid + && tx_result.error.as_deref() == Some(super::DUMMY_PACKAGE_EXPECTED_ERROR) + }) + }) +} + impl Filter for EsploraChainSource { fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { self.tx_sync.register_tx(txid, script_pubkey); diff --git a/src/chain/mod.rs b/src/chain/mod.rs index 0f96c409f8..9b2f975446 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -31,19 +31,20 @@ use crate::{Error, PersistedNodeMetrics}; /// We use this parent-child TRUC package to make sure the configured chain source supports /// broadcasting packages via the `submitpackage` Bitcoin Core RPC. -const PARENT_TXID: &str = "9a015f93fac6cb203c2b994e18b85176eb0354a22a468255516f3c6002d3f696"; +const DUMMY_PACKAGE_EXPECTED_ERROR: &str = "bad-txns-inputs-missingorspent"; +const PARENT_TXID: &str = "11105ba7c94f2fdc1b870a9fbdb136ca006bcf598c372548a1a3051001f3238f"; const PARENT_HEX: &str = - "0300000000010160d0cdb72f2ddf719f40ca32f44614c67577fc75996140544003915683c34a310000000000fd\ + "0300000000010160d0cdb72f2ddf719f40ca32f44614c67577fc75996140544003915683c34a310000000000ff\ ffffff0201000000000000000451024e73876100000000000022512042731375894dad3b25092cd0f713dc5bee4\ a71e30a95e1db3d880906d7eba1fa01409327942924218e4eb1635a7cce6706fcb37b8bbb61a2f0b86357356681\ - 4e09419a3501e02252043bb237d479304632282fe9159db9e9a6ae6ec5bedea9f0f115a97b0e00"; -const CHILD_TXID: &str = "d011b3ff78cdfb8b93822639ea87771847936b04bb83afc8763a7c02a386ae26"; + 4e09419a3501e02252043bb237d479304632282fe9159db9e9a6ae6ec5bedea9f0f11500000000"; +const CHILD_TXID: &str = "6a051464dbf0534a060a61355a6a971559de004795328f8dfd19f3197f9bb4b0"; const CHILD_HEX: &str = - "0300000000010296f6d302603c6f515582462aa25403eb7651b8184e992b3c20cbc6fa935f019a0000000000ff\ - ffffff96f6d302603c6f515582462aa25403eb7651b8184e992b3c20cbc6fa935f019a0100000000fdffffff015\ + "030000000001028f23f3011005a3a14825378c59cf6b00ca36b1bd9f0a871bdc2f4fc9a75b10110000000000ff\ + ffffff8f23f3011005a3a14825378c59cf6b00ca36b1bd9f0a871bdc2f4fc9a75b10110100000000ffffffff015\ 660000000000000225120ac18cd599a1be003595854e2eeec18dbe1c92d04b0ba05812d04445e3fcf16bc000140\ 1462a35808d77a164f0a23a84c4721d1545befd09ad19945bb8aa0ea5576953a9699038725f944b1bc429942ef4\ - 7e6504a554babf022cb15db53be2d8c1dbfe5a97b0e00"; + 7e6504a554babf022cb15db53be2d8c1dbfe500000000"; fn dummy_package() -> [bitcoin::Transaction; 2] { use bitcoin::consensus::Decodable; @@ -55,11 +56,27 @@ fn dummy_package() -> [bitcoin::Transaction; 2] { Transaction::consensus_decode(&mut &parent_tx_bytes[..]).expect("read from a constant"); let child = Transaction::consensus_decode(&mut &child_tx_bytes[..]).expect("read from a constant"); - assert_eq!(parent.compute_txid().to_string(), PARENT_TXID); - assert_eq!(child.compute_txid().to_string(), CHILD_TXID); + let [parent_txid, child_txid] = dummy_package_txids(); + assert_eq!(parent.compute_txid(), parent_txid); + assert_eq!(child.compute_txid(), child_txid); + assert_eq!(parent.lock_time, bitcoin::absolute::LockTime::ZERO); + assert_eq!(child.lock_time, bitcoin::absolute::LockTime::ZERO); + assert!(parent + .input + .iter() + .chain(child.input.iter()) + .all(|input| input.sequence == bitcoin::Sequence::MAX)); + assert!(child.input.iter().all(|input| input.previous_output.txid == parent.compute_txid())); [parent, child] } +fn dummy_package_txids() -> [Txid; 2] { + [ + PARENT_TXID.parse().expect("read from a constant"), + CHILD_TXID.parse().expect("read from a constant"), + ] +} + pub(crate) enum WalletSyncStatus { Completed, InProgress { subscribers: tokio::sync::broadcast::Sender> }, diff --git a/src/ffi/types.rs b/src/ffi/types.rs index c6b48dc961..31c1b3607a 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -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::{ @@ -1817,6 +1820,82 @@ impl From for NodeFeatures { } } +#[derive(Debug, Clone, PartialEq, Eq, uniffi::Object)] +#[uniffi::export(Debug, Eq)] +pub struct ChannelTypeFeatures { + pub(crate) inner: LdkChannelTypeFeatures, +} + +#[uniffi::export] +impl ChannelTypeFeatures { + /// 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 { + 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_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 for ChannelTypeFeatures { + fn from(features: LdkChannelTypeFeatures) -> Self { + Self { inner: features } + } +} + #[derive(Debug, Clone, PartialEq, Eq, uniffi::Object)] #[uniffi::export(Debug, Eq)] pub struct InitFeatures { diff --git a/src/lib.rs b/src/lib.rs index cb570243cc..3cfc78223e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -291,6 +291,16 @@ impl Node { return Err(Error::AlreadyRunning); } + match self.start_inner(&mut is_running_lock) { + Ok(()) => Ok(()), + Err(e) => { + self.chain_source.stop(); + Err(e) + }, + } + } + + fn start_inner(&self, is_running_lock: &mut bool) -> Result<(), Error> { log_info!( self.logger, "Starting up LDK Node with node ID {} on network: {}", diff --git a/src/tx_broadcaster.rs b/src/tx_broadcaster.rs index 491a9cbde5..782112dadb 100644 --- a/src/tx_broadcaster.rs +++ b/src/tx_broadcaster.rs @@ -18,7 +18,7 @@ use crate::logger::{log_error, LdkLogger}; use crate::types::Wallet; use crate::Error; -const BCAST_PACKAGE_QUEUE_SIZE: usize = 50; +const BCAST_PACKAGE_QUEUE_SIZE: usize = 256; /// A package of transactions that LDK handed to the broadcaster in one `broadcast_transactions` /// call, along with each transaction's type. Queued until the background task classifies and diff --git a/src/types.rs b/src/types.rs index 5552877ef8..22429d980b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -39,6 +39,8 @@ use lightning::util::sweep::OutputSweeper; use lightning_block_sync::gossip::GossipVerifier; use lightning_liquidity::utils::time::DefaultTimeProvider; use lightning_net_tokio::SocketDescriptor; +#[cfg(not(feature = "uniffi"))] +use lightning_types::features::ChannelTypeFeatures; use crate::chain::bitcoind::UtxoSourceClient; use crate::chain::ChainSource; @@ -51,6 +53,8 @@ use crate::message_handler::NodeCustomMessageHandler; use crate::payment::{PaymentDetails, PendingPaymentDetails}; use crate::runtime::RuntimeSpawner; +#[cfg(feature = "uniffi")] +type ChannelTypeFeatures = Arc; #[cfg(not(feature = "uniffi"))] type InitFeatures = lightning::types::features::InitFeatures; #[cfg(feature = "uniffi")] @@ -642,6 +646,11 @@ pub struct ChannelDetails { /// /// See [`ReserveType`] for details on how reserves differ between anchor and legacy channels. pub reserve_type: Option, + /// The negotiated channel type features. + /// + /// Will be `None` until channel negotiation has completed and the channel type has been + /// determined. + pub channel_type: Option, } impl ChannelDetails { @@ -706,6 +715,7 @@ impl ChannelDetails { .expect("value is set for objects serialized with LDK v0.0.109+"), channel_shutdown_state: value.channel_shutdown_state, reserve_type, + channel_type: value.channel_type.map(maybe_wrap), } } } diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index f8d9d521eb..f569d29858 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -63,6 +63,7 @@ use crate::payment::{ }; use crate::runtime::Runtime; use crate::types::{Broadcaster, PaymentStore, PendingPaymentStore}; +use crate::util::random_range; use crate::{ChainSource, Error}; pub(crate) enum OnchainSendAmount { @@ -326,7 +327,7 @@ impl Wallet { } if !unconfirmed_outbound_txids.is_empty() { - let txs_to_broadcast: Vec = { + let mut txs_to_broadcast: Vec = { let locked_wallet = self.inner.lock().expect("lock"); unconfirmed_outbound_txids .iter() @@ -337,6 +338,10 @@ impl Wallet { }) .collect() }; + for i in (1..txs_to_broadcast.len()).rev() { + let j = random_range(0, i as u64) as usize; + txs_to_broadcast.swap(i, j); + } if !txs_to_broadcast.is_empty() { let tx_count = txs_to_broadcast.len(); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 50e2b993c8..04be196294 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1578,12 +1578,15 @@ pub(crate) async fn do_channel_full_cycle( ); if disable_node_b_reserve { - let node_a_outbound_capacity_msat = node_a.list_channels()[0].outbound_capacity_msat; - let node_a_reserve_msat = - node_a.list_channels()[0].unspendable_punishment_reserve.unwrap() * 1000; - let zero_fee_commitments = node_a.list_channels()[0].feerate_sat_per_1000_weight == 0; + let node_a_channel = node_a.list_channels().into_iter().next().unwrap(); + let node_a_outbound_capacity_msat = node_a_channel.outbound_capacity_msat; + let node_a_reserve_msat = node_a_channel.unspendable_punishment_reserve.unwrap() * 1000; + let zero_fee_commitments = node_a_channel + .channel_type + .as_ref() + .map_or(false, |c| c.requires_anchor_zero_fee_commitments()); let node_a_anchors_msat = if zero_fee_commitments { 0 } else { 2 * 330 * 1000 }; - let funding_amount_msat = node_a.list_channels()[0].channel_value_sats * 1000; + let funding_amount_msat = node_a_channel.channel_value_sats * 1000; // Node B does not have any reserve, so we only subtract a few items on node A's // side to arrive at node B's capacity let node_b_capacity_msat = funding_amount_msat diff --git a/tests/common/scenarios/channel.rs b/tests/common/scenarios/channel.rs index 74e04127c6..038a016ad8 100644 --- a/tests/common/scenarios/channel.rs +++ b/tests/common/scenarios/channel.rs @@ -9,6 +9,8 @@ use std::time::Duration; use electrsd::corepc_node::Client as BitcoindClient; use electrsd::electrum_client::ElectrumApi; +#[cfg(all(eclair_test, zero_fee_commitment_tests))] +use ldk_node::ReserveType; use ldk_node::{Event, Node}; use super::super::external_node::ExternalNode; @@ -41,6 +43,21 @@ pub(crate) async fn open_channel_to_external( .map(|ch| ch.channel_id.clone()) .unwrap_or_else(|| panic!("Could not find channel on external node {}", peer.name())); + #[cfg(all(eclair_test, zero_fee_commitment_tests))] + { + let channel = node + .list_channels() + .into_iter() + .find(|channel| channel.user_channel_id == user_channel_id) + .expect("opened channel should be listed"); + let channel_type = channel.channel_type.as_ref().expect("channel type should be set"); + assert_eq!(channel.counterparty.node_id, ext_node_id); + assert!(channel.counterparty.features.supports_anchor_zero_fee_commitments()); + assert!(channel_type.requires_anchor_zero_fee_commitments()); + assert_eq!(channel.feerate_sat_per_1000_weight, 0); + assert_eq!(channel.reserve_type, Some(ReserveType::Adaptive)); + } + (user_channel_id, ext_channel_id) } diff --git a/tests/common/scenarios/mod.rs b/tests/common/scenarios/mod.rs index ffbfc2b007..5b73b65112 100644 --- a/tests/common/scenarios/mod.rs +++ b/tests/common/scenarios/mod.rs @@ -87,15 +87,15 @@ pub(crate) async fn wait_for_htlcs_settled( panic!("HTLCs did not settle on {} channel {} within 15s", peer.name(), ext_channel_id); } -/// Build a fresh LDK node configured for interop tests. Uses electrum at the +/// Build a fresh LDK node configured for interop tests. Uses esplora at the /// docker-compose default port and bumps sync timeouts for combo stress. pub(crate) fn setup_ldk_node() -> Node { let config = crate::common::random_config(); let mut builder = ldk_node::Builder::from_config(config.node_config); - let mut sync_config = ldk_node::config::ElectrumSyncConfig::default(); + let mut sync_config = ldk_node::config::EsploraSyncConfig::default(); sync_config.timeouts_config.onchain_wallet_sync_timeout_secs = 180; sync_config.timeouts_config.lightning_wallet_sync_timeout_secs = 120; - builder.set_chain_source_electrum("tcp://127.0.0.1:50001".to_string(), Some(sync_config)); + builder.set_chain_source_esplora("http://127.0.0.1:3002".to_string(), Some(sync_config)); let node = builder.build(config.node_entropy).unwrap(); node.start().unwrap(); node diff --git a/tests/docker/docker-compose-eclair.yml b/tests/docker/docker-compose-eclair.yml index 56a5629f1a..e305b0b42c 100644 --- a/tests/docker/docker-compose-eclair.yml +++ b/tests/docker/docker-compose-eclair.yml @@ -5,7 +5,7 @@ services: # causing Eclair to fall behind the chain tip. Host networking avoids # this by keeping all inter-process communication on localhost. bitcoin: - image: blockstream/bitcoind:30.2 + image: blockstream/bitcoind:31.0 platform: linux/amd64 network_mode: host command: @@ -77,4 +77,5 @@ services: -Declair.bitcoind.zmqtx=tcp://127.0.0.1:28333 -Declair.features.keysend=optional -Declair.on-chain-fees.confirmation-priority.funding=slow + ${ECLAIR_EXTRA_JAVA_OPTS:-} -Declair.printToConsole diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index ba15f6fa30..fe8c79f751 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -1686,7 +1686,9 @@ async fn splice_channel() { let user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id()); let opening_transaction_fee_sat = 156; - let zero_fee_commitments = node_a.list_channels()[0].feerate_sat_per_1000_weight == 0; + let channel = node_a.list_channels().into_iter().next().unwrap(); + let zero_fee_commitments = + channel.channel_type.as_ref().map_or(false, |c| c.requires_anchor_zero_fee_commitments()); let closing_transaction_fee_sat = if zero_fee_commitments { 0 } else { 614 }; let anchor_output_sat = if zero_fee_commitments { 0 } else { 330 };