For shared immutable key–value and time series databases

MultiChain streams enable a blockchain to be used as a general purpose append-only database, with the blockchain providing timestamping, notarization and immutability. A MultiChain blockchain can contain any number of streams, where the data published in every stream is stored by every node. If a node chooses to subscribe to a stream, it will index that stream’s contents to enable efficient retrieval in various ways.

Each stream is an ordered list of items, in which each item has the following characteristics:

  • One or more publishers who have digitally signed that item.
  • A key between 0 and 256 bytes in length.
  • Some data, which can reach many megabytes in size.
  • Information about the item’s transaction and block, including its txid, blockhash, blocktime, and so on.

Like native assets, MultiChain streams can be referred to in any of three ways:

  • An optional stream name, chosen at the time of stream creation. If used, the name must be unique on a blockchain, between both assets and streams. Stream names are stored as UTF-8 encoded strings up to 32 bytes in size and are case insensitive.
  • A createtxid, containing the txid of the transaction in which the stream was created.
  • A streamref which encodes the block number and byte offset of the stream creation transaction, along with the first two bytes of its txid.

If root-stream-name in the blockchain parameters is a non-empty string, it defines a stream which is created with the blockchain and can be written to immediately. The root stream’s createtxid is the txid of the coinbase of the genesis block, and its streamref is 0-0-0.

For more background, please see the blog post on streams. If you are looking for documentation on quasi streams (deprecated), click here.

Permissions in streams

Streams are created by a special transaction output, which must only be signed by addresses which have the create permission (unless anyone-can-create is true in the blockchain parameters). This is easy to do using the create command in multichain-cli or the JSON-RPC API. The stream’s creator automatically receives admin, activate and write permissions for that stream. It is not possible to create more than one stream in a single transaction, or to combine stream creation with initial or follow-on asset issuance.

Because a stream’s data is stored by every node on a blockchain, streams cannot have effective read permissions. (Even if these were implemented at the level of MultiChain’s API, the stream data could still be read directly from each node’s disk drive.) As a result, read permissions in a stream should be managed through application-level encryption of each item’s data, with decryption keys distributed as appropriate.

Each stream item is encoded in a single transaction output (see below), which is easy to create using the publish command. If raw transactions are used, a single transaction can write to multiple streams atomically using multiple outputs, but it cannot write more than one item to a particular stream. The publishers of a stream item are defined by the addresses used in the inputs which signed the output containing that item. If an input spends a pay-to-scripthash (P2SH) multisig output, the P2SH address is considered as the item publisher, independent of the actual public keys used in the input.

When a stream is created, it is either open or closed. Any address can publish items into an open stream, subject to the usual restriction that the address used in each input of a transaction must have global send permissions. In closed streams, every publisher of a stream item must have write permission for that stream, or the item and its transaction are not valid. The write permission for an address can be modified in a permissions transaction signed by an address with per-stream admin or activate permissions, while admin or activate permissions can be changed by those with per-stream admin permissions only.

For the root stream, the creator of the chain’s first genesis block automatically receives admin, activate and write permissions. The root stream is open for general writing if the root-stream-open blockchain parameter is true.

Streams in transaction data

For regular use of MultiChain, you can ignore the technical details below, which describe MultiChain protocol 10007 or later (see deprecated protocols for details of earlier versions). They are only relevant if you want to work with the raw data within MultiChain transactions. Note that you can also use the raw transactions APIs to encode and decode this information.

Stream creation outputs

A transaction output creates a stream if it contains the following, followed by an OP_DROP (0x75) and OP_RETURN (0x6a):

Field Size Description
Identifier 4 bytes spkn or 0x73 0x70 0x6b 0x6e
Type 1 byte 0x02 for a stream.
Repeat the below for each stream property
Property key Variable If the first byte of the key is 0x00, it denotes a property with special meaning to MultiChain, and the second byte gives the property type. For now, the only possible keys are 0x00 0x01, for the stream’s name, and 0x00 0x04, where a property value of 0x00 or 0x01 denotes whether the stream is open to all writers. If the first byte of the property key is not 0x00, it contains the null-delimited name of a user-defined custom field, e.g. 0x75 0x72 0x6c 0x00 for url.
Length 1-9 bytes Bitcoin-style variable-length integer indicating the length of the property value in bytes.
Value Variable The property’s value as raw binary.
Stream item outputs

A transaction output contains a stream item if it has exactly the following structure:

stream-identifier OP_DROP item-key OP_DROP OP_RETURN item-data

The stream-identifier has the following structure:

Field Size Description
Prefix 4 bytes spke or 0x73 0x70 0x6b 0x65
Stream 16 bytes First 16 bytes of stream creation txid in reverse order.

The item-key has the following structure:

Field Size Description
Prefix 4 bytes spkk or 0x73 0x70 0x6b 0x6b
Key data Variable Binary data of item key (can be empty).

The item-data has no prefix and is embedded directly after the OP_RETURN.