bitcoin-sidechain-handler-lld

Objective

This document is intended to outline generic design of a blockchain handler, a component used for monitoring and processing changes on a Bitcoin network.

For general information on sidechain handlers, read: https://peerplays.atlassian.net/wiki/spaces/PIX/pages/352026689/Generic+Sidechain+Interface+High+Level+Design

Block Diagram

Link to draw.io file:

https://drive.google.com/file/d/1ErmQfeaWa9m67si4hb0RZs54WVYWR0pC/view?usp=sharing

Sequence diagram

Link to draw.io file

https://drive.google.com/file/d/17kbez7C1Djaj-2AgyEzQ1Z-5CrSZ5wZE/view?usp=sharing

Same sequence diagram applies for all sidechain handlers. It shows interactions between components in a single sidechain handler.

Components

The purpose of Bitcoin sidechain handler is to notify sidechain manager that a transaction of interest has happened, and forward the data about that transaction to it, for further processing. As stated in general design, standard components of the sidechain handler are listener, communication interface and event handler.

Listener

For general information on sidechain listeners, read: https://peerplays.atlassian.net/wiki/spaces/PIX/pages/352092181/Generic+Sidechain+Listener+HLD

Some of the info in this section are taken from useful article on ZMQ and Bitcoin https://bitcoindev.network/accessing-bitcoins-zeromq-interface/

Assumptions

Blockchain node provides interface for monitoring changes in a blockchain.

Bitcoin node provides ZeroMQ messaging interface. The ZeroMQ facility implements a notification interface through a set of specific notifiers. Currently there are notifiers that publish blocks and transactions. This read-only facility requires only the connection of a corresponding ZeroMQ subscriber port in receiving software; it is not authenticated nor is there any two-way protocol involvement. Therefore, subscribers should validate the received data since it may be out of date, incomplete or even invalid.

Monitoring is based on new block, new transaction or filtered single event (like transfer operation to specific address)

Bitcoin's ZMQ uses a publish/subscribe (pubsub) design pattern, where the publisher in our case is our bitcoin node which published messages based on events. The subscriber on the other hand, includes any application looking to utilise it by subscribing to these events. There are currently 4 different publish (PUB) notification topics we can expose via bitcoind which notify us when ever a new block or transaction is validated by the node.

  • zmqpubhashtx : Publishes transaction hashes

  • zmqpubhashblock : Publishes block hashes

  • zmqpubrawblock : Publishes raw block information

  • zmqpubrawtx : Publishes raw transaction information

We can use any of these, but some are more practical than others. E.g. we can subscribe to receive hash values of newly created blocks:

socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 0 );

socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) );

zmqpubrawtx looks like best candidate we can use, since we can get raw transaction info, decode it, and we will have all required info in a single call. However, depending on number of transaction, we can expect more traffic between bitcoin node and listener.

zmqpubhashblock is also a good candidate, we will receive the hash of newly created block, then we can read the whole block by communication interface, parse it and get the info we need.

There is a sufficient C/C++ library for connecting to monitoring interface

ZeroMQ project provides a client library which we can use to connect to a Bitcoin node ZeroMQ interface. The project is alive and well maintained. At the time of writing this document, we are not aware of any problem which would require us to use specific library version, or link it statically into our software.

https://zeromq.org/

https://github.com/zeromq/libzmq

There is a libzmq3-dev package in Ubuntu 18.04 repository.

https://packages.ubuntu.com/bionic/libzmq3-dev

This will be obvious developers choice, for ease of installation and usage.

Communication interface

Bitcoin provides RPC interface we can use to read additional transaction info from a node.

Depending on how we configure the listener, we will need more or less functionalities in this component.

In general, we will need:

Communication interface can be implemented as a HTTP client, which will send requests to a bitcoin node.

Event Handler

Event handler is a working horse of sidechain handler. It will receive input from a listener, and process it any way needed, in order to get the minimum required information about event of interest, namely transactions to the addresses we are interested in.

In general, event handler needs:

  • Access to the list of addresses we are interested in

Suggested implementation

  • sidechain_manager is a class implementing Sidechain Manager

    • Introduce a new element of network enum, to indicate that we want to use bitcoin network

    • namespace graphene { namespace peerplays_sidechain {

    • enum networks {

    • bitcoin

    • ...

    • };

    • class sidechain_net_manager {

...

    • Extend factory method to create the instance of sidechain_handler_bitcoin

    • bool sidechain_net_manager::create_handler(peerplays_sidechain::networks network, const boost::program_options::variables_map& options) {

    • ...

    • switch (network) {

    • case networks::bitcoin: {

    • std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(options));

    • net_handlers.push_back(std::move(h));

    • ret_val = true;

    • }

    ...

  • Implement sidechain_handler_bitcoin class, inheriting sidechain_handler, implementing sidechain handler for Bitcoin

  • Implement zmq_listener, using ZeroMQ library

    • subsribe to hashblock or raw transaction

    • socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 0 );

    • socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) );

      • or

      • socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 0 );

    socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) );

  • Implement bitcoin_rpc_client class, HTTP client used to communicate with Bitcoin node

    • Implement, at least, following methods:

      • receive_full_block - to retrieve full block by block hash

      • receive_transaction - to retrieve transaction info by transaction hash

      • receive_block_as_json - to retrieve full block as json by block hash

      • decode_transaction - to decode raw bitcoin trasnaction

      • receive_estimated_fee - to retrieve transaction cost (transaction fee)

      • send_btc_tx - to send signed transaction to the bitcon node

  • Implement any additional code needed, for receiving event data, collecting any additional information about event of interest, with the final result as a data structure containing the following info:

  • struct sidechain_transaction_info {

  • graphene::peerplays_sidechain::networks network; // set to network::bitcoin

  • std::string source;

  • std::string destination;

  • uint64_t amount;

  • uint64_t transaction_fee;

}

    • Identifier of a sidechain handler (network::bitcoin)

    • Transaction’s source address (as string, representing sidechain native address)

    • Transaction’s destination address (as string, representing sidechain native address)

    • Transaction’s amount (as unsigned integer, in sidechain native currency)

    • Estimated transaction fee (as unsigned integer, in sidechain native currency)

Last updated