Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App from GooglePlay is not verifiable #758

Open
Giszmo opened this issue Dec 14, 2019 · 45 comments
Open

App from GooglePlay is not verifiable #758

Giszmo opened this issue Dec 14, 2019 · 45 comments
Labels
bug Something isn't working

Comments

@Giszmo
Copy link

Giszmo commented Dec 14, 2019

Please check my findings on the wallet's verifiability and let me know when I should give it another try. We are already in touch but I'm pinging all the wallets we failed to verify, so here's an issue for this.

@issue-label-bot issue-label-bot bot added the bug Something isn't working label Dec 14, 2019
@issue-label-bot
Copy link

Issue-Label Bot is automatically applying the label bug to this issue, with a confidence of 0.77. Please mark this comment with 👍 or 👎 to give our bot feedback!

Links: app homepage, dashboard and code for this bot.

@ncoelho
Copy link
Member

ncoelho commented Dec 14, 2019

This setup is tested on macOS and Linux, if you are using other setup, please help us figure out what is missing on it.

Open an Android Emulator or Connect an Android device.

The run:
$ npm i && npm start
$ react-native run-android

similar issue being described here #759

@Overtorment
Copy link
Member

Overtorment commented Dec 14, 2019 via email

@Giszmo
Copy link
Author

Giszmo commented Dec 16, 2019

If you build on CI, you should be there already. The more it can be automated, the better. Just strip the signing part or provide a fallback dummy key. Signature is obviously ignored for the verification.

I have a few other open source wallets that reacted to my shout-out and I'll review all asap again.

Yes, verification is important. Any non-verified release might be the end of the project. As a release manager myself I certainly want every tempted criminal to understand that putting me under distress won't get them all our customers' coins.

@bolatovumar
Copy link
Contributor

@Giszmo pls see this issue (#759) where I attempted to build the app for Android from scratch. I submitted a PR (#801) with updated instructions which was merged.

@Giszmo
Copy link
Author

Giszmo commented Jan 7, 2020

#801 using npx react-native run-android doesn't sound like building an APK. The run-android commands are for development with live code reload I think. Also for build automation, clicking in AS is not an option. The build process cannot depend on AS and if it does, the version of AS should matter, should it not?

@bolatovumar
Copy link
Contributor

@Giszmo you are right, this is not for building an APK, it's for local development only.

@Overtorment
Copy link
Member

Can you try

$ cd android
$ ./gradlew bundleRelease

for me it builds this file : ./app/build/outputs/bundle/release/app.aab but I have some local gradle config, I don't remember really how things work here, we have builds setup on CI since like a year ago.
anyway here's a guide https://facebook.github.io/react-native/docs/signed-apk-android.html it should definately help

@marcosrdz
Copy link
Member

I use assembleRelease instead of bundleRelease

@Giszmo
Copy link
Author

Giszmo commented Jan 8, 2020

I tried again to verify the build but

  • there is no tag 4.9.1
  • ./gradlew bundleRelease doesn't work
  • ./gradlew assembleRelease doesn't work

Please provide tested and working build instructions on your main Readme.md.


Relevant part of review:

$ docker run -it --volume $PWD:/mnt --workdir /mnt --rm beevelop/cordova bash
root@93d42b33d091:/mnt# npm install
root@93d42b33d091:/mnt# cd android/
root@9b73bbcbb500:/mnt/android# yes | /opt/android/tools/bin/sdkmanager "build-tools;28.0.3"
root@93d42b33d091:/mnt/android# ./gradlew clean assembleRelease

but this also didn't succeed:

* What went wrong:
Execution failed for task ':@remobile_react-native-qrcode-local-image:verifyReleaseResources'.
> 1 exception was raised by workers:
  com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource linking failed
  error: resource android:style/TextAppearance.Material.Widget.Button.Borderless.Colored not found.
  error: resource android:style/TextAppearance.Material.Widget.Button.Colored not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values-v26/values-v26.xml:7: error: resource android:attr/colorError not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values-v26/values-v26.xml:11: error: resource android:attr/colorError not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values-v26/values-v26.xml:15: error: style attribute 'android:attr/keyboardNavigationCluster' not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values-v28/values-v28.xml:7: error: resource android:attr/dialogCornerRadius not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values-v28/values-v28.xml:11: error: resource android:attr/dialogCornerRadius not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2734: error: resource android:attr/fontStyle not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2735: error: resource android:attr/font not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2736: error: resource android:attr/fontWeight not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2737: error: resource android:attr/fontVariationSettings not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2738: error: resource android:attr/ttcIndex not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2902: error: resource android:attr/startX not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2905: error: resource android:attr/startY not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2908: error: resource android:attr/endX not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2911: error: resource android:attr/endY not found.
  /mnt/node_modules/@remobile/react-native-qrcode-local-image/android/build/intermediates/res/merged/release/values/values.xml:2919: error: resource android:attr/offset not found.
  error: failed linking references.
  


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.4.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 4m 29s
120 actionable tasks: 97 executed, 23 up-to-date

Different but same for ./gradlew clean bundleRelease:

...
Error: Unable to resolve module `../../release-notes` from `/mnt/screen/settings/releasenotes.js`: The module `../../release-notes` could not be found from `/mnt/screen/settings/releasenotes.js`. Indeed, none of these files exist:
  * `/mnt/release-notes(.native||.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)`
  * `/mnt/release-notes/index(.native||.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)`
    at ModuleResolver.resolveDependency (/mnt/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:163:15)
    at ResolutionRequest.resolveDependency (/mnt/node_modules/metro/src/node-haste/DependencyGraph/ResolutionRequest.js:52:18)
    at DependencyGraph.resolveDependency (/mnt/node_modules/metro/src/node-haste/DependencyGraph.js:283:16)
    at Object.resolve (/mnt/node_modules/metro/src/lib/transformHelpers.js:264:42)
    at dependencies.map.result (/mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:399:31)
    at Array.map (<anonymous>)
    at resolveDependencies (/mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:396:18)
    at /mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:269:33
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:87:24)

> Task :app:bundleReleaseJsAndAssets FAILED

> Task :app:bundleReleaseJsAndAssets_SentryUpload FAILED
Processing react-native sourcemaps for Sentry upload.
error:> Analyzing 2 sources
 No such file or directory (os error 2)

Add --log-level=[info|debug] or export SENTRY_LOG_LEVEL=[info|debug] to see more output.
Please attach the full debug log to all bug reports.

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:bundleReleaseJsAndAssets'.
> Process 'command 'node'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:bundleReleaseJsAndAssets_SentryUpload'.
> Process 'command 'node_modules/@sentry/cli/bin/sentry-cli'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
==============================================================================

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.4.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 33s

@Overtorment
Copy link
Member

well fuck me. I'll have to get myself a clean system and setup builds from scratch writing down every step I make. stay tuned...

@Giszmo
Copy link
Author

Giszmo commented Mar 29, 2020

staying tuned ...

@Overtorment
Copy link
Member

thanks for your patience!
I recently automated builds on travisCI for end-to-end testing with detox.
that's one step closer to verifiable builds. I'll get back to this once I sort some other important tickets.

@Overtorment
Copy link
Member

ok, I adapted our travisCI scenario to build apk. works on clean ubuntu 18, tested it on cheap digitalocean vps. don't run as root. here's the script:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install unzip git npm -y
sudo npm i n -g
sudo n stable
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

export COMPILE_API=29
export ANDROID_BUILD_TOOLS=29.0.2
export ABI=x86_64
export ADB_INSTALL_TIMEOUT=8
export ANDROID_HOME=${HOME}/android-sdk
export ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip"
export EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator
export GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
export JDK="1.8"
export TOOLS=${ANDROID_HOME}/tools
export PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
export API=28
export TRAVIS_OS_NAME="linux"


# Set up JDK 8 for Android SDK
curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
export TARGET_JDK="${JDK}"
source ~/.install-jdk-travis.sh

# Set up Android SDK
wget -q "${ANDROID_TOOLS_URL}" -O android-sdk-tools.zip
unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
rm android-sdk-tools.zip
mkdir ~/.android # avoid harmless sdkmanager warning
echo 'count=0' > ~/.android/repositories.cfg # avoid harmless sdkmanager warning
yes | sdkmanager --licenses >/dev/null # accept all sdkmanager warnings
echo y | sdkmanager --no_https "platform-tools" >/dev/null
echo y | sdkmanager --no_https "tools" >/dev/null # A second time per Travis docs, gets latest versions
echo y | sdkmanager --no_https "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null # Implicit gradle dependency - gradle drives changes
echo y | sdkmanager --no_https "platforms;android-${COMPILE_API}" >/dev/null # We need the API of the current compileSdkVersion from gradle.properties




git clone https://github.com/BlueWallet/BlueWallet
cd BlueWallet
npm i
cd android
./gradlew assembleRelease

keytool -genkeypair -v -keystore detox.keystore -alias detox  -keyalg RSA -keysize 2048 -validity 10000 -storepass 123456 -keypass 123456 -dname  'cn=Unknown, ou=Unknown, o=Unknown, c=Unknown'
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore detox.keystore ./app/build/outputs/apk/release/app-release-unsigned.apk  detox -storepass 123456

it should build APK and sign it with random key.
Im sure there's easier way to do this. maybe someone will make a dockerfile out of it.

let me know how is it going

@Overtorment
Copy link
Member

if script works for you I will include it in repo under scripts/

@junderw
Copy link
Collaborator

junderw commented May 22, 2020

https://github.com/junderw/BlueWallet/tree/dockerBuildAndroid/scripts/docker

This works.

To change the version you need to do the following before running the docker run again:

  1. Modify startup.sh
  2. build image again (so the COPY step will pull in the new startup.sh)

If anyone wants to clean up that docker so it will work more deterministically (hard because that Gravis-CI stuff can be changed on a whim) cool.

I tested the apk. It works fine.

@Giszmo
Copy link
Author

Giszmo commented May 26, 2020

I gave it another try and while sort of following @Overtorment's code yielded a compile error, maybe because I deviated from the script, @junderw's docker did compile fine but the result differs a lot from the apk I got from Google Play.

My attempt at running the commands mostly in some (kind of random) docker container. Should have used ubuntu:18.04.

$ git clone https://github.com/BlueWallet/BlueWallet.git
$ cd BlueWallet/
$ git tag | grep 5.3.7
v5.3.7
leo@codex:/tmp/BlueWallet$ git checkout v5.3.7
$ docker run -it --volume $PWD:/mnt --workdir /mnt --rm beevelop/cordova bash
root@200b459d6058:/mnt# apt update -y && apt upgrade -y && apt install unzip git npm -y
root@200b459d6058:/mnt# npm i n -g
root@200b459d6058:/mnt# n stable
root@200b459d6058:/mnt# echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf && sysctl -p
root@200b459d6058:/mnt# export COMPILE_API=29
root@200b459d6058:/mnt# export ANDROID_BUILD_TOOLS=29.0.2
root@200b459d6058:/mnt# export ABI=x86_64
root@200b459d6058:/mnt# export ADB_INSTALL_TIMEOUT=8
root@200b459d6058:/mnt# export ANDROID_HOME=${HOME}/android-sdk
root@200b459d6058:/mnt# export ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip"
root@200b459d6058:/mnt# export EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator
root@200b459d6058:/mnt# export GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
root@200b459d6058:/mnt# export JDK="1.8"
root@200b459d6058:/mnt# export TOOLS=${ANDROID_HOME}/tools
root@200b459d6058:/mnt# export PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
root@200b459d6058:/mnt# export API=28
root@200b459d6058:/mnt# export TRAVIS_OS_NAME="linux"
root@200b459d6058:/mnt# curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
root@200b459d6058:/mnt# export TARGET_JDK="${JDK}"
root@200b459d6058:/mnt# source ~/.install-jdk-travis.sh
root@200b459d6058:/mnt# wget -q "${ANDROID_TOOLS_URL}" -O android-sdk-tools.zip
root@200b459d6058:/mnt# unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
root@200b459d6058:/mnt# rm android-sdk-tools.zip
root@200b459d6058:/mnt# mkdir ~/.android # avoid harmless sdkmanager warning
root@200b459d6058:/mnt# echo 'count=0' > ~/.android/repositories.cfg # avoid harmless sdkmanager warning
root@200b459d6058:/mnt# yes | sdkmanager --licenses >/dev/null # accept all sdkmanager warnings
root@200b459d6058:/mnt# echo y | sdkmanager --no_https "platform-tools" >/dev/null
root@200b459d6058:/mnt# echo y | sdkmanager --no_https "tools" >/dev/null # A second time per Travis docs, gets latest versions
root@200b459d6058:/mnt# echo y | sdkmanager --no_https "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null # Implicit gradle dependency - gradle drives changes
root@200b459d6058:/mnt# echo y | sdkmanager --no_https "platforms;android-${COMPILE_API}" >/dev/null # We need the API of the current compileSdkVersion from gradle.properties
root@200b459d6058:/mnt# npm i
root@200b459d6058:/mnt# cd android
root@200b459d6058:/mnt/android# ./gradlew assembleRelease

...

Error: Unable to resolve module `../../release-notes` from `screen/settings/releasenotes.js`: 

None of these files exist:
  * release-notes(.native|.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
  * release-notes/index(.native|.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
    at ModuleResolver.resolveDependency (/mnt/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:163:15)
    at ResolutionRequest.resolveDependency (/mnt/node_modules/metro/src/node-haste/DependencyGraph/ResolutionRequest.js:52:18)
    at DependencyGraph.resolveDependency (/mnt/node_modules/metro/src/node-haste/DependencyGraph.js:282:16)
    at Object.resolve (/mnt/node_modules/metro/src/lib/transformHelpers.js:267:42)
    at /mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:426:31
    at Array.map (<anonymous>)
    at resolveDependencies (/mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:423:18)
    at /mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:275:33
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/mnt/node_modules/metro/src/DeltaBundler/traverseDependencies.js:87:24)

> Task :app:bundleReleaseJsAndAssets FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:bundleReleaseJsAndAssets'.
> Process 'command 'npx'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 5m 51s
58 actionable tasks: 58 executed

Running the docker:

$ git clone https://github.com/junderw/BlueWallet
$ cd BlueWallet/
$ git checkout dockerBuildAndroid 
$ docker build scripts/docker/ -t bluewalletbuilder
$ cd scripts/docker/
$ docker run --name builder -v $(pwd)/output:/data bluewalletbuilder
$ diffoscope "/path/to/BlueWallet 5.3.7 (io.bluewallet.bluewallet).apk" output/app-release-unsigned.apk | wc -l
8523006

The Dockerfile works to compile the apk but it should require a revision/tag as command line parameter and not require the user to edit a script for that.


Edit: As the line with diffoscope might not be clear if you don't know the tool: apk tools necessarily are not bit by bit reproducible, as the verifier cannot reproduce the release signature, which is part of the apk. Therefore build verification requires to unpack the apk. This can be done with unzip or with apktool but the following result means the binary on google play is not reproducible:

$ unzip "/path/to/BlueWallet 5.3.7 (io.bluewallet.bluewallet).apk" -d zipGoogle
$ unzip output/app-release-unsigned.apk -d zipDocker
$ diff --recursive --brief zip*
Files zipDocker/AndroidManifest.xml and zipGoogle/AndroidManifest.xml differ
Files zipDocker/assets/index.android.bundle and zipGoogle/assets/index.android.bundle differ
Files zipDocker/classes2.dex and zipGoogle/classes2.dex differ
Files zipDocker/classes.dex and zipGoogle/classes.dex differ
Files zipDocker/lib/arm64-v8a/libimagepipeline.so and zipGoogle/lib/arm64-v8a/libimagepipeline.so differ
Files zipDocker/lib/arm64-v8a/libjsc.so and zipGoogle/lib/arm64-v8a/libjsc.so differ
Files zipDocker/lib/arm64-v8a/libnative-filters.so and zipGoogle/lib/arm64-v8a/libnative-filters.so differ
Files zipDocker/lib/arm64-v8a/libnative-imagetranscoder.so and zipGoogle/lib/arm64-v8a/libnative-imagetranscoder.so differ
Files zipDocker/lib/arm64-v8a/librsjni_androidx.so and zipGoogle/lib/arm64-v8a/librsjni_androidx.so differ
Files zipDocker/lib/armeabi-v7a/libimagepipeline.so and zipGoogle/lib/armeabi-v7a/libimagepipeline.so differ
Files zipDocker/lib/armeabi-v7a/libjsc.so and zipGoogle/lib/armeabi-v7a/libjsc.so differ
Files zipDocker/lib/armeabi-v7a/libnative-filters.so and zipGoogle/lib/armeabi-v7a/libnative-filters.so differ
Files zipDocker/lib/armeabi-v7a/libnative-imagetranscoder.so and zipGoogle/lib/armeabi-v7a/libnative-imagetranscoder.so differ
Files zipDocker/lib/armeabi-v7a/librsjni_androidx.so and zipGoogle/lib/armeabi-v7a/librsjni_androidx.so differ
Files zipDocker/lib/x86/libimagepipeline.so and zipGoogle/lib/x86/libimagepipeline.so differ
Files zipDocker/lib/x86/libjsc.so and zipGoogle/lib/x86/libjsc.so differ
Files zipDocker/lib/x86/libnative-filters.so and zipGoogle/lib/x86/libnative-filters.so differ
Files zipDocker/lib/x86/libnative-imagetranscoder.so and zipGoogle/lib/x86/libnative-imagetranscoder.so differ
Files zipDocker/lib/x86/librsjni_androidx.so and zipGoogle/lib/x86/librsjni_androidx.so differ
Files zipDocker/lib/x86_64/libimagepipeline.so and zipGoogle/lib/x86_64/libimagepipeline.so differ
Files zipDocker/lib/x86_64/libjsc.so and zipGoogle/lib/x86_64/libjsc.so differ
Files zipDocker/lib/x86_64/libnative-filters.so and zipGoogle/lib/x86_64/libnative-filters.so differ
Files zipDocker/lib/x86_64/libnative-imagetranscoder.so and zipGoogle/lib/x86_64/libnative-imagetranscoder.so differ
Files zipDocker/lib/x86_64/librsjni_androidx.so and zipGoogle/lib/x86_64/librsjni_androidx.so differ
Only in zipDocker/META-INF: DETOX.RSA
Only in zipDocker/META-INF: DETOX.SF
Only in zipGoogle/META-INF: GOOGPLAY.RSA
Only in zipGoogle/META-INF: GOOGPLAY.SF
Files zipDocker/META-INF/MANIFEST.MF and zipGoogle/META-INF/MANIFEST.MF differ
Files zipDocker/res/raw/node_modules_bigi_package.json and zipGoogle/res/raw/node_modules_bigi_package.json differ
Files zipDocker/res/raw/node_modules_elliptic_package.json and zipGoogle/res/raw/node_modules_elliptic_package.json differ

Edit 2: The file compiled with docker does run on my AVD and looks complete but the diff in the files is also very significant, looking at the file size:

$ ll "/path/to/BlueWallet 5.3.7 (io.bluewallet.bluewallet).apk" output/app-release-unsigned.apk
-rw-rw-r-- 1 leo leo 27M May 26 17:02 '/path/to/BlueWallet 5.3.7 (io.bluewallet.bluewallet).apk'
-rw-r--r-- 1 leo leo 29M May 26 16:58  output/app-release-unsigned.apk

@Overtorment
Copy link
Member

Error: Unable to resolve module ../../release-notes - something went wrong with npm i, this file is generated postinstall with list of changes since last tag.
dockerfile and my script are very much identical, so whichever works for you.

The Dockerfile works to compile the apk but it should require a revision/tag as command line parameter and not require the user to edit a script for that.

we can work on that later, when we move dockerfile to repo.

ok my guess is that by default npm i installs dev dependencies as well ,that's why diff is so big. from documentation:

By default, npm install will install all modules listed as dependencies in package. json . With the --production flag (or when the NODE_ENV environment variable is set to production ), npm will not install modules listed in devDependencies .

can you try rerunning but with --production in npm install command? Ill do some experiments on my end as well.

@junderw
Copy link
Collaborator

junderw commented May 27, 2020

might also want to do npm ci instead, since any patches for react dependencies would get pulled in if we had any ^ dependencies

@Overtorment
Copy link
Member

we do not have any ^ dependencies for production. some devDependencies do have ^

@Giszmo
Copy link
Author

Giszmo commented May 28, 2020

$ cat startup.sh | grep production
npm --production i
$ docker run --name builder --rm -v $(pwd)/output:/data bluewalletbuilder
...
BUILD SUCCESSFUL in 6m 18s
...
$ ll output/app-release-unsigned.apk /path/to/BlueWallet\ 5.3.7.apk 
-rw-rw-r-- 1 leo leo 27M May 26 17:02 '/path/to/BlueWallet 5.3.7.apk'
-rw-r--r-- 1 leo leo 29M May 28 00:43  output/app-release-unsigned.apk

@Overtorment
Copy link
Member

also, i noticed that our nodejs ver on CI is set to 10.x
anyway, will try to mess around builds some more

@Overtorment
Copy link
Member

got some progress.

bad news, is that the size difference is there. when you build from docker its ~29 Mb, and builds that we get from our CI/CD (appcenter.ms) is ~27 Mb.
good news is that I was able to isolate the difference to bundled libjsc.so binaries.

a bit in-depth... so, you can unzip apk and see its contents. i started examining apk from docker and apk from appcenter. the main executable is assets/index.android.bundle which is minified javascript. i did diff on them and the difference was only 3k - I peeked and it was some difference in paths (on the machine it was built on) and readme/release notes. so that's good news.
all other files in apk are the same, except several libjsc.so - per each target architecture (armeabi-v7a, x86_64 etc) there's its own libjsc.so and they are the ones that contribute to apk files difference, 1-2 megabytes per architecture.

quick googling showed that this is javascriptcore binaries that are used to actually bootstrap javascript https://github.com/react-native-community/jsc-android-buildscripts
Ill try to dig more into this, maybe will ask questions on StackOverflow or appcenter support. maybe Ill even dust off my mad reverse engineering skillz and disassemble those binaries with IDA to see whats going on inside and where the difference comes from.

@Overtorment
Copy link
Member

react-native-community/jsc-android-buildscripts#135

@Overtorment
Copy link
Member

Overtorment commented Jun 16, 2020

some progress, found out specific versions of build container on appcenter:

https://github.com/actions/virtual-environments/blob/master/images/macos/macos-10.15-Readme.md
https://docs.microsoft.com/en-us/appcenter/build/macos-10.15-software (same doc)

@junderw how hard it is you think to bake those specific versions in our docker? Im looking into Azure containers atm

@junderw
Copy link
Collaborator

junderw commented Jun 16, 2020

Very hard...

Not to mention it looks like appcenter is macOS, and iirc there are no macOS docker containers.....???

@Giszmo
Copy link
Author

Giszmo commented Jun 18, 2020

no macOS docker containers.....???

To my understanding of how Docker works, you can't have a macOS container as Docker is using the host's kernel which would not be the same on macOS. So if there was a macOS container, it would only run on macOS.

On the other hand I'm very confused right now. Docker is still magic to me and due to it not being as flexible as virtual machines and not designed to provide security, I am considering to switch away from docker to VMWare or whatever. Please let me know if that would be necessary or helpful in your case.

@Overtorment
Copy link
Member

@Giszmo would that be an option for you to use appcenter to setup your own CI for BW to make at least 1 build to compare and count that as verifiable?

otherwise, I have some thoughts on renting https://www.macincloud.com to install specified software versions and try to do a verifiable build there.

@Giszmo
Copy link
Author

Giszmo commented Jun 19, 2020

It's probably much work that would be obsolete with the next release.

If you build this on a Mac and it can only be reproduced on a Mac, I would prefer to get neutral, trusted reproducers to join the project and rebuild it on their infrastructure but ideally somebody would figure out how to do this with a VM. I never ran a MacOS VM but I assume that's possible. No idea if that would require a license or if there is some more accessible way of doing this.

@Overtorment
Copy link
Member

Overtorment commented Jun 19, 2020 via email

@emanuelb
Copy link

emanuelb commented May 3, 2021

Building latest version, tag v6.1.0 fail due to ssh dependency/usage, opened issue about it: #3059
Also build.gradle file use + instead of static versions or adding dependency locking (thus not reproducible), opened issue about it: #3050
Using MAC in VM on linux is possible with Docker-OSX project: https://github.com/sickcodes/Docker-OSX which is something to test after being able to compile the app in linux container as well.

@emanuelb
Copy link

emanuelb commented May 27, 2021

Both 2 issues in previous comment are fixed, I Tested v6.1.4 Release, except the difference in many .so files, there are others diffs as well, opened issues about them:

  1. in ./res/raw/releasenotes.json Release notes not generated when running from tag checkout #3145
  2. in AndroidManifest.xml VersionCode=1 in AndroidManifest from generated APK instead of 1620538430 from apk in github #3148 (looks like currently a workaround with sed is needed to change versionCode before app compilation, might be fixed in future, also using timestamp as versionCode is kinda incorrect... migration from AppCircle to Github Actions may help with that as well...)
  3. Diff in AndroidManifest.xml and additional stamp-cert-sha256 file probably due to google-play+ci signing usage Why google-play app-signing is used? diff in AndroidManifest.xml & additional stamp-cert-sha256 file #3219
  4. in ./assets/index.android.bundle & ./res/raw/node_modules_bigi_package.json & ./res/raw/node_modules_csstree_package.json & ./res/raw/node_modules_elliptic_package.json & ./res/raw/node_modules_realm_package.json Due to Filepath & node version leakage, Filepath+node version variation & package info result in non reproducible build and are leaked into the APK #3239
  5. while I didn't look at the difference in .so file, declaring a ndkVersion needed for consistent usage of same NDK version, opened issue for it Add ndkVersion with static value to build.gradle for newer+reproducible NDK usage #3218
  6. another related issue to use newer package-lock format with better support for reproducible-builds Use newer lockfileVersion > 1 (newer Lockfile format for package-lock.json) #3080

Regarding the diffs in .so files, running diffoscope is needed to look at the diff & also compiling from MACOSX which I might look into it in the future when most of the above issues are fixed...

Containerfile used to build the apk is:

build: podman build --pull --rm -t bluewallet_lv_build_apk -f ContainerFile
APK file generated in: /home/appuser/app/bluewallet/BlueWallet/android/app/build/outputs/apk/release/app-release-unsigned.apk

FROM frolvlad/alpine-glibc

RUN set -ex; \
    apk update; \
    apk add --no-cache \
        openjdk8 \
        git \
        npm; \
    adduser -D appuser;

USER appuser

ENV ANDROID_SDK_ROOT="/home/appuser/app/sdk" \
    ANDROID_HOME="/home/appuser/app/sdk" \
    NODE_ENV="production"

RUN set -ex; \
    mkdir -p "/home/appuser/app/sdk/licenses" "/home/appuser/app/bluewallet/"; \
    printf "\n24333f8a63b6825ea9c5514f83c2829b004d1fee" > "/home/appuser/app/sdk/licenses/android-sdk-license"; \
    cd /home/appuser/app/bluewallet/; \
    git clone https://github.com/BlueWallet/BlueWallet; \
    cd BlueWallet; \
    git checkout v6.1.4;
    
WORKDIR /home/appuser/app/bluewallet/BlueWallet/

RUN set -ex; \
    npm ci --no-optional --no-audit --no-fund --ignore-scripts; \
    npm run postinstall; \
    cd android; \
    ./gradlew assembleRelease

@eriknylund
Copy link

eriknylund commented Sep 20, 2021

ok, I adapted our travisCI scenario to build apk. works on clean ubuntu 18, tested it on cheap digitalocean vps. don't run as root. here's the script:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install unzip git npm -y
sudo npm i n -g
sudo n stable
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

export COMPILE_API=29
export ANDROID_BUILD_TOOLS=29.0.2
export ABI=x86_64
export ADB_INSTALL_TIMEOUT=8
export ANDROID_HOME=${HOME}/android-sdk
export ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip"
export EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator
export GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
export JDK="1.8"
export TOOLS=${ANDROID_HOME}/tools
export PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
export API=28
export TRAVIS_OS_NAME="linux"


# Set up JDK 8 for Android SDK
curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
export TARGET_JDK="${JDK}"
source ~/.install-jdk-travis.sh

# Set up Android SDK
wget -q "${ANDROID_TOOLS_URL}" -O android-sdk-tools.zip
unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
rm android-sdk-tools.zip
mkdir ~/.android # avoid harmless sdkmanager warning
echo 'count=0' > ~/.android/repositories.cfg # avoid harmless sdkmanager warning
yes | sdkmanager --licenses >/dev/null # accept all sdkmanager warnings
echo y | sdkmanager --no_https "platform-tools" >/dev/null
echo y | sdkmanager --no_https "tools" >/dev/null # A second time per Travis docs, gets latest versions
echo y | sdkmanager --no_https "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null # Implicit gradle dependency - gradle drives changes
echo y | sdkmanager --no_https "platforms;android-${COMPILE_API}" >/dev/null # We need the API of the current compileSdkVersion from gradle.properties




git clone https://github.com/BlueWallet/BlueWallet
cd BlueWallet
npm i
cd android
./gradlew assembleRelease

keytool -genkeypair -v -keystore detox.keystore -alias detox  -keyalg RSA -keysize 2048 -validity 10000 -storepass 123456 -keypass 123456 -dname  'cn=Unknown, ou=Unknown, o=Unknown, c=Unknown'
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore detox.keystore ./app/build/outputs/apk/release/app-release-unsigned.apk  detox -storepass 123456

it should build APK and sign it with random key.
Im sure there's easier way to do this. maybe someone will make a dockerfile out of it.

let me know how is it going

I tried these steps on a Digital Ocean Ubuntu 18.04 (LTS) x64 to reproduce the build for v6.2.7. The setup and build works fine, I end up with the following diff:

circleci@ubuntu-s-4vcpu-8gb-intel-fra1-01:~$ diff -qr signed/ self-signed/
Files signed/AndroidManifest.xml and self-signed/AndroidManifest.xml differ
Only in self-signed/META-INF: DETOX.RSA
Only in self-signed/META-INF: DETOX.SF
Files signed/META-INF/MANIFEST.MF and self-signed/META-INF/MANIFEST.MF differ
Only in signed/META-INF: MBLUEWAL.RSA
Only in signed/META-INF: MBLUEWAL.SF
Files signed/assets/index.android.bundle and self-signed/assets/index.android.bundle differ
Files signed/classes3.dex and self-signed/classes3.dex differ
Files signed/lib/x86/librealm.so and self-signed/lib/x86/librealm.so differ
Files signed/lib/x86_64/librealm.so and self-signed/lib/x86_64/librealm.so differ
Files signed/res/G2.json and self-signed/res/G2.json differ
Files signed/res/GH.json and self-signed/res/GH.json differ
Files signed/res/Iw.json and self-signed/res/Iw.json differ
Files signed/res/KA.json and self-signed/res/KA.json differ
Files signed/res/V4.json and self-signed/res/V4.json differ
Files signed/res/f2.json and self-signed/res/f2.json differ
Files signed/res/zL.json and self-signed/res/zL.json differ

As noted in #3239, the .json files differ on user name.

circleci@ubuntu-s-4vcpu-8gb-intel-fra1-01:~$ diff signed/res/G2.json self-signed/res/G2.json
5c5
<       "/Users/runner/work/1/s"
---
>       "/home/circleci/BlueWallet"
30c30
<   "_where": "/Users/runner/work/1/s",
---
>   "_where": "/home/circleci/BlueWallet",

@eriknylund
Copy link

A few quirks later:

  • Emulating the user directory /Users/runner/work/1/s
  • Setting the versionCode to match the timestamp inserted by CI I am down to the following smaller diffs (1631880995 in the case of v6.2.7)
  • Using master branch and git reset --hard 06b962761ba47904217a6b951ffda5ef936ea598 avoids diff of master vs tag: in GH.json but causes diff in gT.json instead. Seems the scripts/release-notes.sh and scripts/current-branch are intended to be run on the master branch at the time of release and will not work properly later on. So either of these have to be fixed by hand, the easier way is setting the content of current-branch.json to "master" after running npm run postinstall.

I end up with this diff

# diff -qr signed/ unsigned/
Files signed/AndroidManifest.xml and unsigned/AndroidManifest.xml differ
Only in signed/META-INF: MANIFEST.MF
Only in signed/META-INF: MBLUEWAL.RSA
Only in signed/META-INF: MBLUEWAL.SF
Files signed/assets/index.android.bundle and unsigned/assets/index.android.bundle differ
Files signed/lib/x86/librealm.so and unsigned/lib/x86/librealm.so differ
Files signed/lib/x86_64/librealm.so and unsigned/lib/x86_64/librealm.so differ
Files signed/res/gT.json and unsigned/res/gT.json differ

Further inspection with diff/diffoscope

# diff signed/res/gT.json unsigned/res/gT.json
1c1
< "* Update BlueComponents.js \n* FIX: DrawerRoot would shrink on Desktop \n* FIX: Add timeouts for modal navigation \n* Translate /loc/en.json in de_DE \n* TST: improve e2e \n* OPS: Version bump & release notes \n* Update dependency react-native-screens to v3.7.2 \n* Update dependency react-native-device-info to v8.3.3 \n* TST: remove travis e2e \n* Translate /loc/en.json in es_419 \n"
---
> ""

# diffoscope signed/assets/index.android.bundle unsigned/assets/index.android.bundle
-__d(function(e,n,o,t,a,s,r){a.exports="* Update BlueComponents.js \n* FIX: DrawerRoot would shrink on Desktop \n* FIX: Add timeouts for modal navigation \n* Translate /loc/en.json in de_DE \n* TST: improve e2e \n* OPS: Version bump & release notes \n* Update dependency react-native-screens to v3.7.2 \n* Update dependency react-native-device-info to v8.3.3 \n* TST: remove travis e2e \n* Translate /loc/en.json in es_419 \n"},1565,[]);
+__d(function(n,o,t,_,c,d,e){c.exports=""},1565,[]);
# diffoscope signed/lib/x86/librealm.so unsigned/lib/x86/librealm.so
 |##################################################################################################|  100%                             Time: 0:00:43 
--- signed/lib/x86/librealm.so
+++ unsigned/lib/x86/librealm.so
├── readelf --wide --file-header {}
│ @@ -13,8 +13,8 @@
│    Start of section headers:          9263184 (bytes into file)
│    Flags:                             0x0
│    Size of this header:               52 (bytes)
│    Size of program headers:           32 (bytes)
│    Number of program headers:         8
│    Size of section headers:           40 (bytes)
│    Number of section headers:         27
│ -  Section header string table index: 25
│ +  Section header string table index: 26
├── readelf --wide --sections {}
│ @@ -23,14 +23,14 @@
│    [18] .data.rel.ro      PROGBITS        008957b0 8947b0 02fbcc 00  WA  0   0  4
│    [19] .fini_array       FINI_ARRAY      008c537c 8c437c 000008 00  WA  0   0  4
│    [20] .init_array       INIT_ARRAY      008c5384 8c4384 000248 00  WA  0   0  4
│    [21] .dynamic          DYNAMIC         008c55cc 8c45cc 000138 08  WA  4   0  4
│    [22] .got              PROGBITS        008c5704 8c4704 0009ec 00  WA  0   0  4
│    [23] .got.plt          PROGBITS        008c60f0 8c50f0 003f10 00  WA  0   0  4
│    [24] .data             PROGBITS        008ca000 8c9000 00c748 00  WA  0   0 16
│ -  [25] .shstrtab         STRTAB          00000000 8d5748 000108 00      0   0  1
│ -  [26] .bss              NOBITS          008d6780 8d5780 005e38 00  WA  0   0 64
│ +  [25] .bss              NOBITS          008d6780 8d5780 005e38 00  WA  0   0 64
│ +  [26] .shstrtab         STRTAB          00000000 8d5748 000108 00      0   0  1
│  Key to Flags:
│    W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
│    L (link order), O (extra OS processing required), G (group), T (TLS),
│    C (compressed), x (unknown), o (OS specific), E (exclude),
│    p (processor specific)

Lines appear in different order.

# diffoscope signed/lib/x86_64/librealm.so unsigned/lib/x86_64/librealm.so
 |##################################################################################################|  100%                             Time: 0:00:35 
--- signed/lib/x86_64/librealm.so
+++ unsigned/lib/x86_64/librealm.so
├── readelf --wide --file-header {}
│ @@ -13,8 +13,8 @@
│    Start of section headers:          9467072 (bytes into file)
│    Flags:                             0x0
│    Size of this header:               64 (bytes)
│    Size of program headers:           56 (bytes)
│    Number of program headers:         8
│    Size of section headers:           64 (bytes)
│    Number of section headers:         27
│ -  Section header string table index: 25
│ +  Section header string table index: 26
├── readelf --wide --sections {}
│ @@ -23,14 +23,14 @@
│    [18] .data.rel.ro      PROGBITS        0000000000895df0 894df0 05a7f0 00  WA  0   0 16
│    [19] .fini_array       FINI_ARRAY      00000000008f05e0 8ef5e0 000010 00  WA  0   0  8
│    [20] .init_array       INIT_ARRAY      00000000008f05f0 8ef5f0 000490 00  WA  0   0  8
│    [21] .dynamic          DYNAMIC         00000000008f0a80 8efa80 000270 10  WA  4   0  8
│    [22] .got              PROGBITS        00000000008f0cf0 8efcf0 0013a8 00  WA  0   0  8
│    [23] .got.plt          PROGBITS        00000000008f2098 8f1098 007f60 00  WA  0   0  8
│    [24] .data             PROGBITS        00000000008fa000 8f9000 00e3b0 00  WA  0   0 16
│ -  [25] .shstrtab         STRTAB          0000000000000000 9073b0 00010a 00      0   0  1
│ -  [26] .bss              NOBITS          00000000009083c0 9073c0 0092f0 00  WA  0   0 64
│ +  [25] .bss              NOBITS          00000000009083c0 9073c0 0092f0 00  WA  0   0 64
│ +  [26] .shstrtab         STRTAB          0000000000000000 9073b0 00010a 00      0   0  1
│  Key to Flags:
│    W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
│    L (link order), O (extra OS processing required), G (group), T (TLS),
│    C (compressed), x (unknown), o (OS specific), E (exclude),
│    l (large), p (processor specific)
├── objdump --line-numbers --disassemble --demangle --section=.text {}
│ @@ -372045,15 +372045,15 @@
│    2b4416:	e8 75 3c ea ff       	callq  158090 <__stack_chk_fail@plt>
│    2b441b:	48 89 c3             	mov    %rax,%rbx
│    2b441e:	48 8d 7c 24 08       	lea    0x8(%rsp),%rdi
│    2b4423:	e8 68 3e ea ff       	callq  158290 <std::__ndk1::locale::~locale()@plt>
│    2b4428:	48 89 df             	mov    %rbx,%rdi
│    2b442b:	e8 f9 7c 33 00       	callq  5ec129 <std::set_terminate(void (*)())@@Base+0x3299>
│    2b4430:	50                   	push   %rax
│ -  2b4431:	48 8b 35 20 ca 63 00 	mov    0x63ca20(%rip),%rsi        
│ +  2b4431:	48 8b 35 20 ca 63 00 	mov    0x63ca20(%rip),%rsi        # 8f0e58 <std::__ndk1::ctype<char>::id@@Base-0x1f290>
│    2b4438:	e8 23 48 ea ff       	callq  158c60 <std::__ndk1::locale::use_facet(std::__ndk1::locale::id&) const@plt>
│    2b443d:	59                   	pop    %rcx
│    2b443e:	c3                   	retq   
│    2b443f:	90                   	nop
│    2b4440:	0b 77 20             	or     0x20(%rdi),%esi
│    2b4443:	e9 28 48 ea ff       	jmpq   158c70 <std::__ndk1::ios_base::clear(unsigned int)@plt>
│    2b4448:	41 57                	push   %r15
│ @@ -379512,15 +379512,15 @@
│    2ba8f2:	48 8b 40 e8          	mov    -0x18(%rax),%rax
│    2ba8f6:	48 89 0c 07          	mov    %rcx,(%rdi,%rax,1)
│    2ba8fa:	48 8b 07             	mov    (%rdi),%rax
│    2ba8fd:	48 03 78 e8          	add    -0x18(%rax),%rdi
│    2ba901:	48 89 d6             	mov    %rdx,%rsi
│    2ba904:	e9 a5 da fe ff       	jmpq   2a83ae <Java_io_realm_react_RealmReactModule_clearContextInjectedFlag@@Base+0x6ad2e>
│    2ba909:	50                   	push   %rax
│ -  2ba90a:	48 8b 35 67 65 63 00 	mov    0x636567(%rip),%rsi        
│ +  2ba90a:	48 8b 35 67 65 63 00 	mov    0x636567(%rip),%rsi        # 8f0e78 <std::__ndk1::num_put<char, std::__ndk1::ostreambuf_iterator<char, std::__ndk1::char_traits<char> > >::id@@Base-0x1f118>

...

Lines appear in different order and there's a comment at the end of line.

@eriknylund
Copy link

eriknylund commented Sep 25, 2021

Here's the script I'm using:

sudo apt-get update -y && sudo apt-get upgrade -y
sudo apt-get install -y unzip git npm
sudo npm i n -g
sudo n stable

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

export COMPILE_API=29
export ANDROID_BUILD_TOOLS=29.0.2
export ABI=x86_64
export ADB_INSTALL_TIMEOUT=8
export ANDROID_HOME=${HOME}/android-sdk
export ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip"
export EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator
export GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
export JDK="1.8"
export TOOLS=${ANDROID_HOME}/tools
export PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
export API=28
export TRAVIS_OS_NAME="linux"

# Set up JDK 8 for Android SDK
curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
export TARGET_JDK="${JDK}"
source ~/.install-jdk-travis.sh

# Set up Android SDK
wget -q "${ANDROID_TOOLS_URL}" -O android-sdk-tools.zip
unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
rm android-sdk-tools.zip
mkdir ~/.android # avoid harmless sdkmanager warning
echo 'count=0' > ~/.android/repositories.cfg # avoid harmless sdkmanager warning
yes | sdkmanager --licenses >/dev/null # accept all sdkmanager warnings
echo y | sdkmanager --no_https "platform-tools" >/dev/null
echo y | sdkmanager --no_https "tools" >/dev/null # A second time per Travis docs, gets latest versions
echo y | sdkmanager --no_https "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null # Implicit gradle dependency - gradle drives changes
echo y | sdkmanager --no_https "platforms;android-${COMPILE_API}" >/dev/null # We need the API of the current compileSdkVersion from gradle.properties

# Emulate appcenter.ms work directory
mkdir -p /Users/runner/work/1
git clone -b v6.2.7 https://github.com/BlueWallet/BlueWallet /Users/runner/work/1/s

# Install apktool
wget https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool
wget -O apktool.jar https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.6.0.jar
chmod +x apktool*
mv apktool* /usr/local/bin

# Use apktool to find the version code (a timestamp set by appcenter.ms during build)
pushd /Users/runner/work/1
wget https://github.com/BlueWallet/BlueWallet/releases/download/v6.2.7/BlueWallet-6.2.7.apk
apktool d -o reversed BlueWallet-6.2.7.apk
versionCode=$( cat reversed/apktool.yml | grep versionCode | sed 's/.*: //g' | sed "s/'//g" )
echo $versionCode

# Build v6.2.7
pushd s
npm i
# npm doesn't always run the scripts in postinstall
npm run postinstall
cat release-notes.json 
cat current-branch.json
# Force branch to master
echo '"master"' > current-branch.json
# Force version code to match released APK
sed -i "s/versionCode 1/versionCode $versionCode/g" android/app/build.gradle

pushd android
./gradlew assembleRelease 
popd
popd

unzip -d signed BlueWallet-6.2.7.apk
unzip -d unsigned s/android/app/build/outputs/apk/release/app-release-unsigned.apk 

diff -qr signed unsigned

With this I am down to the following diff:

# diff -qr signed unsigned
Files signed/AndroidManifest.xml and unsigned/AndroidManifest.xml differ
Only in signed/META-INF: MANIFEST.MF
Only in signed/META-INF: MBLUEWAL.RSA
Only in signed/META-INF: MBLUEWAL.SF
Files signed/lib/x86/librealm.so and unsigned/lib/x86/librealm.so differ
Files signed/lib/x86_64/librealm.so and unsigned/lib/x86_64/librealm.so differ

If there's a way to resolve the small diff in x86/librealm.so and x86_64/librealm.so I'm happy to learn how.

@emanuelb
Copy link

emanuelb commented Oct 7, 2021

Tagging @bmwiedemann maybe you know how to resolve & why happening the diff in ordering & additonal comment inside librealm.so file, details in above comment: #758 (comment)

@bmwiedemann
Copy link

My usual approach in such cases is to find out how the files are produced.
Either I check log files for relevant commands or I use
strace and then I use autoprovenance (is generic and should work in other Linuxes) on the output to find the commands.

Chances are, that .o files are linked in random order returned by a readdir syscall. Some filesystems have reproducible readdir order, so issues will not surface there. Sometimes, there are also ordering issues from hashes or ASLR.

@emanuelb
Copy link

emanuelb commented Apr 21, 2023

Latest version 6.3.2 on Github is kinda reproducible with Containerfile below (Requires to find workaround for ordering issue in Binary AndroidManifest.xml, opened issue also in reproducible-apk-tools repo as they might add generic solution to order this file obfusk/reproducible-apk-tools#15 also related to additional tag in Google Play version at #3219 )

Compile with:

podman build --pull --ulimit nofile=8192:8192 --rm -t bluewallet_build_apk_632 -f Containerfile632

APK generated in:

/Users/runner/work/1/s/android/app/build/outputs/apk/release/app-release-unsigned.apk

Containerfile632 content:

FROM docker.io/node:16.18.1-bullseye

RUN set -ex; \
    apt-get update; \
    DEBIAN_FRONTEND=noninteractive apt-get install --yes -o APT::Install-Suggests=false --no-install-recommends openjdk-11-jre-headless; \
    rm -rf /var/lib/apt/lists/*; \
    useradd -ms /bin/bash appuser; \
    mkdir -p /Users/runner/work/1/; \
    chown -R appuser:appuser /Users/;

USER appuser

ENV ANDROID_SDK_ROOT="/home/appuser/sdk" \
    ANDROID_HOME="/home/appuser/sdk" \
    NODE_ENV="production"

RUN set -ex; \
    mkdir -p "/home/appuser/sdk/licenses"; \
    printf "\n24333f8a63b6825ea9c5514f83c2829b004d1fee" > "/home/appuser/sdk/licenses/android-sdk-license"; \
    cd /Users/runner/work/1/; \
    git clone --branch v6.3.2 https://github.com/BlueWallet/BlueWallet /Users/runner/work/1/s/;
    
WORKDIR /Users/runner/work/1/s/

RUN set -ex; \
    npm install --production --no-optional --omit=optional --no-audit --no-fund --ignore-scripts; \
    npm run postinstall; \
    sed -i "s/versionCode 1/versionCode 1668358190/g" android/app/build.gradle; \
    sed -i '/^\s*<\/application>\s*/i <meta-data android:name="com.bugsnag.android.BUILD_UUID" android:value="8699182a-9b70-4ef4-ab1c-d7a0d2020499"\/>' android/app/src/main/AndroidManifest.xml; \
    echo '"master"' > current-branch.json;

RUN set -ex; \    
    cd /Users/runner/work/1/s/android; \
    ./gradlew assembleRelease

The diff between it and version on github in https://github.com/BlueWallet/BlueWallet/releases/download/v6.3.2/BlueWallet-6.3.2.apk is:

sha256sum of apk from github: 939275561273a0901ce80bb34c37457b8a1a6a1bb11f8267837e7b8db1d0fbf5

Files ./build/AndroidManifest.xml and ./BlueWallet-6.3.2/AndroidManifest.xml differ
Only in ./BlueWallet-6.3.2/META-INF: MANIFEST.MF
Only in ./BlueWallet-6.3.2/META-INF: MBLUEWAL.RSA
Only in ./BlueWallet-6.3.2/META-INF: MBLUEWAL.SF

In the decoded AndroidManifest.xml the diff is:

96d95
<     <meta-data android:name="com.bugsnag.android.BUILD_UUID" android:value="8699182a-9b70-4ef4-ab1c-d7a0d2020499"/>
141a141
>     <meta-data android:name="com.bugsnag.android.BUILD_UUID" android:value="8699182a-9b70-4ef4-ab1c-d7a0d2020499"/>

The sed command was used to add this entry during compilation (as without it the value generated will be different then the one in github APK), but it ends up in different location in the decoded AndroidManifest (not before the </application> tag as it's supposed to be) which makes the file differ.

Opened issue #5474 to suggest using a "static" value that changes in tags/branch for the BUILD_UUID value as if it will be static in the AndroidManifest.xml file in repo there won't be any ordering issue.

What left is to check version downloaded from Google Play directly which may require more changes to build script (as just comparing the github APK to APK downloaded from https://apk.support and https://apkcombo.com/ websites will show diffs in some files)

@eriknylund
Copy link

Thanks @emanuelb! This is a really good improvement. I think as long as the diff is easy to verify and it is in the manifest then it's acceptable. Look forward to also being able to verify the Google Play APK and future improvements on this.

@nobody77777
Copy link

@emanuelb First, thank you! Second, I successfully built 6.3.2 following your instructions above but was not able to build 6.4.4. I basically got stuck at "info Done copying assets". Do we need to modify the content of Containerfilexxx to make it work with future versions? Here are the logs below:

w: file:///Users/runner/work/1/s/node_modules/rn-ldk/android/src/main/java/com/rnldk/RnLdkModule.kt:741:15 'reject(String!): Unit' is deprecated. Deprecated in Java
w: file:///Users/runner/work/1/s/node_modules/rn-ldk/android/src/main/java/com/rnldk/RnLdkModule.kt:817:46 'reject(String!): Unit' is deprecated. Deprecated in Java
w: file:///Users/runner/work/1/s/node_modules/rn-ldk/android/src/main/java/com/rnldk/RnLdkModule.kt:818:48 'reject(String!): Unit' is deprecated. Deprecated in Java
w: file:///Users/runner/work/1/s/node_modules/rn-ldk/android/src/main/java/com/rnldk/RnLdkModule.kt:819:46 'reject(String!): Unit' is deprecated. Deprecated in Java
w: file:///Users/runner/work/1/s/node_modules/rn-ldk/android/src/main/java/com/rnldk/RnLdkModule.kt:923:20 'toUpperCase(): String' is deprecated. Use uppercase() instead.
w: file:///Users/runner/work/1/s/node_modules/rn-ldk/android/src/main/java/com/rnldk/RnLdkModule.kt:942:85 'toLowerCase(): String' is deprecated. Use lowercase() instead.

> Task :app:checkReleaseAarMetadata
> Task :rn-ldk:extractReleaseAnnotations
> Task :rn-ldk:compileReleaseJavaWithJavac
> Task :rn-ldk:mergeReleaseGeneratedProguardFiles
> Task :rn-ldk:mergeReleaseConsumerProguardFiles
> Task :rn-ldk:mergeReleaseJavaResource
> Task :app:generateReleaseResValues
> Task :rn-ldk:syncReleaseLibJars
> Task :rn-ldk:bundleReleaseLocalLintAar
> Task :app:processReleaseGoogleServices
> Task :app:mapReleaseSourceSetPaths

> Task :app:createBundleReleaseJsAndAssets
warning: the transform cache was reset.
                Welcome to Metro v0.73.9
              Fast - Scalable - Integrated


warn Package react-native-fingerprint-scanner contains invalid configuration: "dependency.assets" is not allowed,"dependency.hooks" is not allowed. Please verify it's properly linked using "react-native config" command and contact the package maintainers about this.
info Writing bundle output to:, /Users/runner/work/1/s/android/app/build/generated/assets/createBundleReleaseJsAndAssets/index.android.bundle
info Writing sourcemap output to:, /Users/runner/work/1/s/android/app/build/intermediates/sourcemaps/react/release/index.android.bundle.packager.map
info Done writing bundle output
info Done writing sourcemap output
info Copying 64 asset files
info Done copying assets

@emanuelb
Copy link

@ nobody77777 Yes, some modifications are needed for every version change (like using different node version) , for version 6.4.4 I posted a Containerfile at WalletScrutiny project in comment for MR for BlueWallet at:
https://gitlab.com/walletscrutiny/walletScrutinyCom/-/merge_requests/441#note_1398702669

also the stuck is not unusual and I experienced it myself in latest versions, had to run the script / last operation ( ./gradlew assembleRelease command) multiple times until it worked

@nobody77777
Copy link

@emanuelb Ok I understand it better now. Thank you!

@Giszmo
Copy link
Author

Giszmo commented Dec 31, 2023

I tried to reproduce 6.4.13 from Play Store and got a big diff.

@Giszmo
Copy link
Author

Giszmo commented Jan 31, 2024

Again, I tried to reproduce v6.5.0 and the diff was huge.

===== Begin Results =====
appId:          io.bluewallet.bluewallet
signer:         42250147991337ed230fbd93c0be0e5f6183d02eed9e1d53e5aac94167cf3f2f
apkVersionName: 6.5.0
apkVersionCode: 1706539467
verdict:        
appHash:        fc37c018b74e6ded8922e1c5d3724ffe3b9a4ac74341868bd34e396145f83e5a
commit:         f73307ac61048a1d5aa69edd5039685b1a828a13

Diff:
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/AndroidManifest.xml and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/AndroidManifest.xml differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/assets/dexopt/baseline.prof and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/assets/dexopt/baseline.prof differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/assets/dexopt/baseline.profm and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/assets/dexopt/baseline.profm differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/assets/index.android.bundle and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/assets/index.android.bundle differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/classes3.dex and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/classes3.dex differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/lib/arm64-v8a/libreanimated.so and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/lib/arm64-v8a/libreanimated.so differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/lib/armeabi-v7a/libreanimated.so and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/lib/armeabi-v7a/libreanimated.so differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/lib/x86/libreanimated.so and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/lib/x86/libreanimated.so differ
Files /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/lib/x86_64/libreanimated.so and /tmp/fromBuild_io.bluewallet.bluewallet_1706539467/lib/x86_64/libreanimated.so differ
Only in /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/META-INF: GOOGPLAY.RSA
Only in /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/META-INF: GOOGPLAY.SF
Only in /tmp/fromPlay_io.bluewallet.bluewallet_1706539467/META-INF: MANIFEST.MF
Only in /tmp/fromPlay_io.bluewallet.bluewallet_1706539467: stamp-cert-sha256

Revision, tag (and its signature):
object f73307ac61048a1d5aa69edd5039685b1a828a13
type commit
tag v6.5.0
tagger Marcos Rodriguez Velez <marcospr@pm.me> 1706553495 -0400

v6.5.0
===== End Results =====

I used this Dockerfile to build:

FROM docker.io/node:16.20.0-bullseye-slim

ARG UID=1000
ARG TAG
ARG VERSION

RUN set -ex; \
    apt-get update; \
    DEBIAN_FRONTEND=noninteractive apt-get install --yes \
      -o APT::Install-Suggests=false --no-install-recommends \
      patch git openjdk-11-jre-headless openjdk-11-jdk; \
    rm -rf /var/lib/apt/lists/*; \
    deluser node; \
    useradd --uid $UID --create-home --shell /bin/bash appuser; \
    mkdir -p /Users/runner/work/1/; \
    chown -R appuser:appuser /Users/;

USER appuser

ENV ANDROID_SDK_ROOT="/home/appuser/sdk" \
    ANDROID_HOME="/home/appuser/sdk" \
    NODE_ENV="production"

RUN set -ex; \
    mkdir -p "/home/appuser/sdk/licenses"; \
    printf "\n24333f8a63b6825ea9c5514f83c2829b004d1fee" > "/home/appuser/sdk/licenses/android-sdk-license"; \
    cd /Users/runner/work/1/; \
    git clone --branch $TAG https://github.com/BlueWallet/BlueWallet /Users/runner/work/1/s/;

WORKDIR /Users/runner/work/1/s/

RUN set -ex; \
    npm install --production --no-optional --omit=optional --no-audit --no-fund --ignore-scripts; \
    npm run postinstall; \
    # Work around issue with realm: https://github.com/realm/realm-js/issues/6204#issuecomment-1772638401
    rm -rf node_modules/realm; npm install realm; \
    echo '"master"' > current-branch.json;

RUN set -ex; \
    cd /Users/runner/work/1/s/android; \
    ./gradlew assembleRelease

and these build commands:

repo=https://github.com/BlueWallet/BlueWallet
tag="v$versionName"
builtApk=$workDir/app-release-unsigned.apk

test() {
  podman rmi bluewallet -f
  podman build \
    --tag bluewallet \
    --cgroup-manager cgroupfs \
    --ulimit nofile=16384:16384 \
    --build-arg UID=$(id -u) \
    --build-arg TAG=$tag \
    --build-arg VERSION=$versionCode \
    --file $SCRIPT_DIR/test/android/io.bluewallet.bluewallet.dockerfile
  podman run \
    -it \
    --volume $workDir:/mnt \
    --rm \
    -u root \
    bluewallet \
    bash -c \
      'cp /Users/runner/work/1/s/android/app/build/outputs/apk/release/*.apk /mnt/'

  podman rmi bluewallet -f
  podman image prune -f
}

@Giszmo
Copy link
Author

Giszmo commented Apr 18, 2024

Diff is huge for 6.6.1, too. With diffoscope I see no pattern to take a guess on what might be the issue. I used basically the same script as above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

10 participants