Skip to content

Commit 815ccdb

Browse files
committed
fix: pin crypto provider
1 parent a6c0033 commit 815ccdb

7 files changed

Lines changed: 125 additions & 11 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ env_logger = "0.11.8"
5454
hex = "0.4"
5555
lnurl-rs = { version = "0.9.0", default-features = false, features = ["async", "async-https"], optional = true }
5656
reqwest = { version = "0.12.12" , default-features = false, features = ["json", "rustls-tls"] }
57+
rustls = { version = "0.23.32", default-features = false, features = ["ring"] }
5758
macros = { version = "1.0.0", package = "boltz-client-macros" }
5859
futures-util = "0.3.31"
5960
tokio-tungstenite-wasm = { version = "0.6.0", features = ["rustls-tls-webpki-roots"], optional = true }

bindings/Makefile

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,43 @@
1+
REPO_ROOT := $(abspath ..)
2+
CARGO_TARGET_DIR ?= ../target
3+
MANYLINUX_IMAGE ?= quay.io/pypa/manylinux_2_28_x86_64
4+
MANYLINUX_PYTHON ?= /opt/python/cp310-cp310/bin/python
5+
AUDITWHEEL_PLAT ?= manylinux_2_28_x86_64
6+
RUST_TOOLCHAIN ?= 1.88.0
7+
DOCKER_PLATFORM_ARG := $(if $(MANYLINUX_PLATFORM),--platform $(MANYLINUX_PLATFORM),)
8+
DOCKER_USER ?= $(shell id -u):$(shell id -g)
9+
110
build-debug:
211
cargo build
3-
cp ../target/debug/libboltz_client.so ./python/
4-
cargo run generate --language python --no-format --library ../target/debug/libboltz_client.so --out-dir python/
12+
cp $(CARGO_TARGET_DIR)/debug/libboltz_client.so ./python/
13+
cargo run generate --language python --no-format --library $(CARGO_TARGET_DIR)/debug/libboltz_client.so --out-dir python/
514

615
build-release:
716
cargo build --release
817
cargo build # Also build debug for uniffi to read metadata
9-
cp ../target/release/libboltz_client.so ./python/
10-
cargo run generate --language python --no-format --library ../target/debug/libboltz_client.so --out-dir python/
18+
cp $(CARGO_TARGET_DIR)/release/libboltz_client.so ./python/
19+
cargo run generate --language python --no-format --library $(CARGO_TARGET_DIR)/debug/libboltz_client.so --out-dir python/
1120

1221
build-python: build-release
1322
cd python && uv build
1423

15-
prepare-release-python: build-python
16-
cd python && \
17-
uvx auditwheel repair dist/*-py3-none-any.whl --wheel-dir dist/ && \
18-
rm dist/*-py3-none-any.whl && \
19-
uv run --isolated --no-project --with dist/*.whl python -c "import boltz_client" && \
20-
uv run --isolated --no-project --with dist/*.tar.gz python -c "import boltz_client"
24+
build-python-manylinux:
25+
docker run --rm $(DOCKER_PLATFORM_ARG) \
26+
--user "$(DOCKER_USER)" \
27+
-e HOME=/tmp/boltz-build-home \
28+
-e CARGO_HOME=/tmp/boltz-cargo \
29+
-e RUSTUP_HOME=/tmp/boltz-rustup \
30+
-e CARGO_TARGET_DIR=/tmp/boltz-target \
31+
-e UV_CACHE_DIR=/tmp/boltz-uv-cache \
32+
-e PYTHON_BIN="$(MANYLINUX_PYTHON)" \
33+
-e AUDITWHEEL_PLAT="$(AUDITWHEEL_PLAT)" \
34+
-e RUST_TOOLCHAIN="$(RUST_TOOLCHAIN)" \
35+
-v "$(REPO_ROOT):/io" \
36+
-w /io/bindings \
37+
"$(MANYLINUX_IMAGE)" \
38+
bash scripts/build-python-manylinux.sh
39+
40+
prepare-release-python: build-python-manylinux
2141

2242
format-python:
2343
# Also formats Python tests

bindings/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,25 @@ make build-release
5050

5151
# Build Python package (wheel)
5252
make build-python
53+
54+
# Build release Python artifacts in a manylinux container
55+
make prepare-release-python
5356
```
5457

58+
Use `prepare-release-python` for release artifacts. It builds in manylinux,
59+
repairs the wheel with `auditwheel`, removes the intermediate `py3-none-any`
60+
wheel, and smoke-tests both the repaired wheel and the sdist. Override
61+
`MANYLINUX_IMAGE`, `MANYLINUX_PYTHON`, or `AUDITWHEEL_PLAT` only when the
62+
release target changes.
63+
5564
### Generated Files
5665

5766
The build process generates:
5867

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

6373
### Testing
6474

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
PYTHON_BIN="${PYTHON_BIN:-/opt/python/cp310-cp310/bin/python}"
5+
AUDITWHEEL_PLAT="${AUDITWHEEL_PLAT:-manylinux_2_28_x86_64}"
6+
RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-1.88.0}"
7+
8+
if [ ! -x "$PYTHON_BIN" ]; then
9+
echo "Python interpreter not found: $PYTHON_BIN" >&2
10+
exit 2
11+
fi
12+
13+
mkdir -p "$HOME" "${CARGO_HOME:-$HOME/.cargo}" "${RUSTUP_HOME:-$HOME/.rustup}" "${UV_CACHE_DIR:-$HOME/.cache/uv}"
14+
export PATH="${CARGO_HOME:-$HOME/.cargo}/bin:$HOME/.local/bin:$(dirname "$PYTHON_BIN"):$PATH"
15+
16+
if ! command -v cargo >/dev/null 2>&1; then
17+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
18+
| sh -s -- -y --profile minimal --default-toolchain "$RUST_TOOLCHAIN"
19+
fi
20+
21+
if ! command -v uv >/dev/null 2>&1; then
22+
"$PYTHON_BIN" -m pip install --user --upgrade uv
23+
fi
24+
25+
if ! command -v auditwheel >/dev/null 2>&1; then
26+
"$PYTHON_BIN" -m pip install --user --upgrade auditwheel
27+
fi
28+
29+
make build-release
30+
31+
cd python
32+
rm -rf dist
33+
uv build
34+
35+
repair_dir="$(mktemp -d)"
36+
test_dir="$(mktemp -d)"
37+
cleanup() {
38+
rm -rf "$repair_dir" "$test_dir"
39+
}
40+
trap cleanup EXIT
41+
42+
auditwheel repair --plat "$AUDITWHEEL_PLAT" dist/*-py3-none-any.whl --wheel-dir "$repair_dir"
43+
rm dist/*-py3-none-any.whl
44+
mv "$repair_dir"/*.whl dist/
45+
46+
wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
47+
sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
48+
49+
if [ -z "$wheel" ] || [ -z "$sdist" ]; then
50+
echo "expected wheel and sdist in bindings/python/dist" >&2
51+
exit 1
52+
fi
53+
54+
case "$wheel" in
55+
*py3-none-any*)
56+
echo "refusing release wheel with non-platform tag: $wheel" >&2
57+
exit 1
58+
;;
59+
esac
60+
61+
"$PYTHON_BIN" -m venv "$test_dir/wheel"
62+
"$test_dir/wheel/bin/python" -m pip install --no-index "$wheel"
63+
"$test_dir/wheel/bin/python" -c "import boltz_client"
64+
65+
"$PYTHON_BIN" -m venv "$test_dir/sdist"
66+
"$test_dir/sdist/bin/python" -m pip install "$sdist"
67+
"$test_dir/sdist/bin/python" -c "import boltz_client"

src/swaps/boltz.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
2020
use crate::network::Network;
2121
use crate::{error::Error, network::Chain, util::secrets::Preimage};
22+
#[cfg(feature = "ws")]
23+
use crate::util::ensure_rustls_crypto_provider;
2224
use crate::{BtcSwapScript, LBtcSwapScript};
2325
use bitcoin::secp256k1;
2426
use bitcoin::{hashes::sha256, hex::DisplayHex, PublicKey};
@@ -376,6 +378,7 @@ impl BoltzApiClientV2 {
376378
/// Returns the web socket connection to the boltz server
377379
#[cfg(feature = "ws")]
378380
pub async fn connect_ws(&self) -> Result<WebSocketStream, Error> {
381+
ensure_rustls_crypto_provider();
379382
Ok(connect(self.get_ws_url()).await?)
380383
}
381384

@@ -385,6 +388,7 @@ impl BoltzApiClientV2 {
385388
&self,
386389
protocols: &[&str],
387390
) -> Result<WebSocketStream, Error> {
391+
ensure_rustls_crypto_provider();
388392
Ok(connect_with_protocols(self.get_ws_url(), protocols).await?)
389393
}
390394

src/swaps/status_stream.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::boltz::{InvoiceCreated, InvoiceError, SwapStatus, WsRequest, WsResponse};
22
use crate::error::Error;
3-
use crate::util::sleep;
3+
use crate::util::{ensure_rustls_crypto_provider, sleep};
44
use futures_util::{SinkExt, StreamExt};
55
use log::{debug, error, info, trace, warn};
66
use std::collections::{HashMap, HashSet};
@@ -22,6 +22,8 @@ struct RequestPacket {
2222

2323
impl BoltzWsConnection {
2424
async fn new(url: &str, protocols: Option<&[&str]>) -> Result<Self, Error> {
25+
ensure_rustls_crypto_provider();
26+
2527
let ws = if let Some(protocols) = protocols {
2628
connect_with_protocols(url, protocols).await?
2729
} else {

src/util/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ use crate::error::Error;
1717
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1818
static INIT: std::sync::Once = std::sync::Once::new();
1919

20+
#[cfg(feature = "ws")]
21+
static RUSTLS_CRYPTO_PROVIDER: std::sync::Once = std::sync::Once::new();
22+
2023
/// Setup function that will only run once, even if called multiple times.
2124
pub fn setup_logger() {
2225
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
@@ -32,6 +35,13 @@ pub fn setup_logger() {
3235
});
3336
}
3437

38+
#[cfg(feature = "ws")]
39+
pub(crate) fn ensure_rustls_crypto_provider() {
40+
RUSTLS_CRYPTO_PROVIDER.call_once(|| {
41+
let _ = rustls::crypto::ring::default_provider().install_default();
42+
});
43+
}
44+
3545
pub async fn sleep(duration: Duration) {
3646
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3747
{

0 commit comments

Comments
 (0)