Creando aplicaciones en la Blockchain de Ethereum usando Java y Web3j

Hola amigos, hoy les quiero hablar de la librería Web3j en la cual he hecho varias pruebas y me parece genial ya que es muy ligera y contiene todo lo necesario para crear aplicación y contratos inteligentes en la blockchain de ethereum.

¿Que es Web3j?

Es una biblioteca ligera escrita en Java 8, esta en una capa superior ya que necesita integrarse con un cliente o nodo de la red Ethereum, en mi entrada anterior Instalar un nodo testnet de ethereum rinkeby esta todo lo necesario para crear tu propio nodo y obtener monedas testnet para comenzar las pruebas.

Características principales:

  1. Interacción con los clientes Ethereum usando el  API JSON-RPC dando soporte a todos los tipos de métodos disponibles
  2. Soporta todos los  nodos Geth y Parity para administrar cuentas y firmar transacciones
  3. Envío de solicitudes de clientes de forma asíncrona y sincrónica
  4. Generación automática de wrappers de función de contrato inteligente Java de archivos Solidity ABI

Enlaces de interés:

Si necesitas más información acerca de este proyecto te dejo unos enlaces de la documentación oficial:

Creando una cuenta Ethereum en Java usando Web3j

Lo primero que vamos a hacer es crear una cuenta ethereum para recibir nuestro ether, para ello vamos a utilizar el api de web3j

    public static String[] createNewWallet(String password) throws Exception {
        try {
            File file = new File(WALLET_PATH);
            String name = null;
            String json = null;
            if (file.exists()) {
                // al crear el monedero nos retorna el nombre del archivo generado dentro de la carpeta indicada
                name = WalletUtils.generateFullNewWalletFile(password, file);
                // vamos a abrir el monedero y retornar el json generado
                Path path = FileSystems.getDefault().getPath(WALLET_PATH, name);
                byte[] b = java.nio.file.Files.readAllBytes(path);
                json = new String(b);
                return new String[]{name, json};
            } else {
                throw new Exception("Invalid WALLET_PATH " + WALLET_PATH);
            }

        } catch (Exception ex) {
            throw ex;
        }
    }

En la sentencia anterior podemos destacar la siguiente linea: WalletUtils.generateFullNewWalletFile(password, file); Como podemos ver hemos usando la clase WalletUtils y hemos creado una cuenta ethereum de una forma muy simple.

Invocamos nuestro método de la forma siguiente:

EthereumWallet e = EthereumWallet.getInstance();
String[] wallet = e.createNewWallet("123456");
for (String string : wallet) {
    System.out.println(string);
}

El arreglo generado contiene las siguientes cadenas:

1.- Nombre del monedero:

UTC--2017-07-16T16-58-10.930000000Z--54f80f2634207fba30525ebdd779cedd7e745cb4.json

2.- JSON con los datos del monedero:

{
	"address": "54f80f2634207fba30525ebdd779cedd7e745cb4",
	"id": "c3484c26-54af-4c4f-baf5-a10caf813031",
	"version": 3,
	"crypto": {
		"cipher": "aes-128-ctr",
		"ciphertext": "1a70945a3f05a3c2e2bb3def0cd3b88b735462067ebe5acc1f63a74f6b98bb15",
		"cipherparams": {
			"iv": "d3d05d62e600f3d1f91f64856db9a8c2"
		},
		"kdf": "scrypt",
		"kdfparams": {
			"dklen": 32,
			"n": 262144,
			"p": 1,
			"r": 8,
			"salt": "ba10abe0b7adb51bc52dacc477ebeb5e3008d5f72fa60bfee3a6d409483248fd"
		},
		"mac": "96ae81b9a7233c544cca25616f0b85adad7b9ec9b1e5a2e81dc686d7e4576086"
	}
}

Las cuentas en ethereum son ficheros JSON encriptados con una contraseña. Este fichero debe guardarse con mucho cuidado y en un lugar seguro ya que podrias perder tus ether.

Abrir una cuenta Ethereum en Java usando Web3j

Ahora queremos comenzar a utilizar nuestra cuenta ethereum para ello vamos a ver el siguiente método:

public Credentials openWallet(String password, String walletName) throws Exception {
    Credentials credentials = WalletUtils.loadCredentials(password, WALLET_PATH + walletName);
    return credentials;
}

Hemos usado nuevamente la clase WalletUtils, esta vez el método loadCredentials que necesita el nombre de la cuenta ethereum y la contraseña. Este nos retorna un objeto Credencial el cual nos da acceso a administrar nuestros ether de ese monedero.

Este seria un ejemplo para utilizar el método anterior:

Credentials credentials = e.openWallet("123456", "UTC--2017-07-16T16-58-10.930000000Z--54f80f2634207fba30525ebdd779cedd7e745cb4.json");
String addressSender = credentials.getAddress();
System.out.println(addressSender);

Crear una instancia del objecto Web3j

Para comenzar a usar los métodos de webj3 necesitamos conectarnos al nodo que tenemos en ejecución, recordando mi post anterior hemos colocado el socket IPC en la siguiente ruta «/opt/apps/geth.ipc», ahora bien si queremos conectarnos directamente al socket y tener los eventos de la blockchain en tiempo real usaremos:

private final static String IPC_SOCKET_PATH = "/opt/apps/geth.ipc";

web3j = Web3j.build(new UnixIpcService(IPC_SOCKET_PATH));

O bien si no queremos usar Socket podemos usar HTTP con las siguiente sentencia:

private final static String URL_JSON_RPC = "http://127.0.0.1:38904";

web3j = Web3j.build(new HttpService(URL_JSON_RPC));

Ver el balance o saldo de una cuenta Ethereum en Java usando Web3j

Ahora si a todos se nos viene la primera pregunta y ¿Como vemos el saldo de nuestro monedero? Vamos a crear el siguiente método que nos retorna el saldo del monedero.

    public BigInteger getAddressBalance(String address) throws Exception {
        try {
            // Vamos a enviar una solicitud asíncrona usando el objecto web3j 
            EthGetBalance ethGetBalance = web3j
                    .ethGetBalance(address, DefaultBlockParameterName.LATEST)
                    .sendAsync()
                    .get();
            
            // saldo en wei
            BigInteger wei = ethGetBalance.getBalance();
            return wei;
        } catch (Exception ex) {
            throw ex;
        }
    }

Ya estamos utilizando el objecto web3j en esta caso hemos usado web3j .ethGetBalance el cual en realidad hace un llamado al Api de Ethereum como se ve a continuación:

{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x54f80f2634207fba30525ebdd779cedd7e745cb4","latest"],"id":1}

Vamos a ver un ejemplo del uso del método que hemos creado:

BigInteger wei =  e.getAddressBalance(addressSender);
System.out.println(wei + " wei"); // Salida en wei 
BigDecimal x18 = new BigDecimal("1000000000000000000");
BigDecimal bigDecimal = new BigDecimal(wei);
BigDecimal result = bigDecimal.divide(x18);
System.out.println(result + " ether"); // salida en ether

Recibiendo ether en nuestra cuenta Ethereum usando Web3j:

Para recibir ether tenemos los métodos Observable que implementa Java RX para el manejo de eventos, vamos a crear el siguiente método:

public void onTransactionReceived(Transaction tx) {
        BigInteger wei = tx.getValue();
        BigDecimal ether = Convert.fromWei(new BigDecimal(tx.getValue()), Convert.Unit.ETHER);
        String to = tx.getTo();
        String hash = tx.getHash();
        String from = tx.getFrom();

        if (addressMap.containsKey(tx.getTo())) {
            // credito
            System.out.println("Se ha recibido su depósito de " + ether + " ether enviado a su cuenta " + to + " enviado por " + from + " tx " + hash);
        } else if (addressMap.containsKey(tx.getFrom())) {
            //debito
            System.out.println("Se ha hecho un retiro de " + ether + " ether desde su cuenta " + to + " hacia la cuenta destino " + from + " tx " + hash);
        }
    }

Creamos el objecto Subscription para ver los eventos de web3j con:

    subscription = web3j.blockObservable(false).subscribe(block -> {
        blockObservable(block);
    }, Throwable::printStackTrace, () -> System.out.println("block done"));
    web3j.transactionObservable().subscribe(tx -> {
        transactionObservable(tx);
    }, Throwable::printStackTrace, () -> System.out.println("tx done"));
    web3j.pendingTransactionObservable().subscribe(tx -> {
        pendingTransactionObservable(tx);
    }, Throwable::printStackTrace, () -> System.out.println("ptx done"));

Ahora si recarguemos nuestro monedero en nuestro caso he enviado 0.03 ether desde la linea de comando de geth, en la cual tenemos saldo:

> eth.accounts[0];
"0xe2632021f255da92be0c0434855a63acbcbae286"
> var sender = eth.accounts[0];
undefined
> var receiver = '0x54f80f2634207fba30525ebdd779cedd7e745cb4'
undefined
> var amount = web3.toWei(0.03, "ether")
undefined
> personal.unlockAccount(sender, "123456")
> eth.sendTransaction({from:sender, to:receiver, value: amount})
"0xcc6403a6179698a653419cccac7458445ff318482054b6bfb0141e9323c9382e"

y lo hemos recibido en nuestro monedero :

Se ha recibido su depósito de 0.03 ether enviado a su cuenta 0x54f80f2634207fba30525ebdd779cedd7e745cb4 enviado por 0xe2632021f255da92be0c0434855a63acbcbae286 tx 0xcc6403a6179698a653419cccac7458445ff318482054b6bfb0141e9323c9382e

Enviar ether desde nuestra cuenta Ethereum usando Web3j:

Ahora ya que ya tenemos balance en nuestra cuenta podemos enviar ethers usando el siguiente método:

public TransactionReceipt sendCoins(Credentials credentials, String toAddress, BigDecimal value) throws Exception {
        try {
            TransactionReceipt transactionReceipt = Transfer.sendFunds(web3j, credentials, toAddress, value, Convert.Unit.ETHER);
            return transactionReceipt;
        } catch (Exception ex) {
            throw ex;
        }
    }

Ahora vamos a enviar ether usando el método creado

e.sendCoins(credentials, sendAccount, new BigDecimal(ethers));

Y vemos como se dispara el evento en nuestra billetera.

Se ha hecho un retiro de 0.02 ether desde su cuenta 0xe2632021f255da92be0c0434855a63acbcbae286 hacia la cuenta destino 0x54f80f2634207fba30525ebdd779cedd7e745cb4 tx 0x9b0c842a143678cc7aeca7818ed82ea414727605e409a540bb84d92ca62b36c7

Ver transacciones de mi monedero usando ETHERSCAN

Ahora quiero listar todas las entradas y salidas de mi cuenta, de momento no he podido hacerlo con el api de web3j  y al parecer es un poco mas complejo ya que tendríamos que desarrollar un script en la consola de geth. Por ahora vamos a usar el API de ETHERSCAN

private final static String ENDPOINT_TRANSACTIONS_LIST = "/api?module=account&action=txlist";
private final static String API_RESTFUL_URI = "http://rinkeby.etherscan.io";

 public static Transactions getTransactionsByAddress(String address) {
        String endpoint = API_RESTFUL_URI + ENDPOINT_TRANSACTIONS_LIST;
        
        Client client = ClientBuilder.newClient();
        Response response = client.target(endpoint)
                .queryParam("address", address)
                .queryParam("startblock", "0")
                .queryParam("endblock", "99999999")
                .queryParam("sort", "asc")
                .queryParam("apikey", ETHERSCAN_APIKEY)
                .request(MediaType.APPLICATION_JSON)
                .get();
        
        String json = response.readEntity(String.class);
        
        client.close();
        ObjectMapper mapper = new ObjectMapper();
        Transactions transactions = null;
        if (response.getStatus() == 200) {
            try {
                transactions = mapper.readValue(json, Transactions.class);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return transactions;
    }

Probamos el método con la siguiente sentencia de código:

Transactions tx = EtherscanService.getTransactionsByAddress(addressSender);
List<Result> list = tx.getResult();
for (Result result : list) {
    System.out.print(" BlockHash " + result.getBlockHash());
}

He creado un pequeño monedero muy básico en ethereum con webj3 si quieres puedes hacer un fork del código desde Github o si quieres colaborar eres bienvenido.

Si te gusta lo que publico puedes seguirme en las redes sociales y unos wei o satoshis son bienvenidos!

 

 

Comments are closed.