Scripts for verifying that the published Bull Bitcoin Mobile app matches a build from source.
Three components work together:
Builds the app from source in a clean, hermetic environment. It installs all toolchains (Rust, Flutter via FVM, Android SDK, Gradle), copies the repo into the image, and runs flutter build. 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 using the root
Dockerfile - Extracts the built artifact from the Docker image
- 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 Docker-to-Docker 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.