web3j icon indicating copy to clipboard operation
web3j copied to clipboard

Versions for Android should support @Keep Annotations to keep for instance org.web3j.crypto.WalletFile from being minified by Googles R8 compiler

Open lsh-silpion opened this issue 4 years ago • 3 comments

Versions for Android should support @Keep Annotations to keep for instance org.web3j.crypto.WalletFile from being minified by Googles R8 compiler

Steps To Reproduce

When creating a release version of our Android App, which uses the web3j framework we experienced than some classes from web3j got minified which should not. In our example this was org.web3j.crypto.WalletFile, where the minification caused strange wallet files to be created which could not be read externally. See attached screenshot:

Bildschirmfoto 2020-07-14 um 17 29 04

Expected behavior

Some classes like org.web3j.crypto.WalletFile should not be minified adding some pro-guard rules for Android should resolve this situation. We added the following rule:

-keep class org.web3j.crypto.* { *; }

for you it would be better to annotate those classes with androidx.annotation.Keep

Actual behavior

minification of all classes by Google R8

Environment

Describe the environment in which the issue occurs

  • Web3j version:

implementation 'org.web3j:core:4.6.0-android'

  • Java or Android version

Android from API version 23 and above

  • Operating System

Android

Additional context

Add any other context about the problem here.

  • Logs
  • Sample code and/or code snippets
  • Unit/integration tests to highlight the issue
  • etherscan references

lsh-silpion avatar Jul 14 '20 16:07 lsh-silpion

Some addition:

When using web3j in a function like this:

    fun getBLX01Balance(walletAddress: String, completion: (BigDecimal) -> Unit) {
        doAsync {
            // connect to node
            val web3 = Web3j.build(HttpService(InfuraEndpoint))

            val credentials: Credentials = WalletUtils.loadCredentials(getWalletPassword(), App.instance.web3WalletFile)

            // TODO: get contractAddress from backend
            val contractAddress = "0x45749338BDb0E35f4e36fA5741bEa0413E7322A9"

            val blx01Contract: ERC20 = ERC20.load(contractAddress, web3, credentials, DefaultGasProvider())

            // TODO: use own wallet address once there are BLX01 tokens on it
            val weiBlx01Balance: BigInteger = blx01Contract.balanceOf("0xcfeb4b83251fb149687fa99f1c660f99411eefe3").send()

            val blx01Balance: BigDecimal = Convert.fromWei(weiBlx01Balance.toString(), Convert.Unit.ETHER)

            completion(blx01Balance)
        }
    }

I needed to add the following rules to proguard-rules.pro:

-keep class org.web3j.crypto.* { *; }
-keep class org.web3j.protocol.** { *; }

otherwise I get problems in com.fasterxml.jackson during serialization like this:

2020-07-16 01:17:38.033 21230-21827/com.bloxxter.bloxxterid E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #3
    Process: com.bloxxter.bloxxterid, PID: 21230
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$4.done(AsyncTask.java:399)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
     Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.web3j.protocol.c.k and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
        at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(:1191)
        at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(:313)
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(:71)
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(:33)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(:480)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(:319)
        at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(:3906)
        at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(:3220)
        at org.web3j.protocol.Service.send(:46)
        at org.web3j.protocol.c.k.c(:87)
        at com.bloxxter.bloxxterid.helper.l$e.e(:122)
        at com.bloxxter.bloxxterid.helper.l$e.a(:27)
        at com.bloxxter.bloxxterid.helper.m.a(:12)
        at com.bloxxter.bloxxterid.helper.m.doInBackground(:6)
        at android.os.AsyncTask$3.call(AsyncTask.java:378)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:919) 
2020-07-16 01:17:38.043 21230-21808/com.bloxxter.bloxxterid E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    Process: com.bloxxter.bloxxterid, PID: 21230
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$4.done(AsyncTask.java:399)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
     Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.web3j.protocol.c.k and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
        at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(:1191)
        at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(:313)
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(:71)
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(:33)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(:480)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(:319)
        at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(:3906)
        at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(:3220)
        at org.web3j.protocol.Service.send(:46)
        at org.web3j.protocol.c.k.c(:87)
        at l.d.g.e.c(:155)
        at l.d.g.d.a(:134)
        at l.d.g.c.b(:293)
        at l.d.g.c.d(:301)
        at l.d.g.c.c(:312)
        at l.d.g.c.f(:400)
        at l.d.g.a.call(Unknown Source:6)
        at org.web3j.protocol.c.i.b(:42)
        at com.bloxxter.bloxxterid.helper.l$d.e(:143)
        at com.bloxxter.bloxxterid.helper.l$d.a(:27)
        at com.bloxxter.bloxxterid.helper.m.a(:12)
        at com.bloxxter.bloxxterid.helper.m.doInBackground(:6)
        at android.os.AsyncTask$3.call(AsyncTask.java:378)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:919) 

And an important hint for somebody who finds this while searching the web for Problems with ProGuard or Googles R8 when using web3j: Googles R8 manipulates the visibilities of class members! (when you allow it to do so, also, even when those classes seem to be protected by a -keep clause). I had to find it out the hard way, I debugged literally for hours until I had a clue what was going on. The default ProGuard file named proguard-android-optimize.txt which will be used by this clause proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' in your standard projects build.gradle contains a clause -allowaccessmodification which causes the serialization of Request to run havoc:

     Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.fasterxml.jackson.databind.util.LRUMap and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.web3j.protocol.core.Request["web3jService"]->org.web3j.protocol.http.HttpService["objectMapper"]->com.fasterxml.jackson.databind.ObjectMapper["_deserializationConfig"]->com.fasterxml.jackson.databind.DeserializationConfig["_base"]->com.fasterxml.jackson.databind.cfg.BaseSettings["_classIntrospector"]->com.fasterxml.jackson.databind.introspect.BasicClassIntrospector["_cachedFCA"])
        at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(:1191)
        at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(:313)
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(:71)
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(:33)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(:727)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(:719)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(:727)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(:719)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(:727)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(:719)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(:727)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(:719)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(:727)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(:719)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(:727)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(:719)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(:155)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(:480)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(:319)
        at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(:3906)
        at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(:3220)
        at org.web3j.protocol.Service.send(:46)
        at org.web3j.protocol.core.Request.send(:87)

morale of the story: Don't use proguard-android-optimize.txt when building Android-Apps with web3j. Better use a copy of this file where you remove the -allowaccessmodification clause.

Good luck!

lsh-silpion avatar Jul 15 '20 23:07 lsh-silpion

I've been testing confusing code for three days. I didn't expect this. Thank you

juewulingluan avatar Dec 14 '21 08:12 juewulingluan

remove -allowaccessmodification from proguard-android-optimize.txt (copy)

It's work , Thank for solution

https://android.googlesource.com/platform/sdk/+/master/files/proguard-android-optimize.txt

RoyserAVA avatar Jun 16 '22 09:06 RoyserAVA