
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:
- Interacción con los clientes Ethereum usando el API JSON-RPC dando soporte a todos los tipos de métodos disponibles
- Soporta todos los nodos Geth y Parity para administrar cuentas y firmar transacciones
- Envío de solicitudes de clientes de forma asíncrona y sincrónica
- 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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:
1 2 3 4 5 |
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:
1 |
UTC--2017-07-16T16-58-10.930000000Z--54f80f2634207fba30525ebdd779cedd7e745cb4.json |
2.- JSON con los datos del monedero:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
{ "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:
1 2 3 4 |
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:
1 2 3 |
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:
1 2 3 |
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:
1 2 3 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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:
1 |
{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x54f80f2634207fba30525ebdd779cedd7e745cb4","latest"],"id":1} |
Vamos a ver un ejemplo del uso del método que hemos creado:
1 2 3 4 5 6 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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:
1 2 3 4 5 6 7 8 9 |
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:
1 2 3 4 5 6 7 8 9 10 11 |
> 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 :
1 |
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:
1 2 3 4 5 6 7 8 |
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
1 |
e.sendCoins(credentials, sendAccount, new BigDecimal(ethers)); |
Y vemos como se dispara el evento en nuestra billetera.
1 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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:
1 2 3 4 5 |
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!