Skip to content
Merged
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ lightning = "0.2.2"

[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies]
electrum-client = { version = "0.25.0", default-features = false, features = ["use-rustls-ring", "proxy"], optional = true }
rustls = { version = "0.23.32", default-features = false, features = ["ring"], optional = true }

[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
Expand Down Expand Up @@ -93,4 +94,4 @@ esplora = []
electrum = ["dep:electrum-client"]
# Feature to enable tests that require previous initialization of a regtest environment
regtest = []
ws = ["tokio/sync", "tokio/rt", "tokio/macros", "dep:tokio-tungstenite-wasm"]
ws = ["tokio/sync", "tokio/rt", "tokio/macros", "dep:tokio-tungstenite-wasm", "dep:rustls"]
25 changes: 15 additions & 10 deletions bindings/Makefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
REPO_ROOT := $(abspath ..)
CARGO_TARGET_DIR ?= ../target
MANYLINUX_IMAGE ?= quay.io/pypa/manylinux_2_28_x86_64
DOCKER_PLATFORM_ARG := $(if $(MANYLINUX_PLATFORM),--platform $(MANYLINUX_PLATFORM),)

build-debug:
cargo build
cp ../target/debug/libboltz_client.so ./python/
cargo run generate --language python --no-format --library ../target/debug/libboltz_client.so --out-dir python/
cp $(CARGO_TARGET_DIR)/debug/libboltz_client.so ./python/
cargo run generate --language python --no-format --library $(CARGO_TARGET_DIR)/debug/libboltz_client.so --out-dir python/

build-release:
cargo build --release
cargo build # Also build debug for uniffi to read metadata
cp ../target/release/libboltz_client.so ./python/
cargo run generate --language python --no-format --library ../target/debug/libboltz_client.so --out-dir python/
cp $(CARGO_TARGET_DIR)/release/libboltz_client.so ./python/
cargo run generate --language python --no-format --library $(CARGO_TARGET_DIR)/debug/libboltz_client.so --out-dir python/

build-python: build-release
cd python && uv build

prepare-release-python: build-python
cd python && \
uvx auditwheel repair dist/*-py3-none-any.whl --wheel-dir dist/ && \
rm dist/*-py3-none-any.whl && \
uv run --isolated --no-project --with dist/*.whl python -c "import boltz_client" && \
uv run --isolated --no-project --with dist/*.tar.gz python -c "import boltz_client"
build-python-manylinux:
docker run --rm $(DOCKER_PLATFORM_ARG) \
-v "$(REPO_ROOT):/io" \
-w /io/bindings \
"$(MANYLINUX_IMAGE)" \
bash scripts/build-python-manylinux.sh

format-python:
# Also formats Python tests
Expand Down
8 changes: 8 additions & 0 deletions bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,23 @@ make build-release

# Build Python package (wheel)
make build-python

# Build release Python artifacts in a manylinux container
make build-python-manylinux
```

Use `build-python-manylinux` for release artifacts. It builds in manylinux,
repairs the wheel with `auditwheel`, removes the intermediate `py3-none-any`
wheel, and smoke-tests both the repaired wheel and the sdist.

### Generated Files

The build process generates:

- `libboltz_client.so` - The compiled Rust library
- `boltz_client.py` - Python bindings module
- `dist/boltz_client-*.whl` - Installable Python wheel
- `dist/boltz_client-*.tar.gz` - Installable source distribution

### Testing

Expand Down
30 changes: 30 additions & 0 deletions bindings/scripts/build-python-manylinux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail

export PATH="/opt/python/cp310-cp310/bin:$HOME/.cargo/bin:$PATH"

if ! command -v cargo >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --profile minimal --default-toolchain none
fi

make build-release

cd python
rm -rf dist
uv build

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT

auditwheel repair --plat manylinux_2_28_x86_64 dist/*-py3-none-any.whl --wheel-dir "$tmp_dir"
rm dist/*-py3-none-any.whl
mv "$tmp_dir"/*.whl dist/

python -m venv "$tmp_dir/wheel"
"$tmp_dir/wheel/bin/python" -m pip install --no-index dist/*.whl
"$tmp_dir/wheel/bin/python" -c "import boltz_client"

python -m venv "$tmp_dir/sdist"
"$tmp_dir/sdist/bin/python" -m pip install dist/*.tar.gz
"$tmp_dir/sdist/bin/python" -c "import boltz_client"
4 changes: 4 additions & 0 deletions src/swaps/boltz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
//! );

use crate::network::Network;
#[cfg(feature = "ws")]
use crate::util::ensure_rustls_crypto_provider;
use crate::{error::Error, network::Chain, util::secrets::Preimage};
use crate::{BtcSwapScript, LBtcSwapScript};
use bitcoin::secp256k1;
Expand Down Expand Up @@ -376,6 +378,7 @@ impl BoltzApiClientV2 {
/// Returns the web socket connection to the boltz server
#[cfg(feature = "ws")]
pub async fn connect_ws(&self) -> Result<WebSocketStream, Error> {
ensure_rustls_crypto_provider();
Ok(connect(self.get_ws_url()).await?)
}

Expand All @@ -385,6 +388,7 @@ impl BoltzApiClientV2 {
&self,
protocols: &[&str],
) -> Result<WebSocketStream, Error> {
ensure_rustls_crypto_provider();
Ok(connect_with_protocols(self.get_ws_url(), protocols).await?)
}

Expand Down
4 changes: 3 additions & 1 deletion src/swaps/status_stream.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::boltz::{InvoiceCreated, InvoiceError, SwapStatus, WsRequest, WsResponse};
use crate::error::Error;
use crate::util::sleep;
use crate::util::{ensure_rustls_crypto_provider, sleep};
use futures_util::{SinkExt, StreamExt};
use log::{debug, error, info, trace, warn};
use std::collections::{HashMap, HashSet};
Expand All @@ -22,6 +22,8 @@ struct RequestPacket {

impl BoltzWsConnection {
async fn new(url: &str, protocols: Option<&[&str]>) -> Result<Self, Error> {
ensure_rustls_crypto_provider();

let ws = if let Some(protocols) = protocols {
connect_with_protocols(url, protocols).await?
} else {
Expand Down
11 changes: 11 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ pub fn setup_logger() {
});
}

#[cfg(all(
feature = "ws",
not(all(target_family = "wasm", target_os = "unknown"))
))]
pub(crate) fn ensure_rustls_crypto_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
}

#[cfg(all(feature = "ws", target_family = "wasm", target_os = "unknown"))]
pub(crate) fn ensure_rustls_crypto_provider() {}

pub async fn sleep(duration: Duration) {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
{
Expand Down
Loading