Scripts for verifying that the published Bull Bitcoin Mobile app matches a build from source.
Three components work together:
Two-file build setup driven by make android release:
Containerfile.toolsinstalls all toolchains (Rust pinned viaRUST_VERSION, Flutter via FVM, Android SDK, Gradle).Containerfile.appcopies the repo, runspub get/build_runner/gen-l10n, and configures Gradle. It does NOT runflutter build— that happens viapodman runagainst the resulting image so the multi-GB build output is never committed to a layer.
Two environment variables are set at build time to eliminate sources of non-determinism:
SOURCE_DATE_EPOCH— set to the timestamp of the latest git commit (git log -1 --format=%ct). OpenSSL embeds a wall-clock build timestamp in compiled binaries by default; setting this variable makes it use a fixed value instead, so any.sothat links against OpenSSL (libark_wallet.so,libboltz.so,libtor.so) is identical across builds.CARGO_ENCODED_RUSTFLAGS— three--remap-path-prefixflags that rewrite absolute paths baked into Rust binaries at compile time (home directory,.cargo,.rustup) to fixed strings (/cargo,/rustup,/build). cargokit readsCARGO_ENCODED_RUSTFLAGSrather thanRUSTFLAGS; flags are separated by the ASCII unit separator\x1f(octal\037).
A small verification tools image containing apktool, bundletool, and Java. It is used only for decoding APKs — it never builds the app. verify_build.sh builds this image automatically and runs apktool/bundletool inside it so no local Java installation is required.
Orchestrates the full verification:
- Builds the verification tools image from
Dockerfile - Optionally downloads the official APK from the GitHub release, or uses a locally provided APK or split APK directory
- Builds the app from the current repo checkout via
make android release(which uses the rootContainerfile.tools+Containerfile.app) - Picks up the extracted APK from the repo root (
./BULL-release.apk) - Decodes both APKs with apktool (inside the tools container)
- Diffs the decoded output excluding
META-INF(signatures are not part of reproducibility) - Writes a
RESULTS.mdverdict to the workspace directory
For build-to-build comparisons to be reproducible, both builds must use the exact same git commit. SOURCE_DATE_EPOCH is derived from git log -1 --format=%ct, so if two builds are from different commits they will embed different timestamps and the .so files will differ.
- Docker or Podman
- 8GB+ available RAM
- 50GB+ free disk space
curlandgitinstalled
cd reproducibility
# Verify against the GitHub release APK (downloads it automatically)
# Repo must be checked out at the matching tag (e.g. git checkout v10.9.8)
./verify_build.sh --version 10.9.8
# Verify a locally provided APK against a fresh build from the current checkout
./verify_build.sh --apk ./bullbitcoin.apk
# Same, with an explicit version (used in the workspace directory name)
./verify_build.sh --version 10.9.8 --apk ./bullbitcoin.apk
# Verify against split APKs extracted from a device (Play Store path)
./verify_build.sh --apk ~/bullbitcoin-splits/
# Clean up the workspace after verification
./verify_build.sh --apk ./bullbitcoin.apk --cleanupA workspace directory bullbitcoin_<version>_verification/ is created next to the script containing:
RESULTS.md— verdict, version info, hash, and commitofficial-decoded/— apktool decode of the reference APKbuilt-decoded/— apktool decode of the freshly built APKdiff.txt/diff_<split>.txt— differences found, if any (excluding META-INF)
adb shell pm path com.bullbitcoin.mobile
# outputs something like: package:/data/app/com.bullbitcoin.mobile-.../base.apk
adb pull /data/app/com.bullbitcoin.mobile-.../base.apk ~/bullbitcoin-splits/
adb pull /data/app/com.bullbitcoin.mobile-.../split_config.arm64_v8a.apk ~/bullbitcoin-splits/
# pull any other split_config.*.apk files listedThen pass --apk ~/bullbitcoin-splits/ to the script.