Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b7fe450
Add core utility functions for hex encoding and HTTP retry
BitcoinZavior Jan 13, 2026
85dfab4
Add utility functions and target triple mapping
BitcoinZavior Jan 13, 2026
feb7f85
Add options and configuration parsing
BitcoinZavior Jan 13, 2026
e126921
Add crate hash calculation
BitcoinZavior Jan 13, 2026
9d582c6
Add Rust toolchain detection
BitcoinZavior Jan 13, 2026
1e2df77
Add Cargo build integration
BitcoinZavior Jan 14, 2026
6c6f974
Add artifacts provider for download and verification
BitcoinZavior Jan 14, 2026
c19cef8
Add precompiled builder main integration
BitcoinZavior Jan 14, 2026
7dab321
Add CLI support utilities
BitcoinZavior Jan 14, 2026
224d288
Add CLI framework
BitcoinZavior Jan 15, 2026
bb8ca56
Add CLI commands: targets and hash
BitcoinZavior Jan 15, 2026
ac5f174
Add CLI commands: gen_key and sign
BitcoinZavior Jan 15, 2026
8434ef4
Add CLI command: precompile_binaries
BitcoinZavior Jan 15, 2026
e2781fe
Add build tool executable
BitcoinZavior Jan 16, 2026
0d197a2
Add tests for precompiled binaries
BitcoinZavior Jan 16, 2026
675d6e9
Add documentation for precompiled binaries
BitcoinZavior Jan 16, 2026
e9051e6
Add CI workflow for precompiling binaries
BitcoinZavior Jan 17, 2026
518cfa5
Integrate precompile workflow into main CI
BitcoinZavior Jan 17, 2026
7728dda
Update build hook to use precompiled builder
BitcoinZavior Jan 17, 2026
8576a9c
Add dependencies and configuration to pubspec.yaml
BitcoinZavior Jan 17, 2026
fbf3f6f
Update .gitignore and README documentation
BitcoinZavior Jan 17, 2026
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
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [main, "**"]
branches: [main]
pull_request:
branches: [main]

Expand Down Expand Up @@ -62,3 +62,11 @@ jobs:
- name: Run tests
run: |
dart test

precompile-binaries:
needs: build-and-test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: write
uses: ./.github/workflows/precompile_binaries.yml
secrets: inherit
88 changes: 88 additions & 0 deletions .github/workflows/precompile_binaries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Precompile Binaries

on:
workflow_call:
secrets:
PRECOMPILED_PRIVATE_KEY:
required: true
workflow_dispatch:

permissions:
contents: write

env:
CRATE_DIR: "native"
CRATE_PACKAGE: "bdk_dart_ffi"

jobs:
precompile_macos_ios:
runs-on: macos-latest
env:
PRIVATE_KEY: ${{ secrets.PRECOMPILED_PRIVATE_KEY }}
GH_TOKEN: ${{ github.token }}
GITHUB_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: dart-lang/setup-dart@v1
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- name: Pub get
run: dart pub get
- name: Precompile (macOS + iOS)
run: |
set -euo pipefail
dart run bin/build_tool.dart precompile-binaries \
-v \
--os=macos \
--manifest-dir="${CRATE_DIR}" \
--crate-package="${CRATE_PACKAGE}" \
--repository="${GITHUB_REPOSITORY}"

precompile_android:
runs-on: ubuntu-latest
env:
PRIVATE_KEY: ${{ secrets.PRECOMPILED_PRIVATE_KEY }}
GH_TOKEN: ${{ github.token }}
GITHUB_TOKEN: ${{ github.token }}
ANDROID_NDK_VERSION: "26.3.11579264"
ANDROID_MIN_SDK: "23"
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: dart-lang/setup-dart@v1
- name: Set up Android SDK
uses: android-actions/setup-android@v3
- name: Install NDK
run: |
set -euo pipefail
sdkmanager --install "ndk;${ANDROID_NDK_VERSION}"
- name: Install cargo-ndk
run: cargo install cargo-ndk --locked
- name: Pub get
run: dart pub get
- name: Precompile (Android)
env:
ANDROID_SDK_ROOT: ${{ env.ANDROID_SDK_ROOT }}
run: |
set -euo pipefail
ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}"
dart run bin/build_tool.dart precompile-binaries \
-v \
--os=android \
--manifest-dir="${CRATE_DIR}" \
--crate-package="${CRATE_PACKAGE}" \
--repository="${GITHUB_REPOSITORY}" \
--android-sdk-location="${ANDROID_SDK_ROOT}" \
--android-ndk-version="${ANDROID_NDK_VERSION}" \
--android-min-sdk-version="${ANDROID_MIN_SDK}"

1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/target
native/target/
**/*.rs.bk
Cargo.lock

# Flutter/Dart
**/build/
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ If you have the Rust toolchain installed, the native library will be automatical
As a user of the package, you don't need to worry about building the native library or bindings yourself.
Only if you want to contribute to the bindings or modify the native code yourself, you can follow the instructions in [development](#development) below.

## Precompiled binaries

This plugin adds a precompiled-binary layer on top of the standard Native Assets approach.
When enabled, the build hook tries to download a signed precompiled binary for your target first.
If no matching binary is available or verification fails, it falls back to building from scratch via the Flutter/Dart build hook.
This gives consumers a choice between using published binaries or building locally.

### pubspec.yaml configuration

In your app's `pubspec.yaml`, add the `bdk_dart` section at the top level (next to `dependencies`), like:

```yaml
bdk_dart:
precompiled_binaries:
mode: auto # auto | always | never
```

`mode` controls when the precompiled path is used:
- `auto` prefers precompiled binaries when available, otherwise builds locally
- `always` requires precompiled binaries and skips local builds
- `never` always builds from source via the build hook

## Development

### Generating bindings
Expand All @@ -80,6 +102,10 @@ Dart test suite, which covers wallet creation, persistence, offline behavior, an
dart test
```

### Precompiled binaries (maintainers)

See `docs/precompiled_binaries.md` for CI details, manual release steps, and configuration.

## License

The Rust crate and generated bindings are dual-licensed under MIT or Apache 2.0 per the
Expand Down
6 changes: 6 additions & 0 deletions bin/build_tool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'package:bdk_dart/src/precompiled/cli/cli.dart';

Future<void> main(List<String> args) async {
await runCli(args);
}

83 changes: 83 additions & 0 deletions docs/precompiled_binaries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Precompiled binaries (maintainers)

This document describes how precompiled binaries are built, signed, and published for the plugin.

## Overview

- CI builds and uploads precompiled binaries via `.github/workflows/precompile_binaries.yml`.
- Artifacts are tagged by the crate hash and uploaded to a GitHub release.
- Each binary is signed with an Ed25519 key; the public key is embedded in `pubspec.yaml`.
- The build hook tries to download a verified binary first and falls back to a local build if needed.

## CI workflow

The workflow runs on `push` to `main` and on manual dispatch. It invokes:

```
dart run bin/build_tool.dart precompile-binaries ...
```

It currently builds macOS/iOS and Android targets.

## Release expectations

- The workflow creates/releases a GitHub release named `precompiled_<crateHash>` where `<crateHash>` comes from the verified crate sources and config.
- If the release already exists, the workflow uploads missing assets without rebuilding anything already present.
- If `gh release view precompiled_<crateHash>` fails locally, rerun `dart run bin/build_tool.dart precompile-binaries ...` with the same crate hash to recreate or update the release.

## How the download works

- The crate hash is computed from the Rust crate sources plus the plugin's `precompiled_binaries` config.
- The release tag is `precompiled_<crateHash>`.
- Assets are named `<targetTriple>_<libraryFileName>` with a matching `.sig` file.
- Each binary is paired with the `.sig` file that the hook uses to verify the download before applying it.
- The hook chooses the correct `lib$cratePackage` (or `lib$cratePackage.so`) artifact by matching the target triple and link mode from the Dart build config.
- On build, the hook downloads the signature and binary, verifies it, then places it in the build output.
- If any step fails (missing asset, bad signature), the hook builds locally via the standard build hook.

## Manual release (local)

Use this when debugging CI or producing artifacts manually.

Required environment variables:

- `PRIVATE_KEY` (Ed25519 private key, hex-encoded, 64 bytes)
- `GH_TOKEN` or `GITHUB_TOKEN` (GitHub token with release upload permissions)

Example:

```
dart run bin/build_tool.dart precompile-binaries \
--manifest-dir="native" \
--crate-package="bdk_dart_ffi" \
--repository="owner/repo" \
--os=macos
```

## Troubleshooting & ops tips

- If `gh release view precompiled_<crateHash>` shows a release without the expected `<targetTriple>_` assets, rerun the build locally to regenerate them.
- A stale crate hash (because sources or `precompiled_binaries` config changed) will point to a release that either doesn’t exist yet or lacks current binaries; re-run `dart run bin/build_tool.dart hash --manifest-dir=native` to confirm the hash and rebuild with the same inputs.
- Use `gh release view precompiled_<crateHash> --json assets --jq '.assets[].name'` to inspect what’s uploaded and verify `.sig` coverage.
- When debugging download failures, set `BDK_DART_PRECOMPILED_VERBOSE=1` to see why the hook skipped an asset.

## Configuration knobs

- `rust-toolchain.toml` controls the Rust channel and target list.
- `pubspec.yaml` under `bdk_dart.precompiled_binaries` must include:
- `artifact_host` (owner/repo)
- `public_key` (Ed25519 public key, hex-encoded, 32 bytes)

## Environment, keys, and secrets

- `PRIVATE_KEY`: 64-byte hex string (Ed25519 private key). This must be set locally or as a GitHub Actions secret before running `precompile-binaries`. Keep it out of source control.
- `PUBLIC_KEY`: Add the matching 32-byte hex public key to `pubspec.yaml` so consumers can verify downloads.
- `GH_TOKEN` / `GITHUB_TOKEN`: release upload permissions (already used in the CI workflow).
- `BDK_DART_PRECOMPILED_VERBOSE=1`: optional; shows download and verification details when debugging consumer builds.

Generate a keypair with `dart run bin/build_tool.dart gen-key` and copy the printed `PRIVATE_KEY`/`PUBLIC_KEY` values. Rotate the pair if you ever suspect the signing key was exposed, and update every release’s config accordingly.

## Security reminder

- Treat the `PRIVATE_KEY` used for signing as highly sensitive; do not commit it to version control and rotate it immediately if you suspect compromise.
- Update the public key in `pubspec.yaml` if the private key is rotated so consumers can still verify downloads.
21 changes: 18 additions & 3 deletions hook/build.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import 'package:bdk_dart/src/precompiled/precompiled_builder.dart';
import 'package:hooks/hooks.dart';
import 'package:native_toolchain_rust/native_toolchain_rust.dart';
import 'package:native_toolchain_rust/native_toolchain_rust.dart' as ntr;

void main(List<String> args) async {
await build(args, (input, output) async {
await const RustBuilder(
final builder = PrecompiledBuilder(
assetName: 'uniffi:bdk_dart_ffi',
).run(input: input, output: output);
buildModeName: ntr.BuildMode.release.name,
fallback: (input, output, assetRouting, logger) async {
final rustBuilder = ntr.RustBuilder(
assetName: 'uniffi:bdk_dart_ffi',
buildMode: ntr.BuildMode.release,
);
await rustBuilder.run(
input: input,
output: output,
assetRouting: assetRouting,
logger: logger,
);
},
);
await builder.run(input: input, output: output);
});
}
Loading