No se puede recuperar el valor de Web3j sendFunds Future

He estado intentando enviar fondos de una cuenta de ethereum a otra usando web3j para probar que funciona en Android. Estoy usando una instancia en la nube de Infura.

Primero, parece que necesito enviarlo de forma asíncrona porque Android requiere que lo hagas. En el código a continuación, he intentado hacerlo, pero falla en el último paso, cuando intento llegar desde el futuro. Si no trato de obtener el valor, entonces no hay excepción, pero obviamente no puedo hacer nada con el valor que quiero hacer.

        final String FROM_ADDRESS = "0x6861B070f43842FC16eAD07854eE5D91B9D27C13";
    final String TO_ADDRESS = "0x31B98D14007bDEe637298086988A0bBd31184523";

    //Credentials credentials = obtainCredentials(WALLET_DIRECTORY);

    Callable<TransactionReceipt> task = new Callable<TransactionReceipt>() {
        @Override
        public TransactionReceipt call() throws Exception {
            Web3j web3 = Web3jFactory.build(
                    new HttpService("https://rinkeby.infura.io/SxLC8uFzMPfzwnlXHqx9")
            );
            Log.d(TAG, web3.ethGasPrice().getJsonrpc());

            ClientTransactionManager clientTransactionManager =
                    new ClientTransactionManager(web3, FROM_ADDRESS);
            Log.d(TAG, clientTransactionManager.getFromAddress());

            org.web3j.tx.Transfer tran = new org.web3j.tx.Transfer(web3, clientTransactionManager);
            Log.d(TAG, String.valueOf(tran.getSyncThreshold()));

            RemoteCall<TransactionReceipt> rc = tran.sendFunds(
                    TO_ADDRESS,
                    BigDecimal.valueOf(1.0),
                    Convert.Unit.ETHER
            );
            Log.d(TAG, String.valueOf(rc.toString()));
            return rc.send();
        }
    };
    Future<TransactionReceipt> future = Async.run(task);
    return future.get();

Viene con esta excepción:

11-19 12:49:03.418 32442-32442/com.example.graeme.beamitup W/System.err: java.util.concurrent.ExecutionException: org.web3j.protocol.exceptions.ClientConnectionException: Invalid response received: okhttp3.internal.http.RealResponseBody@3c286ab
11-19 12:49:03.418 32442-32442/com.example.graeme.beamitup W/System.err:     at java.util.concurrent.FutureTask.report(FutureTask.java:94)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at java.util.concurrent.FutureTask.get(FutureTask.java:164)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at com.example.graeme.beamitup.Transfer.sendTransfer(Transfer.java:183)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at com.example.graeme.beamitup.MainActivity.onCreate(MainActivity.java:26)
11-19 12:49:03.419 32442-32442/com.example.graeme.beamitup W/System.err:     at android.app.Activity.performCreate(Activity.java:6272)

Señala una respuesta no válida de la transferencia HTTP, pero ¿no debería suceder esto incluso sin probar Future.get? El código se ejecuta de cualquier manera, ¿correcto? ¿Qué he hecho mal para crear este error?

Respuestas (1)

1) Esta línea probablemente no esté haciendo lo que esperas:

Log.d(TAG, String.valueOf(rc.toString()));

rc tiene tipo RemoteCall<TransactionReceipt>, lo que significa que debe ejecutarse. En cambio, su código convierte la invocación a una Cadena (dos veces): una vez desde toString()y otra desde String.valueOf.

2) Creas un futuro, luego esperas a que se complete, y sé que sabes que esto no tiene sentido. Prescindí del futuro, ni idea de por qué Android insiste en un futuro. Mi código más simple da el mismo resultado que su código, por lo que al menos sabemos que el futuro no presentará el problema que informa.

3) El error surge después de enviar esta carga JSON-RPC:

{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}

El ClientTransactionManager.sendTransaction()método muere cuando intenta ejecutar:

return web3j.ethSendTransaction(transaction)
            .send();

Aunque web3j.ethSendTransaction(transaction)parece funcionar bien, el send()método encadenado devuelve una respuesta fallida. Sospecho que se requiere autenticación, pero, por supuesto, no se proporcionaron credenciales, por lo que se rechazó la transacción.

No trabajo en Android, así que aquí está el código con el que trabajé:

import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.ClientTransactionManager;
import org.web3j.utils.Convert;

import java.math.BigDecimal;

public class SO {
    private static final String FROM_ADDRESS = "0x6861B070f43842FC16eAD07854eE5D91B9D27C13";
    private static final String TO_ADDRESS = "0x31B98D14007bDEe637298086988A0bBd31184523";

    public static void main(String[] args) throws Exception {
        TransactionReceipt value = new SO().test();
        System.out.println(value);
    }

    private TransactionReceipt test() throws Exception {
        //Credentials credentials = obtainCredentials(WALLET_DIRECTORY);

        Web3j web3 = Web3j.build(
            new HttpService("https://rinkeby.infura.io/SxLC8uFzMPfzwnlXHqx9")
        );
        System.out.println("ethGasPrice=" + web3.ethGasPrice().getJsonrpc());

        ClientTransactionManager clientTransactionManager =
            new ClientTransactionManager(web3, FROM_ADDRESS);
        System.out.println("From address=" + clientTransactionManager.getFromAddress());

        org.web3j.tx.Transfer transfer = new org.web3j.tx.Transfer(web3, clientTransactionManager);
        System.out.println("transfer=" + String.valueOf(transfer.getSyncThreshold()));

        RemoteCall<TransactionReceipt> rc = transfer.sendFunds(
            TO_ADDRESS,
            BigDecimal.valueOf(1.0),
            Convert.Unit.ETHER
        );
        TransactionReceipt receipt = rc.send();
        System.out.println("receipt=" + receipt.toString());
        return receipt;
    }
}
Gracias por su respuesta. Tenía razón en que el problema terminó relacionado con la autorización: no me había dado cuenta de que podía derivar la dirección de una cuenta de ethereum a partir de la clave pública, por lo que no había entendido correctamente el sistema de autorización de web3j. Esto me llevó a no entender cómo autorizar mis transferencias correctamente. Ahora lo he implementado con otra función web3j sendFunds que requiere y usa autorización.
Revisando esto, quise decir que puede derivar la clave/dirección pública de la clave privada.
Te entendí la primera vez, no hay problema. Vote mi respuesta (haga clic en la flecha hacia arriba arriba) si así lo desea :)
¡Hice! Tengo una reputación demasiado baja para que mis votos se muestren todavía:/