From 393731d42147f494ed92d4ff46f72fd95e8808d3 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Mon, 20 Jul 2026 20:58:41 -0500 Subject: [PATCH 1/2] Update ldk-node dependencies Align ldk-node and the companion Lightning dependencies so shared API types remain compatible. Adapt storage pagination, chain sync settings, and payment metadata to the updated interfaces. --- Cargo.toml | 6 +-- orange-sdk/Cargo.toml | 2 +- orange-sdk/src/dyn_store.rs | 52 ++++++++++++++++--- orange-sdk/src/event.rs | 2 +- orange-sdk/src/lib.rs | 7 ++- orange-sdk/src/lightning_wallet.rs | 11 ++-- orange-sdk/src/rebalancer.rs | 2 +- .../src/trusted_wallet/cashu/cashu_store.rs | 11 ++++ orange-sdk/src/trusted_wallet/dummy.rs | 1 + orange-sdk/tests/test_utils.rs | 2 + 10 files changed, 77 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cac0297..d180c38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,9 @@ codegen-units = 1 # Reduce number of codegen units to increase optimizations. panic = 'abort' # Abort on panic [workspace.dependencies] -bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "ff09ce9401afa448549a8f101172700bcd14d7bb" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "3dfcc4cca1866c5e5d4d4eaf3b82e09584e2ce5c" } +bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "0e430be98c09540624a68a68022ee0551e86d1be" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "506cb91f2e0fb87906188b79777bcf42595d3623" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "506cb91f2e0fb87906188b79777bcf42595d3623" } [profile.release] panic = "abort" diff --git a/orange-sdk/Cargo.toml b/orange-sdk/Cargo.toml index e9eea9f..3da0808 100644 --- a/orange-sdk/Cargo.toml +++ b/orange-sdk/Cargo.toml @@ -23,7 +23,7 @@ _cashu-tests = ["_test-utils", "cdk-ldk-node", "cdk/mint", "cdk-sqlite", "cdk-ax [dependencies] graduated-rebalancer = { path = "../graduated-rebalancer", version = "0.1.0" } -ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "8a5426044bdcae6369d7a847697c6143676e2df5" } +ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "0cea34122767f0b86ebaf29c03f49eb38a85fc00" } lightning-macros = "0.2.0" bitcoin-payment-instructions = { workspace = true, features = ["http"] } chrono = { version = "0.4", default-features = false } diff --git a/orange-sdk/src/dyn_store.rs b/orange-sdk/src/dyn_store.rs index 11162f3..22d40fb 100644 --- a/orange-sdk/src/dyn_store.rs +++ b/orange-sdk/src/dyn_store.rs @@ -1,19 +1,22 @@ -//! Object-safe wrapper around ldk-node's async `KVStore` trait. +//! Object-safe wrapper around ldk-node's async `PaginatedKVStore` trait. //! //! `lightning`'s `KVStore` returns `impl Future` from its methods, which makes the trait not //! object-safe — orange-sdk can't share a backend across components as `Arc`. //! //! This module defines `DynStore`, an object-safe trait covering the kv methods (returning -//! boxed futures), with a blanket impl over any concrete type that implements `KVStore`. The -//! whole crate stores backends as `Arc`; conversion to a value ldk-node accepts -//! happens at the call site through a thin newtype that delegates back to `DynStore`. +//! boxed futures), with a blanket impl over any concrete type that implements +//! `PaginatedKVStore`. The whole crate stores backends as `Arc`; conversion to a +//! value ldk-node accepts happens at the call site through a thin newtype that delegates back to +//! `DynStore`. use std::future::Future; use std::pin::Pin; use std::sync::Arc; use ldk_node::lightning::io; -use ldk_node::lightning::util::persist::KVStore; +use ldk_node::lightning::util::persist::{ + KVStore, PageToken, PaginatedKVStore, PaginatedListResponse, +}; use tokio::task::JoinSet; /// Matches the connection capacity used by the VSS HTTP client. Keeping the @@ -38,11 +41,15 @@ pub(crate) trait DynStore: Send + Sync + 'static { fn list_async( &self, primary_namespace: &str, secondary_namespace: &str, ) -> Pin, io::Error>> + Send + 'static>>; + + fn list_paginated_async( + &self, primary_namespace: &str, secondary_namespace: &str, page_token: Option, + ) -> Pin> + Send + 'static>>; } impl DynStore for T where - T: KVStore + Send + Sync + 'static, + T: PaginatedKVStore + Send + Sync + 'static, { fn read_async( &self, p: &str, s: &str, k: &str, @@ -67,6 +74,12 @@ where ) -> Pin, io::Error>> + Send + 'static>> { Box::pin(::list(self, p, s)) } + + fn list_paginated_async( + &self, p: &str, s: &str, page_token: Option, + ) -> Pin> + Send + 'static>> { + Box::pin(::list_paginated(self, p, s, page_token)) + } } // Make `dyn DynStore` itself implement `KVStore` so the same handle orange-sdk shares @@ -96,8 +109,16 @@ impl KVStore for dyn DynStore { } } +impl PaginatedKVStore for dyn DynStore { + fn list_paginated( + &self, p: &str, s: &str, page_token: Option, + ) -> impl Future> + Send + 'static { + self.list_paginated_async(p, s, page_token) + } +} + /// Cloneable handle wrapping `Arc` that satisfies ldk-node's -/// `KVStore + Send + Sync + 'static` bound on `build_with_store`. The trait impl just +/// `PaginatedKVStore + Send + Sync + 'static` bound on `build_with_store`. The trait impl just /// forwards to the underlying `dyn DynStore`. #[derive(Clone)] pub(crate) struct LdkNodeStore(pub(crate) Arc); @@ -125,6 +146,14 @@ impl KVStore for LdkNodeStore { } } +impl PaginatedKVStore for LdkNodeStore { + fn list_paginated( + &self, p: &str, s: &str, page_token: Option, + ) -> impl Future> + Send + 'static { + self.0.list_paginated_async(p, s, page_token) + } +} + /// Reads a set of keys concurrently while preserving the input order. /// /// Storage formats expose record collections as a list followed by individual @@ -242,6 +271,15 @@ mod tests { } } + impl PaginatedKVStore for ControlledStore { + fn list_paginated( + &self, _primary_namespace: &str, _secondary_namespace: &str, + _page_token: Option, + ) -> impl Future> + Send + 'static { + ready(Ok(PaginatedListResponse { keys: Vec::new(), next_page_token: None })) + } + } + #[tokio::test] async fn bounded_reads_limit_concurrency_and_preserve_order() { let (entered, mut entered_rx) = mpsc::unbounded_channel(); diff --git a/orange-sdk/src/event.rs b/orange-sdk/src/event.rs index fc7994e..848d86f 100644 --- a/orange-sdk/src/event.rs +++ b/orange-sdk/src/event.rs @@ -648,7 +648,7 @@ impl LdkEventHandler { .add_event(Event::ChannelClosed { channel_id, user_channel_id, - counterparty_node_id: counterparty_node_id.unwrap(), // safe + counterparty_node_id, reason, }) .await diff --git a/orange-sdk/src/lib.rs b/orange-sdk/src/lib.rs index 7904801..db1a457 100644 --- a/orange-sdk/src/lib.rs +++ b/orange-sdk/src/lib.rs @@ -1777,6 +1777,7 @@ mod tests { let kind = PaymentKind::Onchain { txid: Txid::from_byte_array([42; 32]), status: ConfirmationStatus::Unconfirmed, + tx_type: None, }; assert!(should_surface_lightning_payment_without_metadata(TxStatus::Pending, &kind)); @@ -1787,7 +1788,11 @@ mod tests { let txid = Txid::from_byte_array([42; 32]); let payment = PaymentDetails { id: LightningPaymentId([24; 32]), - kind: PaymentKind::Onchain { txid, status: ConfirmationStatus::Unconfirmed }, + kind: PaymentKind::Onchain { + txid, + status: ConfirmationStatus::Unconfirmed, + tx_type: None, + }, amount_msat: Some(123_000), fee_paid_msat: None, direction: PaymentDirection::Inbound, diff --git a/orange-sdk/src/lightning_wallet.rs b/orange-sdk/src/lightning_wallet.rs index a9b7b20..f62239e 100644 --- a/orange-sdk/src/lightning_wallet.rs +++ b/orange-sdk/src/lightning_wallet.rs @@ -67,10 +67,8 @@ impl LightningWallet { trusted_peers_no_reserve: vec![config.lsp.1], ..Default::default() }; - let ldk_node_config = ldk_node::config::Config { - anchor_channels_config: Some(anchor_channels_config), - ..Default::default() - }; + let ldk_node_config = + ldk_node::config::Config { anchor_channels_config, ..Default::default() }; let mut builder = ldk_node::Builder::from_config(ldk_node_config); builder.set_network(config.network); let node_entropy = config.seed.to_node_entropy(); @@ -113,6 +111,7 @@ impl LightningWallet { fee_rate_cache_update_interval_secs: 30, }), timeouts_config: SyncTimeoutsConfig::default(), + ..Default::default() } } else { ldk_node::config::EsploraSyncConfig::default() @@ -151,6 +150,7 @@ impl LightningWallet { lightning_wallet_sync_interval_secs: 2, fee_rate_cache_update_interval_secs: 30, }), + ..Default::default() }) } else { None @@ -158,7 +158,7 @@ impl LightningWallet { builder.set_chain_source_electrum(url, sync_config) }, ChainSource::BitcoindRPC { host, port, user, password } => { - builder.set_chain_source_bitcoind_rpc(host, port, user, password) + builder.set_chain_source_bitcoind_rpc(host, port, user, password, None) }, }; @@ -353,6 +353,7 @@ impl LightningWallet { kind: PaymentKind::Onchain { txid: funding_txo.txid, status: ConfirmationStatus::Unconfirmed, // todo how do we update this? + tx_type: None, }, amount_msat: Some(amount_sats * 1_000), fee_paid_msat: Some(69), // todo get real fee diff --git a/orange-sdk/src/rebalancer.rs b/orange-sdk/src/rebalancer.rs index cad0cdb..b2a017a 100644 --- a/orange-sdk/src/rebalancer.rs +++ b/orange-sdk/src/rebalancer.rs @@ -176,7 +176,7 @@ impl RebalanceTrigger for OrangeTrigger { for payment in new_recvs { let payment_id = PaymentId::SelfCustodial(payment.id.0); let (txid, status) = match payment.kind { - PaymentKind::Onchain { txid, status } => (txid, status), + PaymentKind::Onchain { txid, status, .. } => (txid, status), _ => continue, }; let event = Event::OnchainPaymentReceived { diff --git a/orange-sdk/src/trusted_wallet/cashu/cashu_store.rs b/orange-sdk/src/trusted_wallet/cashu/cashu_store.rs index c8822b0..18ad8b8 100644 --- a/orange-sdk/src/trusted_wallet/cashu/cashu_store.rs +++ b/orange-sdk/src/trusted_wallet/cashu/cashu_store.rs @@ -1292,6 +1292,7 @@ mod tests { use cdk::Amount; use cdk::nuts::Proof; use cdk::secret::Secret; + use ldk_node::lightning::util::persist::{PageToken, PaginatedKVStore, PaginatedListResponse}; use std::future::ready; use std::sync::Mutex as StdMutex; use std::sync::atomic::{AtomicBool, Ordering}; @@ -1364,6 +1365,16 @@ mod tests { } } + impl PaginatedKVStore for ProofTestStore { + fn list_paginated( + &self, _primary_namespace: &str, _secondary_namespace: &str, + _page_token: Option, + ) -> impl std::future::Future> + Send + 'static + { + ready(Ok(PaginatedListResponse { keys: Vec::new(), next_page_token: None })) + } + } + fn proof_info(secret: &str, amount: u64) -> ProofInfo { let proof = Proof::new( Amount::from(amount), diff --git a/orange-sdk/src/trusted_wallet/dummy.rs b/orange-sdk/src/trusted_wallet/dummy.rs index d01c0dc..87dfa26 100644 --- a/orange-sdk/src/trusted_wallet/dummy.rs +++ b/orange-sdk/src/trusted_wallet/dummy.rs @@ -65,6 +65,7 @@ impl DummyTrustedWallet { bitcoind.params.rpc_socket.port(), cookie.user, cookie.password, + None, ); let tmp = temp_dir().join(format!("orange-test-{uuid}/dummy-ldk")); diff --git a/orange-sdk/tests/test_utils.rs b/orange-sdk/tests/test_utils.rs index 87fd7e5..2ba7d61 100644 --- a/orange-sdk/tests/test_utils.rs +++ b/orange-sdk/tests/test_utils.rs @@ -155,6 +155,7 @@ fn create_lsp(uuid: Uuid, bitcoind: &Bitcoind) -> Arc { bitcoind.params.rpc_socket.port(), cookie.user, cookie.password, + None, ); let tmp = temp_dir().join(format!("orange-test-{uuid}/lsp")); @@ -213,6 +214,7 @@ fn create_third_party(uuid: Uuid, bitcoind: &Bitcoind) -> Arc { bitcoind.params.rpc_socket.port(), cookie.user, cookie.password, + None, ); let tmp = temp_dir().join(format!("orange-test-{uuid}/payer")); From 915ec201ebf4e7256d946c521ea26a3db00a3216 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Mon, 20 Jul 2026 20:59:25 -0500 Subject: [PATCH 2/2] Enable zero-fee commitments Prefer zero-fee commitment channels when supported to avoid commitment feerate negotiation. Use package-capable Esplora fixtures so integration tests exercise the expected chain source and verify zero-fee negotiation. --- .github/workflows/tests.yml | 30 ++++++++++++++++++++ orange-sdk/Cargo.toml | 2 +- orange-sdk/src/lightning_wallet.rs | 1 + orange-sdk/src/trusted_wallet/dummy.rs | 4 ++- orange-sdk/tests/integration_tests.rs | 5 ++++ orange-sdk/tests/test_utils.rs | 29 ++++++++++++++++---- scripts/build_package_esplora.sh | 38 ++++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 7 deletions(-) create mode 100755 scripts/build_package_esplora.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 78721d4..8771c06 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,6 +34,21 @@ jobs: - uses: Swatinem/rust-cache@v2.7.8 + - name: Cache package-enabled Esplora + id: cache-esplora + uses: actions/cache@v4 + with: + path: bin/electrs-${{ runner.os }}-${{ runner.arch }} + key: electrs-submit-package-8c06d801-${{ runner.os }}-${{ runner.arch }} + + - name: Build package-enabled Esplora + if: steps.cache-esplora.outputs.cache-hit != 'true' + run: | + ./scripts/build_package_esplora.sh "bin/electrs-${{ runner.os }}-${{ runner.arch }}" + + - name: Configure package-enabled Esplora + run: echo "ELECTRS_EXE=$(pwd)/bin/electrs-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV" + - name: Check formatting run: cargo fmt --check @@ -62,5 +77,20 @@ jobs: - uses: Swatinem/rust-cache@v2.7.8 + - name: Cache package-enabled Esplora + id: cache-esplora + uses: actions/cache@v4 + with: + path: bin/electrs-${{ runner.os }}-${{ runner.arch }} + key: electrs-submit-package-8c06d801-${{ runner.os }}-${{ runner.arch }} + + - name: Build package-enabled Esplora + if: steps.cache-esplora.outputs.cache-hit != 'true' + run: | + ./scripts/build_package_esplora.sh "bin/electrs-${{ runner.os }}-${{ runner.arch }}" + + - name: Configure package-enabled Esplora + run: echo "ELECTRS_EXE=$(pwd)/bin/electrs-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV" + - name: Run cashu tests run: cargo test --features _cashu-tests -- --test-threads=2 --nocapture diff --git a/orange-sdk/Cargo.toml b/orange-sdk/Cargo.toml index 3da0808..36e67b8 100644 --- a/orange-sdk/Cargo.toml +++ b/orange-sdk/Cargo.toml @@ -38,7 +38,7 @@ async-trait = "0.1" log = "0.4.28" corepc-node = { version = "0.10.1", features = ["29_0", "download"], optional = true } -electrsd = { version = "0.36.1", default-features = false, features = ["esplora_a33e97e1", "corepc-node_29_0"], optional = true } +electrsd = { version = "0.36.1", default-features = false, features = ["legacy", "corepc-node_29_0"], optional = true } cdk-ldk-node = { version = "0.16.0", optional = true } cdk-sqlite = { version = "0.16.0", optional = true } cdk-axum = { version = "0.16.0", optional = true } diff --git a/orange-sdk/src/lightning_wallet.rs b/orange-sdk/src/lightning_wallet.rs index f62239e..0d89dfe 100644 --- a/orange-sdk/src/lightning_wallet.rs +++ b/orange-sdk/src/lightning_wallet.rs @@ -65,6 +65,7 @@ impl LightningWallet { log_info!(logger, "Creating LDK node..."); let anchor_channels_config = ldk_node::config::AnchorChannelsConfig { trusted_peers_no_reserve: vec![config.lsp.1], + enable_zero_fee_commitments: true, ..Default::default() }; let ldk_node_config = diff --git a/orange-sdk/src/trusted_wallet/dummy.rs b/orange-sdk/src/trusted_wallet/dummy.rs index 87dfa26..ae19b2d 100644 --- a/orange-sdk/src/trusted_wallet/dummy.rs +++ b/orange-sdk/src/trusted_wallet/dummy.rs @@ -52,7 +52,9 @@ impl DummyTrustedWallet { uuid: Uuid, lsp: Arc, bitcoind: Arc, tx_metadata: TxMetadataStore, event_queue: Arc, rt: Arc, ) -> Self { - let mut builder = ldk_node::Builder::new(); + let mut config = ldk_node::config::Config::default(); + config.anchor_channels_config.enable_zero_fee_commitments = true; + let mut builder = ldk_node::Builder::from_config(config); builder.set_network(Network::Regtest); let mut seed: [u8; 64] = [0; 64]; rand::thread_rng().fill_bytes(&mut seed); diff --git a/orange-sdk/tests/integration_tests.rs b/orange-sdk/tests/integration_tests.rs index f421eea..7fd4361 100644 --- a/orange-sdk/tests/integration_tests.rs +++ b/orange-sdk/tests/integration_tests.rs @@ -739,6 +739,11 @@ async fn test_receive_to_ln() { let third_party = Arc::clone(¶ms.third_party); let recv_amt = open_channel_from_lsp(&wallet, Arc::clone(&third_party)).await; + let channel = wallet.channels().into_iter().next().expect("expected a JIT channel"); + assert_eq!( + channel.feerate_sat_per_1000_weight, 0, + "JIT channel should use zero-fee commitments" + ); let txs = wallet.list_transactions().await.unwrap(); assert_eq!(txs.len(), 1); diff --git a/orange-sdk/tests/test_utils.rs b/orange-sdk/tests/test_utils.rs index 2ba7d61..9eb2e25 100644 --- a/orange-sdk/tests/test_utils.rs +++ b/orange-sdk/tests/test_utils.rs @@ -26,6 +26,7 @@ use std::env::temp_dir; use std::future::Future; #[cfg(feature = "_cashu-tests")] use std::net::SocketAddr; +use std::path::PathBuf; #[cfg(feature = "_cashu-tests")] use std::str::FromStr; use std::sync::Arc; @@ -91,9 +92,8 @@ async fn create_bitcoind(uuid: Uuid) -> (Arc, Arc) { let mut electrsd_conf = electrsd::Conf::default(); electrsd_conf.http_enabled = true; electrsd_conf.network = "regtest"; - let electrsd = - ElectrsD::with_conf(electrsd::downloaded_exe_path().unwrap(), &bitcoind, &electrsd_conf) - .unwrap_or_else(|_| panic!("Failed to start electrsd for test {uuid}")); + let electrsd = ElectrsD::with_conf(package_esplora_exe_path(), &bitcoind, &electrsd_conf) + .unwrap_or_else(|_| panic!("Failed to start electrsd for test {uuid}")); // mine 101 blocks to get some spendable funds let address = bitcoind.client.new_address().unwrap(); @@ -104,6 +104,21 @@ async fn create_bitcoind(uuid: Uuid) -> (Arc, Arc) { (Arc::new(bitcoind), Arc::new(electrsd)) } +fn package_esplora_exe_path() -> String { + let path = std::env::var_os("ELECTRS_EXE").map(PathBuf::from).unwrap_or_else(|| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../target/package-esplora/electrs") + }); + + if !path.is_file() { + panic!( + "Package-enabled Esplora not found at {}. Run ./scripts/build_package_esplora.sh or set ELECTRS_EXE.", + path.display() + ); + } + + path.into_os_string().into_string().expect("Esplora executable path must be valid UTF-8") +} + fn wait_for_bitcoind_ready(bitcoind: &Bitcoind) { let max_attempts = 30; let delay = Duration::from_millis(500); @@ -140,7 +155,9 @@ pub async fn generate_blocks(bitcoind: &Bitcoind, electrs: &ElectrsD, num: usize } fn create_lsp(uuid: Uuid, bitcoind: &Bitcoind) -> Arc { - let mut builder = ldk_node::Builder::new(); + let mut config = ldk_node::config::Config::default(); + config.anchor_channels_config.enable_zero_fee_commitments = true; + let mut builder = ldk_node::Builder::from_config(config); builder.set_network(Network::Regtest); let mut seed: [u8; 64] = [0; 64]; rand::thread_rng().fill_bytes(&mut seed); @@ -199,7 +216,9 @@ fn create_lsp(uuid: Uuid, bitcoind: &Bitcoind) -> Arc { } fn create_third_party(uuid: Uuid, bitcoind: &Bitcoind) -> Arc { - let mut builder = ldk_node::Builder::new(); + let mut config = ldk_node::config::Config::default(); + config.anchor_channels_config.enable_zero_fee_commitments = true; + let mut builder = ldk_node::Builder::from_config(config); builder.set_network(Network::Regtest); let mut seed: [u8; 64] = [0; 64]; rand::thread_rng().fill_bytes(&mut seed); diff --git a/scripts/build_package_esplora.sh b/scripts/build_package_esplora.sh new file mode 100755 index 0000000..eba9c8b --- /dev/null +++ b/scripts/build_package_esplora.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Zero-fee commitment tests require Esplora's package broadcast endpoint. +# electrsd's downloadable Esplora revision does not implement it, so build the +# package-enabled revision used by ldk-node's zero-fee integration tests. +esplora_repo="https://github.com/tankyleo/blockstream-electrs.git" +esplora_tag="2026-05-26-electrum-submit-package" +esplora_rev="8c06d8010e43f793b1a65f83695ea846e5cd83ed" + +host_platform="$(rustc --version --verbose | awk '/^host:/ { print $2 }')" +case "$host_platform" in + *linux*|*darwin*) ;; + *) + echo "Unsupported platform: $host_platform" >&2 + exit 1 + ;; +esac + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +output_path="${1:-$repo_root/target/package-esplora/electrs}" +build_root="$repo_root/target/package-esplora-build" +mkdir -p "$build_root" +build_dir="$(mktemp -d "$build_root/build.XXXXXXXX")" +trap 'rm -rf -- "$build_dir"' EXIT + +git clone --branch "$esplora_tag" --depth 1 "$esplora_repo" "$build_dir/blockstream-electrs" + +actual_rev="$(git -C "$build_dir/blockstream-electrs" rev-parse HEAD)" +if [[ "$actual_rev" != "$esplora_rev" ]]; then + echo "Esplora revision mismatch: expected $esplora_rev, got $actual_rev" >&2 + exit 1 +fi + +RUSTFLAGS="" cargo build --release --manifest-path "$build_dir/blockstream-electrs/Cargo.toml" +mkdir -p "$(dirname "$output_path")" +install -m 755 "$build_dir/blockstream-electrs/target/release/electrs" "$output_path" +echo "Built package-enabled Esplora at $output_path"