Skip to content

XVersionMessage: BCH node extended version and configuration fields

DRAFT specification

Version: 0.1.0

Authors: Awemany, Griffith

Overview

Using the version message in the Bitcoin protocol (for more details, see https://en.bitcoin.it/wiki/Protocol_documentation#version), peers announce their capabilities and preferences to each other. The version message is limited in scope to a fixed set of well-known fields.

It is deemed desirable to be able to announce further configuration and version information to peers. For example, this can then be used to version advanced block transmission protocols like Graphene.

Notably, the version message can not be extended as some clients might consider an oversized version message illegal.

For this reason, an additional message type named xversion is specified here in. The xversion message transports a generic key-value map that is meant to hold the configuration and version parameters.

As a general reminder, it should be noted that the values given in this extended xversion version and configuration map are, except for basic type checks and mappings, not in any way validated and a node can very easily lie about them. Care should be taken that any implementation depending for its configuration on the values in the mapping will properly account for this fact and ban or otherwise penalize nodes that do not conform with their advertised behavior.

Encoding of the xversion message type

The message envelope command[1] for xversion in all specs prior to 0.1.0 was xversion. In 0.1.0 the message envelope command changes to extversion. This change is necessary to allow for backwards compatibility with peers following the older specifications due to a change in the version handshake between spec versions 0.0.3 and 0.1.0 (see below).

The xversion message contains a single compound field which is a serialized key-value map henceforth named xmap that maps 64-bit integer values to variable-sized byte vectors. The message itself is encoded in the standard Bitcoin message frame[2].

The xmap is encoded using Bitcoin's usual network de-/serialization schemes while using COMPACTSIZE size[3] encodings whenever possible. Note that this is different from using the default encoding you would get when serializing through the std::map serializer (which is commonly found in serialize.h in most current C++ implementations based on the original Satoshi code).

In particular the xmap payload of an xversion message looks like this for N entries in the map:

[compact-size N]
[compact-size key0] [std::vector<uint8_t> default-encoding value0]
[compact-size key1] [std::vector<uint8_t> default-encoding value1]
...
[compact-size key<N-1>] [std::vector<uint8_t> default-encoding value<N-1>]

where std::vector<uint8_t> default-encoding is the default encoding one gets when serializing a std::vector<uint8_t> of size M bytes through the serialization templates usually found in serialize.h:

[compact-size M]
[uint8_t value0]
...
[uint8_t value<M-1>]

A node may serialize the xmap it wants to announce to its peers with any order of the key-value pairs. When receiving an xmap, entries with duplicate keys that are later in the message override values that are that are closer to the start of the message.

Size limit

The serialized size of the xversion payload has to be of size 100000 bytes (100KB, not 100KiB !) or less. Messages that are larger than this are illegal.

Later extensions

Implementations should allow extra bytes following the defined fields in the xversion message to allow for further extensibility as long as the 100KB size constraint is not exceeded.

Handling and sequencing of xversion messages

A node should expect an xversion message to arrive after the version message. Only a single xversion message should arrive during the lifetime of a connection and receipt of multiple such messages should be considered misbehavior of the remote peer.

After receipt of an xversion message, a node must answer with an empty verack message to confirm receipt.

A node signals that it is using xversion by setting service bit 11.

When xversion is enabled the version handshake should be version xversion verack. When xversion is not enabled the handshake should be version verack.

An empty xmap for a peer has a defined reading (see below). However, to simplify node implementations, it is deemed acceptable to enable certain protocol features only after proper receipt of a corresponding xversion message.

Interpretation of the xmap

Xmap keys are 64-bit in size. If a key cannot be found in the xmap, its value must be assumed to be an empty byte vector.

The value is a vector of bytes. These bytes can be an object that is itself serialized, but MUST exist within the vector "envelope" so that implementations that do not recognize a field can skip it. The serialization format of the bytes inside the "envelope" is defined by the creator of the key, however, Bitcoin P2P network serialization is recommended since it is also used to encode/decode most of the other the messages in the Bitcoin protocol.

An entry that has unknown meaning is to be ignored.

For obvious reasons, the enumeration of entries and their expected types in the map should be agreed upon between implementations. For this reason, a separate specification that lists configuration and version keys is introduced, named the xmap directory.

The format of std::vector<uint8_t> is unwieldy for configuration and version parameters that could be expressed as simple integer to integer mappings. For this reason, a specific interpretation for certain value types is assumed. The list of these value types is expected to grow over time and this document correspondingly extended. For now, only a compact-size encoded unsigned 64-bit integer value named u64c is specified.

Predefined value types

The u64c value type

Values in the xmap can be interpreted as compact-size encoded 64-bit unsigned integers (named u64c), allowing to use it as a simple unsigned integer to unsigned integer map.

A value of u64c type is encoded in Bitcoin's message serialization as a COMPACTSIZE integer within the generic byte vector value of the xmap. For example, a value of 0x1000 (4096) is encoded like this as the xmap value: FD 00 10

Which gets further encoded in the xmap encoding itself as: 03 FD 00 10

A special exception is added for empty value vectors. Empty value vectors are to be interpreted as a missing value. The interpretation and handling of missing expected values in the xmap are left up to the receiver.

When interpreting these uint64_t values from the table and certain bits of the value are unused for a given key, the implementer should mask out the needed bits using an AND-mask to allow for future use of the yet unused bits in a given value.

If decoding as a compact size integer fails, a decode failure should be emitted to be handled at a higher level.

The xmap directory

A directory that lists the currently defined key values comes along with this specification and can be found in the file named xversionkeys.h.

Keys are 64-bit in size. They consist of a 32-bit prefix (bits 32..63) followed by a 32-bit suffix (0..31).

Different prefixes are meant for different implementations so that each implementation can extend the version map without needing frequent synchronization with other implementations and while avoiding double-assignments. Of course, regular integration into a common directory should take place. The specifics of this and the exact, detailed interpretation of the fields in the xmap will obviously rely on external specifications that cannot ever be the scope of this document.

Implementation prefixes

The list of implementations prefixes can be found here: https://reference.cash/protocol/p2p/xversionkeys/

An implementation not listed here, but wanting to extend the xversion map can pick an unused prefix but is strongly suggested to communicate the choice with the rest of the teams as early as possible. It is also strongly suggested to communicate with the rest of the teams before removing an implementation from this list.

Versioning of the xversion message itself use the 0x00000000 prefix and the 0x00000000 suffix for the key. The value should reflect what version of the spec the client is following and use the following formula:

(10000 * major) + (100 * minor) + (1 * revision)

For example: spec version 0.1.0 should have a value of 100.

Experimental or temporary features use the 0x00000000 prefix and a non zero suffix as a key.

Notes on implementation details

Bitcoin Unlimited

In the Bitcoin Unlimited reference implementation, the xversion message is handled using the CXVersionMessage class. The actual xmap is serialized and deserialized using the CompactMapSerialization adapter class. To avoid attacks that could cause a slow-down of key lookup in the xmap tables, the table is internally using a salted siphash to map the keys. The implementation can be found in the files src/xversionmessage.h and src/xversionmessage.cpp relative to the source root directory.

References