Skip to content

Internal precompile binaries hook#22

Open
BitcoinZavior wants to merge 42 commits intobitcoindevkit:mainfrom
LtbLightning:internal-precompile-binaries-hook
Open

Internal precompile binaries hook#22
BitcoinZavior wants to merge 42 commits intobitcoindevkit:mainfrom
LtbLightning:internal-precompile-binaries-hook

Conversation

@BitcoinZavior
Copy link

@BitcoinZavior BitcoinZavior commented Jan 18, 2026

Precompiled Binaries Feature

Resolves #20
This PR introduces a comprehensive precompiled binaries system that significantly improves the developer experience by reducing build times and providing secure, signed artifacts.

Key Features

Secure Distribution

  • Ed25519-signed precompiled binaries with public key verification
  • Automated signing and verification pipeline
  • Secure artifact hosting via GitHub Releases

Performance Improvements

  • Skip local Rust compilation for supported platforms
  • Automatic fallback to local builds when precompiled binaries unavailable
  • Configurable modes: auto, always, or never

Developer Experience

  • Zero-configuration setup for most users
  • Comprehensive CLI tooling for maintainers
  • Detailed documentation and troubleshooting guides

Configuration

Users can configure precompiled binaries in their pubspec.yaml:
Defaulting to auto which most users would want.

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

@reez
Copy link
Collaborator

reez commented Jan 22, 2026

This is looking nice, added a bunch of comments/questions, and if you can run dart format to fix the ci failure too.

I’ll set up the signing secret later, let me know if any other maintainer setup is needed.

README.md Outdated
```

`mode` controls when the precompiled path is used:
- `auto` prefers precompiled binaries when available, otherwise builds locally
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto is described as preferring precompiled, but the hook currently disables precompiled when rustup is present (UserOptions.defaultUsePrecompiledBinaries). Should we update this wording to reflect the rustup heuristic, or change the behavior to match the doc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest updating the wording in README.md. It now explicitly states that auto prefers local builds when the Rust toolchain (rustup) is detected; otherwise, it uses precompiled binaries.

- 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc says ‘download verified binary first and fall back’; should we note that mode: always is intended to disable fallback (once that behavior is enforced), or explicitly call out the rustup heuristic for auto?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a “Mode behavior” section. It clarifies that always currently falls back but is intended to fail if a verified binary isn’t present, and reiterates how never operates.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the Mode behavior section + rustup heuristic callout. One mismatch remains: docs say always falls back, but implementation now throws in always on any download/verify failure (no fallback). Can we update precompiled_binaries.md (always bullet + ‘If any step fails’ line) to reflect that always fails the build and only auto falls back?

exitCode = 2;
return;
}
if (repository == null || repository.trim().isEmpty) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--repository is required but never used in any gh release calls; should we pass --repo $repository to view/create/upload (or drop the flag if it’s not needed)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to call it out earlier: --repository was left unused in the release commands by mistake, but we’ve since wired it back in. This lets us rerun the tool against another repo (e.g., for testing or experimentation) without editing the workflow. In production it still defaults to the current repo, so the rest of the release flow behaves as before.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--repository is wired for upload + asset check, but _ensureReleaseExists still calls gh release view without --repo, and gh release create arg order is wrong (--title consumes --repo, so $repository is treated as a positional asset/pattern and the command fails). (lib/src/precompiled/cli/commands/precompile_binaries.dart:394-406)

Can we:

  • add --repo repository to the release view in _ensureReleaseExists
  • reorder create args to --repo repository --title "Precompiled binaries $crateHash" …?

'--package',
cratePackage,
'--release',
'--locked',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass --locked to cargo, but there’s no committed native/Cargo.lock in this repo; cargo build --locked will fail without it. Should we add native/Cargo.lock or remove --locked?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lock file added and .gitignore updated to allow lock file changes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don’t see a Cargo.lock committed in native/ (maybe I’m missing it), but precompile-binaries always runs cargo with --locked (both the cargo ndk … build and cargo build paths). On a clean runner, --locked will fail if there’s no lockfile to lock against.

Should we either:

  • commit native/Cargo.lock (preferred for reproducible precompiled artifacts + stable crate hash), --or--
  • drop --locked?

final output = AccumulatorSink<Digest>();
final input = sha256.startChunkedConversion(output);

void addTextFile(File file) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We concatenate lines from multiple files without a delimiter or filename marker which could (rarely) lead to hash collisions if file boundaries change. Should we consider prefixing each file with its path (or a separator) before hashing to make the hash more robust?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That scenario is extremely rare isn't it? we would have to arrange contents to collide via a prefix/suffix shift. I believe a lightweight fix ( a separator before its contents) should be sufficient.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree it’s rare, but right now we hash line contents with no delimiter / file boundary marker (and no newline between lines), so there are theoretical collisions if boundaries shift. Could we add a lightweight separator (e.g. prefix each file with its relative path + \n, and add \n between lines) before hashing?

Not blocking, but would make the crate hash strictly more robust.

I can also make a patch for this so you dont have to

@BitcoinZavior
Copy link
Author

Thanks for the review 🧡 PR updated to address review comments and to provide more info.

Comment on lines 400 to 403
'--title',
'--repo',
repository,
'Precompiled binaries $crateHash',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The args for the github release crate are misordered: --title is followed by --repo, so the title becomes --repo. The command will fail (or produce a release with the wrong metadata), aborting the whole precompile step. Title value should come immediately after --title, and --repo should be its own flag.

Suggested change
'--title',
'--repo',
repository,
'Precompiled binaries $crateHash',
'--title', 'Precompiled binaries $crateHash',
'--repo', repository,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

this is the same underlying issue as the earlier --repository thread


DownloadedArtifact? handleFailure(String message) {
if (requirePrecompiled) {
throw PrecompiledBinaryRequiredException(message);
Copy link
Contributor

@Johnosezele Johnosezele Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation wordings in precompiled_binaries.md says:
"always: Attempts to use precompiled binaries and falls back to local builds if download/verification fails. Future versions may disable fallback entirely for this mode."
This can be a bit misleading, because the code throws an exception when precompiled is required. Maybe we reword as:
"Throws an exception if download/verification fails: does not fall back"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@reez
Copy link
Collaborator

reez commented Feb 5, 2026

Thanks for the review 🧡 PR updated to address review comments and to provide more info.

great update! I resolved the threads that were addressed, and left a few open with follow-up questions.

also if there's any of the things I mentioned that you just want me to patch myself on this pr im happy to do that.

and then also CI is currently failing on the Format check step (dart format --output=none --set-exit-if-changed …). can you run dart format lib test example bdk_demo/lib bdk_demo/test and commit the result? (looks like lib/src/precompiled/artifacts_provider.dart and test/precompiled/options_test.dart still need formatting.)

@BitcoinZavior
Copy link
Author

Thanks @reez @Johnosezele
More commits added to address previous comments.
Will review and address new comments soon.
Thanks for your feedback, really appreciate it 🧡

@reez
Copy link
Collaborator

reez commented Feb 19, 2026

Thanks @reez @Johnosezele More commits added to address previous comments. Will review and address new comments soon. Thanks for your feedback, really appreciate it 🧡

thanks to you for the PR! will review again when you give me the go ahead that its ready for final review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support prebuilt binaries as default (with opt-in source builds)

3 participants