Как общаться между Java и биткойнами?

У меня проблемы со связью между Java и bitcoind — каждая библиотека JSON RPC, которую я пробую, имеет некоторые проблемы. Может ли кто-нибудь предоставить рабочую реализацию даже самой простой связи JSON RPC между Java и bitcoind?

Есть биткойн-библиотека для java... Возможно, поэтому мало кто работает над JSON RPC для java.

Ответы (9)

Вот ранний экспериментальный клиент, с которым я некоторое время назад поигрался . Он поддерживает getInfo, getBalance и getNewAddress и может быть легко расширен. Чтобы запустить его, учетные данные для вашего локального биткойна должны совпадать со значениями в клиентском классе:

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

Не стесняйтесь использовать этот код как угодно, но помните, что это всего лишь 5 минут работы на старте, а не реальная клиентская реализация. Надеюсь, это поможет вам генерировать некоторые идеи!

Мне очень нравится это решение, единственная проблема в том, что первый вызов этого клиента занимает очень много времени. Вызов getrawtransaction на моем ноутбуке занимает 288 мс, подача иска к этому http-клиенту, в то время как запуск процесса из Java занимает ~ 5 мс. Однако этот клиент более эффективен в долгосрочной перспективе, занимая в среднем около 4 мс для всех остальных вызовов. Я попытался ускорить его, пытаясь использовать аутентификацию с помощью файлов cookie, но не смог... Решения этой проблемы приветствуются!

Вы также можете попробовать https://github.com/clanie/bitcoind-client — он находится на ранней стадии разработки, но уже поддерживает почти все методы, предоставляемые bitcoind.

У меня была такая же проблема, и я создал реализацию здесь: https://github.com/johannbarbie/BitcoindClient4J .

Поскольку это похоже на набор ссылок, я просто добавлю еще одну:

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

Библиотека под лицензией Apache: https://github.com/SulacoSoft/BitcoindConnector4J

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

Он использует Htmunit вместо Apache Http Library, что упрощает его понимание и расширение.

На самом деле я написал и протестировал его для Litecoin для одного из моих проектов. Но он был расширен для поддержки биткойнов, и доступны все методы RPC.

Так как я нигде не смог найти рабочий фрагмент кода, вот полный рабочий пример (на Scala):

Сначала я создал вспомогательный объект:

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
  }
}

Затем я использовал его следующим образом:

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"}"""
) 

Одной из проблем Java является многословность языка. Однако верно также и то, что жизнь языка в этом очень старая.

Я столкнулся с проблемой общения с интерфейсом rpc биткойна для одного из моих побочных проектов на c-lightning, и я не смог найти чистую библиотеку, которая могла бы также взаимодействовать с другими интерфейсами rpc, полученными из ядра биткойна, такими как litecoin. Кроме того, я также нашел библиотеку, которая проста, как библиотека Python.

Мой результат разработки библиотеки, соответствующей этому требованию, называется lite-bitcoin-rpc и доступен на GitHub https://github.com/clightning4j/lite-bitcoin-rpc .

Это простая библиотека, поскольку with дает возможность написать оболочку JSON в классе Java и использовать ее для декодирования ответа. Конечному пользователю нужно только нащупать карту с параметрами и создать класс Java, в котором декодируется полезная нагрузка JSON, полученная от ядра биткойна.

Пример можно найти в тестовом каталоге https://github.com/clightning4j/lite-bitcoin-rpc/tree/main/lib/src/test .

а пример кода можно

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());
    }
  }

Если вам не нравится Scala, вот код Jus12 на 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
}