XVersionMessage: BCH node extended version and configuration fields
Authors: Awemany, Griffith
version message in the Bitcoin protocol (for more details,
peers announce their capabilities and preferences to each other. The
version message is limited in scope to a fixed set of well-known
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.
version message can not be extended as some
clients might consider an oversized
version message illegal.
For this reason, an additional message type named
specified here in. The
xversion message transports a generic
key-value map that is meant to hold the configuration and version
As a general reminder, it should be noted that the values given in
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 for
xversion in all specs prior to 0.1.0
xversion. In 0.1.0 the message envelope command changes to
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).
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.
xmap is encoded using Bitcoin's usual network de-/serialization
schemes while using
COMPACTSIZE size 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
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>]
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
[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
with duplicate keys that are later in the message override
values that are that are closer to the start of the message.
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.
Implementations should allow extra bytes following the defined fields
xversion message to allow for further extensibility as long
as the 100KB size constraint is not exceeded.
Handling and sequencing of
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
verack message to confirm recept.
A node signals that it is using xversion by setting service bit 11.
xversion is enabled the version handshake should be
xversion is not enabled the
handshake should be
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
Interpretation of the
Xmap keys are 64-bit in size. If a key cannot be found in the
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
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
u64c is specified.
Predefined value types
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
For example, a value of
0x1000 (4096) is encoded like this as the
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
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.
A directory that lists the currently defined key values comes along
with this specification and can be found in the file named
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
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
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
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
In the Bitcoin Unlimited reference implementation, the
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.cpp relative to the source root directory.