Things of interesting

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

bitcoindからのメッセージをZeroMQ経由(+SpringBoot)で購読する

やること

bitcoindにはJSON-RPC以外にZeroMQを介したメッセージ購読ができるようになっていますが、これをSpringBootアプリケーション上にサンプル実装します。

ZeroMQの実装ライブラリとしてjeromqを使用します。
GitHub - zeromq/jeromq: Pure Java ZeroMQ

公式ドキュメント
bitcoin/zmq.md at master · bitcoin/bitcoin · GitHub

開発環境

  • Windows10
  • Java8

bitcoindのインストールと起動

  1. 公式サイトからインストーラをダウンロードしてインストールします。
    ダウンロード - ビットコイン

  2. 設定ファイル作成して適当なディレクトリに配置します。
    bitcoin.conf

     regtest=1  
     server=1  
     rpcuser=username
     rpcpassword=password
     zmqpubrawblock=tcp://127.0.0.1:28332
     zmqpubhashblock=tcp://127.0.0.1:28332
     zmqpubrawtx=tcp://127.0.0.1:28332
     zmqpubhashtx=tcp://127.0.0.1:28332
    

    実装の確認をするだけなので regtest 環境で起動させます。

  3. bitcoindを起動します。

    $ cd ${bitcoin intall dir}/daemon
    $ ./bitcoind.exe -conf=/path/to/bitcoin.conf
    

SpringBootアプリケーションの作成

Spirng Initializr等で作成したテンプレートプロジェクトにjeromqライブラリを追加します。

build.gradle

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile group: 'org.zeromq', name: 'jeromq', version: '0.4.2'
}

zmqクライアント部分は公式のpython実装を参考に作成します。
https://github.com/bitcoin/bitcoin/tree/master/contrib/zmq

購読対象のメッセージを設定しないと受信できないので、追加する場合はsubscribeメソッドを呼んでください(コメントアウトの部分)。
bitcoindは以下4種類のメッセージをサポートしています。

socket.subscribe("hashtx"); // transactionのハッシュ値
socket.subscribe("hashblock"); // blockのハッシュ値
socket.subscribe("rawblock"); // blockの生データ
socket.subscribe("rawtx");  // transactionの生データ

BitcondZmqClient.java

@Slf4j
@Component
public class BitcondZmqClient {
    private static final String url = "tcp://127.0.0.1:28332";

    @PostConstruct
    private void init() {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Runnable runner = this::connectZmqServer;
        executor.execute(runner);
    }

    private void connectZmqServer() {
        ZMQ.Context context = ZMQ.context(1);
        ZMQ.Socket socket = context.socket(ZMQ.SUB);

        socket.connect(url);
        socket.subscribe("rawblock");
        // socket.subscribe("hashblock");
        // socket.subscribe("rawtx");
        // socket.subscribe("hashtx");
        log.info("connect to " + url);

        while (!Thread.currentThread().isInterrupted()) {
            String topic = socket.recvStr();
            log.info("client - topic:" + topic);
            if (socket.hasReceiveMore()) {
                // byte[] body = socket.recv();
                String body = socket.recvStr();
                switch (topic) {
                    case "rawblock":
                        log.info("client - body:" + body);
                        break;
                    default:
                        break;
                    }
            }
            if (socket.hasReceiveMore()) {
                String sequence = socket.recvStr();
                log.info("client - sequence:" + sequence);
            }
        }
        socket.close();
        context.term();
    }

}

ComponentScan対象になるように@SpringBootApplication配下のディレクトリに配置してください。
メインの業務処理とは分けられるように別スレッドでZmqClientを起動しています。

ZeroMQ経由のメッセージ購読確認

  1. SpringBootアプリケーションを起動します。

  2. regtest環境とbitcoin-cliコマンドを使ってブロックをマイニングします。

     $ cd ${bitcoin intall dir}/daemon
     $ ./bitcoin-cli.exe -conf=/path/to/bitcoin.conf generate 1
    
  3. ログにブロックの情報が表示されれば成功です。

memo

トランザクション情報もメッセージを変えることで購読できるので試してみてください。

regtest環境を初期構築した後はマイニング報酬を得るのに100ブロック掘る必要があるので sendtoaddress コマンド実行時にお金が足りないと表示される場合には generate 100 で100ブロックを掘ってみてください。