¿Cómo comunicarse entre Java y bitcoind?

Tengo problemas para comunicarme entre Java y bitcoind: todas las bibliotecas JSON RPC que pruebo tienen algunos problemas. ¿Alguien puede proporcionar una implementación funcional incluso de la comunicación JSON RPC más básica entre Java y bitcoind?

Hay una biblioteca de bitcoin para Java... Esa podría ser la razón por la que pocas personas están trabajando en un JSON RPC para Java.

Respuestas (9)

Aquí hay uno de los primeros clientes experimentales con los que había jugado hace un tiempo . Admite getInfo, getBalance y getNewAddress, y se puede expandir fácilmente. Para ejecutarlo, las credenciales de su bitcoind local deben coincidir con los valores de la clase de cliente:

httpclient.getCredentialsProvider().setCredentials(new AuthScope("localhost", 8332),
                    new UsernamePasswordCredentials("btc", "123"));

Siéntase libre de usar este código de cualquier manera, pero comprenda que son solo 5 minutos de trabajo al principio, no una implementación de cliente realista. ¡Espero que te ayude a generar algunas ideas!

Me gusta mucho esta solución, el único problema es que la primera llamada a este cliente lleva mucho tiempo. Una llamada 'getrawtransaction' en mi computadora portátil toma 288 ms para demandar a este cliente http mientras se ejecuta un proceso desde Java toma ~ 5 ms. Sin embargo, este cliente es más eficiente a largo plazo y toma alrededor de 4 ms en promedio para todas las demás llamadas. Intenté acelerarlo usando la autenticación de cookies, pero no pude... ¡Las soluciones a este problema son bienvenidas!

También puede probar https://github.com/clanie/bitcoind-client : se encuentra en desarrollo temprano, pero ya es compatible con casi todos los métodos proporcionados por bitcoind.

Tuve el mismo problema y creé una implementación aquí: https://github.com/johannbarbie/BitcoindClient4J

Debido a que esto parece una colección de enlaces, simplemente agregaré otro:

https://github.com/priiduneemre/btcd-cli4j

Biblioteca bajo licencia Apache: https://github.com/SulacoSoft/BitcoindConnector4J

https://github.com/nitinsurana/Litecoin-Bitcoin-RPC-Java-Connector

Utiliza Htmlunit en lugar de Apache Http Library, lo que lo hace un poco fácil de entender y ampliar.

De hecho, lo escribí y lo probé para Litecoin para uno de mis proyectos. Pero se ha ampliado para admitir bitcoin y todos los métodos RPC están disponibles.

Como no pude encontrar un fragmento de código de trabajo en ninguna parte, aquí hay un ejemplo de trabajo completo (en Scala):

Primero creé un objeto auxiliar:

import java.net.URL
import java.net.HttpURLConnection
import org.apache.commons.io.IOUtils

object CurlJsonData {
  def curl(url:String, jsonEncodedString:String) = {
    val httpcon = new URL(url).openConnection.asInstanceOf[HttpURLConnection]
    httpcon.setDoOutput(true);
    httpcon.setRequestProperty("Content-Type", "application/json");
    httpcon.setRequestProperty("Accept", "application/json");
    httpcon.setRequestMethod("POST");
    httpcon.connect;

    val outputBytes = jsonEncodedString.getBytes("UTF-8");

    // 'using' method from: https://stackoverflow.com/a/5218279/243233

    using(httpcon.getOutputStream){os =>
      os.write(outputBytes)
    }
    val code = httpcon.getResponseCode
    val isError = code >= 400 && code <= 500
    val resp = using{
      if (isError) httpcon.getErrorStream else httpcon.getInputStream
    }{is =>
      val writer = new StringWriter;
      IOUtils.copy(is, writer, "UTF-8");
      writer.toString;
    }
    httpcon.disconnect
    if (isError) throw new Exception(
      s"Resp code $code. Error: ${resp.take(200)}"
    ) else resp
  }
}

Entonces, lo usé de la siguiente manera:

import java.net.Authenticator
import java.net.PasswordAuthentication

val rpcuser = "alice";
val rpcpassword = "secret";

Authenticator.setDefault(
  new Authenticator {
    override def getPasswordAuthentication:PasswordAuthentication = {
      new PasswordAuthentication (rpcuser, rpcpassword.toCharArray)
    }
  }
)  

CurlJsonData.curl(
  "http://localhost:8332", 
  """{"method":"getblockchaininfo","params":[],"id":1,"jsonrpc":"1.0"}"""
) 

Uno de los problemas de Java es la verbosidad del lenguaje. Sin embargo, también es cierto que la vida de la lengua es muy antigua en estos que.

Caí en el problema de hablar con la interfaz rpc de bitcoin para uno de mis proyectos paralelos en c-lightning y no pude encontrar una biblioteca limpia que pudiera hablar también con otras interfaces rpc derivadas del núcleo de bitcoin, como litecoin. Además, también encontré una biblioteca que es fácil como una biblioteca de python.

Mi resultado de diseñar una biblioteca que respeta este requisito, se llama lite-bitcoin-rpc y está disponible en GitHub https://github.com/clightning4j/lite-bitcoin-rpc

Esta es una biblioteca fácil porque brinda la posibilidad de escribir el contenedor JSON en una clase Java y usarlo para decodificar la respuesta. El usuario final solo necesita sentir un mapa con parámetros y crear la clase Java donde decodificar la carga útil JSON recibida del núcleo de bitcoin.

Se puede encontrar un ejemplo en el directorio de prueba https://github.com/clightning4j/lite-bitcoin-rpc/tree/main/lib/src/test

y un ejemplo de código puede ser

public class LiteBitcoinRPCTest {

  private LiteBitcoinRPC bitcoinRPC;

  public LiteBitcoinRPCTest() {
    this.bitcoinRPC = new LiteBitcoinRPC("sandbox", "sandbox", "http://127.0.0.1:18333/");
  }

  @Test
  public void getBlockchainInfo() {
    try {
      BlockchainInfo info =
          bitcoinRPC.makeBitcoinRequest("getblockchaininfo", BlockchainInfo.class);
      TestCase.assertEquals(info.getChain(), "regtest");
    } catch (Exception e) {
      e.printStackTrace();
      TestCase.fail(e.getLocalizedMessage());
    }
  }

  @Test
  public void estimateFeeRateWithError() {
    Parameters parameters = new Parameters("estimatesmartfee");
    parameters.addParameter("conf_target", 6);
    try {
      BitcoinEstimateFee feee = bitcoinRPC.makeBitcoinRequest(parameters, BitcoinEstimateFee.class);
      TestCase.assertFalse(feee.getErrors().isEmpty());
    } catch (LiteBitcoinRPCException | BitcoinCoreException e) {
      TestCase.fail(e.getMessage());
    }
  }

Si Scala no es lo tuyo, aquí está el código de Jus12 en Kotlin:

package com.my.blockchainparser

import java.net.Authenticator
import java.net.PasswordAuthentication
import java.net.URL
import java.net.HttpURLConnection

fun main(args: Array<String>) {
    val rpcuser = "user"
    val rpcpassword = "password"
    Authenticator.setDefault(object : Authenticator() {
        override fun getPasswordAuthentication(): PasswordAuthentication {
            return PasswordAuthentication(rpcuser, rpcpassword.toCharArray())
        }
    })

    System.out.println(curl(
            "http://localhost:8332",
            """{"method":"getblockchaininfo","params":[],"id":1,"jsonrpc":"1.0"}"""
    ))
}

fun curl(url:String, jsonEncodedString:String): String {
    val httpcon = URL(url).openConnection() as HttpURLConnection
    httpcon.setDoOutput(true);
    httpcon.setRequestProperty("Content-Type", "application/json");
    httpcon.setRequestProperty("Accept", "application/json");
    httpcon.setRequestMethod("POST");
    httpcon.connect();

    val outputBytes = jsonEncodedString.toByteArray();

    httpcon.getOutputStream().use {
        it.write(outputBytes)
    }
    val code = httpcon.getResponseCode()
    val isError = code >= 400 && code <= 500
    val text = (if (isError) httpcon.getErrorStream() else httpcon.getInputStream())
            ?.bufferedReader()?.use {
        it.readText()
    } ?: "no connection"
    if (isError) throw Exception(
            "Resp code $code. Error: ${text.take(200)}"
    )
    return text
}