Build your own adapter for reading MultiChain Enterprise feed files

MultiChain feeds are real-time on-disk binary logs of the events relating to one or more blockchain streams, or the blocks in a chain. They are designed for reading by external processes, and can be used to keep an external database synchronized with the state of the chain. MultiChain feeds are available in MultiChain 2.0 Enterprise, a demo of which is available for free download.

The open source MultiChain Feed Adapter, written in Python, can read a feed and automatically replicate its contents to several popular databases. For most purposes, we recommended extending or modifying this adapter, which includes everything you need to parse feed files correctly.

However some developers may wish to create their own solution for reading feed files in a different programming language. The information below explains the format of the files, which are designed for efficient reading as binary data. The files use a simple unified structure based mostly on 1-byte identifiers and small-endian 32-bit integers.

Overall File Structure

Files in a feed directory are named consecutively in the form feedXXXXXX.dat where XXXXXX is a six-digit integer with leading zeros. The first file is named feed000000.dat and each file ends with a record indicating the number of the next file to move to. The feed moves on to the next file when the current file reaches 128 MB in size or after 24 hours – whichever is sooner. Files are append-only, and it is up to the parser to keep track of the current file number and position.

Each file contains one or more records, and each record contains one or more fields. Most records and fields relating to blockchain events are optional, and their presence will depend on the feed’s options as set through the JSON-RPC API commands addtofeed and updatefeed.

Each record in a file takes the following form:

Field Size Description
Record identifier 1 byte Identifier for type of record to follow.
Record size 4 bytes Byte length of remainder of record, as a 32-bit small-endian integer.
Repeat the below for each field in the record
Field identifier 1 byte Identifier for type of field to follow.
Field size 4 bytes Byte length of field data, as a 32-bit small-endian integer. This is included even if the field length is fixed.
Field data Variable Appropriate data for the field identifier.

Parsing a Feed File

Each feed file begins with a Previous File record (starting 0x04). After that, records are written in batches, where each batch is bracketed by a Batch Start and Batch End record.

As it proceeds through the file, the parser must wait until it sees a 0x01 byte indicating a Batch Start record before proceeding to read until the end of that batch. If the next byte does not indicate a Batch Start record, the parser should wait, because the next batch has not yet been written.

Below is a description of each of the possible record types that can appear in a feed file, along with the fields that can appear within the record. The title of each section shows the 1-byte record identifier. Any records or fields with unknown identifiers should be safely ignored. If the size of a field indicated in the file does not match the size in the tables below, the field can also be ignored.

Previous File – 0x04

This record appears at the start of each feed file, with the following fields:

Field identifier Size Description Required?
0x01 4 bytes Byte length of previous feed file, as a 32-bit small-endian integer. Yes
0x02 4 bytes Number of previous feed file, as a 32-bit small-endian integer. Yes
0x05 4 bytes File creation time, as Unix timestamp in small-endian order. Yes

Batch Start and Incomplete Batch – 0x01 and 0x03

One of these records appears at the start of a batch of records that were written to a file in a single write operation, with the following field:

Field identifier Size Description Required?
0x01 4 bytes Byte length of batch, including this record and the Batch End record, as 32-bit small-endian integer. This is optimized for forward scanning – the next batch (if it exists) starts this many bytes after the current one. Yes

When writing a batch to a feed file, MultiChain writes the batch prefixed by an Incomplete Batch record with identifier 0x03, then flushes the write operation to disk and updates its internal pointer. The Incomplete Batch record is then converted to a Start Batch record by changing its identifier to 0x01, meaning that the batch is ready to parse.

Batch End – 0x02

This record appears at the end of a batch of records that were written to a file in a single operation, with the following fields:

Field identifier Size Description Required?
0x01 4 bytes Byte length of batch, including the Batch Start record but not this Batch End record, as a 32-bit small-endian integer. This is optimized for backward scanning – the batch started this many bytes back. Yes
0x02 4 bytes If this is the last batch in this file, the number of the next feed file, as a 32-bit small-endian integer. No

Block Add Start and Block Add End – 0x26 and 0x27

These records are written before and after attaching a new valid block to the end of the chain, and can contain the following fields:

Field identifier Size Description Required?
0x20 4 bytes Height of block, as a 32-bit small-endian integer. No
0x21 32 bytes Hash of block, with bytes ordered to match JSON-RPC API (not network protocol). Yes
0x22 4 bytes Number of transactions in block, as a 32-bit small-endian integer. No
0x23 4 bytes Confirmation time of block, as Unix timestamp in small-endian order. No
0x24 Variable Address of the block miner, as a regular base58-encoded string. No
0x25 4 bytes Byte size of block, as a 32-bit small-endian integer. No

Block Remove Start and Block Remove End – 0x23 and 0x24

These records are written before and after detaching a block from the end of the chain, due to a (rare) blockchain reorganization. They can contain the following fields:

Field identifier Size Description Required?
0x20 4 bytes Height of block, as a 32-bit small-endian integer. No
0x21 32 bytes Hash of block, with bytes ordered to match JSON-RPC API (not network protocol). Yes

Stream Item Received – 0x30

This is the first feed record written for each stream item, with the following fields:

Field identifier Size Description Required?
0x30 20 bytes ID of item in feed, for reference in other feed records. Yes
0x32 Variable Name of stream, as a UTF-8 encoded string. No
0x2A 32 bytes Transaction ID containing item, with bytes ordered to match JSON-RPC API (not network protocol). No
0x2B 4 bytes Number of transaction output (vout) containing item, as a 32-bit small-endian integer. No
0x33 Variable Address of item publisher, as a regular base58-encoded string. This field will appear multiple times if the item has multiple publishers. No
0x34 Variable Item key, as a UTF-8 encoded string. This field will appear multiple times if the item has multiple keys. No
0x35 4 bytes Item format, where 0 means binary, 1 means text, and 2 means JSON. No
0x36 8 bytes Item data size in bytes, as a 64-bit small-endian integer. No
0x37 Variable Data for binary items – see maxshowndata note below. No
0x38 Variable Data for text items in UTF-8 encoding – see maxshowndata note below. No
0x39 Variable Data for JSON items in regular JSON format – see maxshowndata note below. No
0x29 4 bytes Time item was received, as a Unix timestamp in small-endian order. No
0x3B 1 byte Item flags. Current flags are 1 if the item data is available, 2 if the item data is off-chain, and 4 is the item is off-chain and contains multiple chunks. No
0x2C 40 bytes Item dataref, as a binary string – see maxshowndata note below. No

In order to save disk space, if an item’s data as stored in the transaction is longer than the feed’s maxshowndata setting, it will not be included within the feed file. Instead, that data can be retrieved by passing the item’s dataref to the getdatarefdata or datareftobinarycache JSON-RPC APIs. Note also that if an item’s data is off-chain, that data may not yet be available – see the Offchain Data Available record below.

Stream Item Confirmed – 0x31

This record is written when a stream item is confirmed in a block, with the following fields:

Field identifier Size Description Required?
0x30 20 bytes ID of item in feed, for reference in other feed records. Yes
0x32 Variable Name of stream, as a UTF-8 encoded string. No
0x2A 32 bytes Transaction ID containing item, with bytes ordered to match JSON-RPC API (not network protocol). No
0x2B 4 bytes Number of transaction output (vout) containing item, as a 32-bit small-endian integer. No
0x20 4 bytes Height of block, as a 32-bit small-endian integer. No
0x21 32 bytes Hash of block, with bytes ordered to match JSON-RPC API (not network protocol). No
0x23 4 bytes Confirmation time of block, as Unix timestamp in small-endian order. No
0x2D 4 bytes Byte offset of transaction output containing item in block, as a 32-bit small-endian integer. No
0x2C 40 bytes Item dataref, as a binary string. This may be different from a dataref provided for the item in a previous record. If so, both datarefs will work, but the latest one will offer better performance. No

Stream Item Unconfirmed – 0x32

This record is written when the block confirming a stream item is removed, due to a (rare) blockchain reorganization, with the following fields:

Field identifier Size Description Required?
0x30 20 bytes ID of item in feed, for reference in other feed records. Yes
0x32 Variable Name of stream, as a UTF-8 encoded string. No
0x2A 32 bytes Transaction ID containing item, with bytes ordered to match JSON-RPC API (not network protocol). No
0x2B 4 bytes Number of transaction output (vout) containing item, as a 32-bit small-endian integer. No

Stream Item Invalid – 0x33

This record is written when an unconfirmed transaction containing a stream item becomes invalid, so the item should be removed from the stream. This is an unusual and unlikely event – possible causes include a double spend of one of the transaction’s inputs or a revocation of stream write permissions being confirmed before the stream item. The record can contain the same fields as a Stream Item Unconfirmed record, as well as two more below:

Field identifier Size Description Required?
0x10 1 byte Internal code, useful for debugging purposes. No
0x32 Variable Short message explaining cause of invalidity. No

Offchain Data Available – 0x34

This record is written when the data for an off-chain stream item becomes available, with the following fields:

Field identifier Size Description Required?
0x30 20 bytes ID of item in feed, for reference in other feed records. Yes
0x32 Variable Name of stream, as a UTF-8 encoded string. No
0x35 4 bytes Item format, where 0 means binary, 1 means text, and 2 means JSON. No
0x36 8 bytes Item data size in bytes, as a 64-bit small-endian integer. No
0x37 Variable Data for binary items – see maxshowndata note below. No
0x38 Variable Data for text items in UTF-8 encoding – see maxshowndata note below. No
0x39 Variable Data for JSON items in regular JSON format – see maxshowndata note below. No
0x29 4 bytes Time off-chain data was received, as a Unix timestamp in small-endian order. No
0x3B 1 byte Updated item flags. Current flags are 1 if the item data is available, 2 if the item data is off-chain, and 4 is the item is off-chain and contains multiple chunks. No
0x2C 40 bytes Updated item dataref, as a binary string – see maxshowndata note below. This may be different from a dataref provided for the item in a previous record. If so, both datarefs will work, but the latest one will offer better performance. No

As with the Item Received record, if an item’s data as stored in the transaction is longer than the feed’s maxshowndata setting, it will not be included within the feed file. Instead, that data can be retrieved by passing the dataref to the getdatarefdata or datareftobinarycache JSON-RPC APIs.

Offchain Data Purged – 0x35

This record is written when the off-chain data for a stream item is purged from local storage, with the following fields:

Field identifier Size Description Required?
0x30 20 bytes ID of item in feed, for reference in other feed records. Yes
0x32 Variable Name of stream, as a UTF-8 encoded string. No