Skip to content

Block Service

James Chiang edited this page Aug 20, 2018 · 3 revisions

The Libbitcoin Server provides a dedicated service which publishes each new valid block extending the strong chain received by the Bitcoin node on the p2p network.

The ZMQ socket connection setup for subscribing to this service follows that of the heartbeat service.

Block Service Messages

Published block service messages consist of 3 message frames. The sequence and block height are little endian encoded, and the block data encoded in the over-the-wire block format.

[----- 2-byte sequence -----]  
[------ 4-byte height ------]
[-- serialised block data --]

Note: The sequence increments for each published block message, and wraps around once the maximum value is reached. Missing sequence values may indicate reconnects at the transport level, or dropped messages at the publisher or subscriber socket if the high water mark is reached on either side.

Example: Parsing Newly Received Blocks

In the following example, we will subscribe to newly received blocks, and extract the miner coin base text data.

We subscribe with a ZMQ SUB socket ⑴, which in the bc::protocol::zmq library is initialised with an empty filter and will not filter any messages sent by the publisher.

The subscribing socket is connected to the publishing endpoint ⑵, and subsequently can begin receiving ⑶ messages. Dropped messages on either publisher or subscriber side can be observed in gaps between received sequence ⑷ values. Given the slow rate of new block messages, dropped messages are likely due to the underlying network.

The block subscription message payloads include a 4-byte height message frame ⑸ and a message frame containing the block data ⑹ serialised in the Bitcoin over-the-wire format. The Libbitcoin-system bc::chain::block type provides methods to parse the block data, with which we can then extract the coinbase input script ⑺ from each newly received block.

The coinbase input script is structured as follows:

[1-byte prefix][4-byte blockheight datapush][miner data...]

Extracting the miner data ⑻ is simply a question of extracting data beginning with the fifth byte from the coinbase script, and parsing it for printable ascii characters.

#include <iostream>
#include <bitcoin/protocol.hpp> // Version 3.
#include <bitcoin/bitcoin.hpp>  // Version 3.

int main() {

    bc::protocol::zmq::context my_context(true);

    // (1)
    bc::protocol::zmq::socket my_subscriber(
        my_context,
        bc::protocol::zmq::socket::role::subscriber);

    // (2)
    bc::code ec;
    bc::config::endpoint public_endpoint(
        "tcp://testnet2.libbitcoin.net:19093");
    ec = my_subscriber.connect(public_endpoint);


    while (true)
    {
        // (3)
        bc::protocol::zmq::message block_message;
        block_message.receive(my_subscriber);

        // (4)
        bc::data_chunk sequence_chunk;
        block_message.dequeue(sequence_chunk);
        uint16_t sequence = sequence_chunk[0] | (sequence_chunk[1] << 8);

        // (5)
        bc::data_chunk height_chunk;
        block_message.dequeue(height_chunk);
        uint32_t height = height_chunk[0] | (height_chunk[1] << 8) |
            (height_chunk[2] << 16) | (height_chunk[3] << 24);

        // (6)
        bc::data_chunk published_block_chunk;
        block_message.dequeue(published_block_chunk);
        bc::chain::block published_block;
        published_block.from_data(published_block_chunk, true);

        // (7)
        auto coinbase_input_script_data = published_block.transactions()[0]
            .inputs()[0].script().to_data(true); // true: incl. script prefix

        // (8)
        std::string coinbase_miner_string;
        for (int i = 5; i < coinbase_input_script_data.size(); i++)
        {
            if((coinbase_input_script_data[i] > 31) &&
                (coinbase_input_script_data[i] < 127))
            {
                coinbase_miner_string += coinbase_input_script_data[i];
            }
            else
            {
                coinbase_miner_string += "?";
            }
        }

        std::cout << "--------------------" << std::endl;
        std::cout << "[" << sequence << "]" << std::endl;
        std::cout << "Blockheight: " << height << std::endl;
        std::cout << "Coinbase Miner Text: " << std::endl;
        std::cout << coinbase_miner_string << std::endl;

    }

    return 0;
}

Console output: (likely won't output immediately due to block intervals)

--------------------
[58401]
Blockheight: 1384608
Coinbase Miner Text:
/???????? Mr. Mo/
--------------------
[58402]
Blockheight: 1384609
Coinbase Miner Text:
/???????? Mr. Mo/
Clone this wiki locally