Things of interesting

プログラミングに関する技術ネタの備忘録

Java(SpringBoot)でbitcoindのJSON-RPCを利用する

やること

bitcoindはJSON形式でブロックやトランザクションの情報を取得できるJSON-RPCという通信プロトコルをサポートしています。
今回はSpringBootで作成したプロジェクト上で、bitcoind JSON-RPCを呼び出すサンプル実装を行います。

事前準備

SPRING INITIALIZR を使ってテンプレートプロジェクトを作成します。

以下の環境で作業を行います。

  • Java9
  • SpringBoot2.0.0M6
  • jsonrpc4j

実装してみる

jsonrpc4j(Java向けJSON-RPCライブラリ)

JavaJSON-RPCを利用するのにJava用ライブラリであるjsonrpc4jを使用します。
build.gradleの依存関係に追加します。
build.gradle

dependencies {
(Spring関連は省略)
    compile group: 'com.github.briandilley.jsonrpc4j', name: 'jsonrpc4j', version: '1.5.1'
}

Configクラスの作成

JSON-RPC Clientが呼び出すURLやユーザー・パスワードの設定を行います。
Server側となるbitcoindのconf設定は以下の記事と同じ前提です。

hnak.hatenablog.jp

以下のクラスを作成して適当なディレクトリに配置します。
BitcoindConfig.java

@Configuration
public class BitcoindConfig {
    private static final String endpoint = "http://localhost:18332";
    private static final String rpcuser ="username";
    private static final String rpcpassword ="password";
   
    public BitcoindConfig() {
        Authenticator.setDefault(new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication (rpcuser, rpcpassword.toCharArray());
            }
        });
    }

    @Bean
    public JsonRpcHttpClient jsonRpcHttpClient() {
        URL url = null;
        //You can add authentication headers etc to this map
        Map<String, String> map = new HashMap<>();
        try {
            url = new URL(endpoint);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new JsonRpcHttpClient(url, map);
    }

    @Bean
    public BitcoindClientAPI bitcoindClientAPI(JsonRpcHttpClient jsonRpcHttpClient) {
        return ProxyUtil.createClientProxy(getClass().getClassLoader(), BitcoindClientAPI.class, jsonRpcHttpClient);
    }
}

testnet,regtestのデフォルトポートは18332、本番環境は8332です。

JSON-RPC用のインターフェースクラスの作成

次にJSON-RPC用のインターフェースクラスを作成します。
@JsonRpcMethodアノテーションマッピングするAPIを指定することでJSON-RPCを呼び出すことができます。
呼び出したいAPIは以下を参考に引数、返り値を合わせます。

Chain Query: Bitcoin API:

今回は getblock APIを呼び出してみるのでblockhashとverbosityを引数に設定して、返り値はverbosity=1の想定でAPI Resultに合わせたPOJOを定義しておきます。

BitcoindClientAPI.java

public interface BitcoindClientAPI {

    @JsonRpcMethod("getblock")
    GetBlockResult getBlock(@JsonRpcParam(value = "blockhash") String blockhash, @JsonRpcParam(value = "verbosity") int verbosity);

}

GetBlockResult.java

@Getter
@Setter
public class GetBlockResult {
    private String hash;
    private int confirmations;
    private int size;
    private int strippedsize;
    private int weight;
    private int height;
    private int version;
    private String versionHex;
    private String merkleroot;
    private List<String> tx;
    private int time;
    private int mediantime;
    private int nonce;
    private String bits;
    private int difficulty;
    private String chainwork;
    private String previousblockhash;
    private String nextblockhash;
}

呼び出し元コントローラーの作成

@RestController
@RequestMapping("bitcoind")
public class BitcoindRpcController {

    @Autowired
    private BitcoindClientAPI bitcoindClientAPI;

    @RequestMapping(value = "block/{blockhash}", method = RequestMethod.GET)
    public GetBlockResult getBlock(@PathVariable("blockhash") String blockhash) {
        return bitcoindClientAPI.getBlock(blockhash, 1); // verbosity = 1
    }
}

動作確認

./gradlew bootRunでプロセスを起動し、以下のURLを叩いてブロックの情報が取得できればOKです。 http://localhost:8080/bitcoind/block/{blockhash}

※{blockhash} の部分にはbitcoin-cliのgenerateコマンドで実際に産出されたブロックのHash値を指定してください。