BOLD #2: Peer Protocol

Overview

The current protocol requires a direct connection between two nodes for performing updates, trades, and swaps. This section describes how the connection is set up.

Each node maintains a persistent secp256k1 private key with a corresponding public key, node key for short, which uniquely identifies the node in the network. We recommend only allowing manual resets of the private key, for example by deleting a file or database entry.

An initial handshake is required to establish a secure TCP-based session between two nodes. The default listening TCP port is 8885 (XU in ASCII).

Handshake Protocol

The communication session is established by creating a TCP connection and agreeing on ephemeral key material for further encrypted communication, in addition to utilizing the persistent key for authentication. The process of establishing this session is the “handshake” and is carried out between the “initiator” (the peer that opened the TCP connection) and the “recipient” (the peer that accepted it).

The handshake consists of each side sending the SessionInit message, and waiting to receive the SessionAck message back. The first SessionInit message is expected to be sent by the initiator.

The initiator must know the recipient's identity (node key) in advance. The recipient learns the initiator's identity by receiving the SessionInit message.

By the end of the handshake, two distinct shared keys are created, one for each side of the communication, to be used to encrypt all messages during the session's lifetime.

Handshake Messages

These messages are used in the initial handshake:

SessionInit Message (0x00)

`string id = 1`
The message's globally unique identifier, generated by the sender
`string sign = 2`
secp256k1 signature over sha256 hash of a JSON-serialized msg containing fields 3-7
`string peer_pub_key = 3`
The target node secp256k1 public key (in hex)
`string ephemeral_pub_key = 4`
An ephemeral secp256k1 public key (in hex), generated by the sender, for ECDH key exchange
`NodeState node_state = 5`
General info regarding the sender's current node state
`string version = 6`
OpenDEX client version
`string node_pub_key = 7`
The sender's secp256k1 public key (in hex)

Once received by the destination node, the message is authenticated as follows:

  • peer_pub_key should match the destination node's public key

  • node_pub_key should match the sender node expected public key (relevant for the initiator node only since he already knows the recipient node's identity)

  • sign should be a valid secp256k1 signature over the sha256 hash of a JSON-serialized msg containing fields 3-7

If the fields of the SessionInit message are valid, the receiver replies with a SessionAck message. If a SessionAck message is not received within a reasonable time frame (10 seconds is recommended), the sender may disconnect.

SessionAck Message (0x01)

`string id = 1`
The message's globally unique identifier, generated by the sender
`string req_id = 2`
The id of the received SessionInit message
`string ephemeral_pub_key = 3`
An ephemeral secp256k1 public key (in hex), generated by the sender, for ECDH key exchange

Once the receiver of the SessionInit message (Bob) has generated his ECDH keys, he can calculate the shared key by using the ephemeral_pub_key from the SessionInit message. Once the SessionAck message is received by the sender of the SessionInit message (Alice), she can compute the shared key as well. All future communication from Alice to Bob must be encrypted with aes-256-cbc symmetric encryption using the shared key.

Coordination Messages

These messages are used to maintain the P2P overlay after a session has been established via the initial handshake.

Ping Message (0x04)

`string id = 1`
The message's globally unique identifier, generated by the sender

In order to allow long-lived TCP connections, both ends keep the TCP connection alive at the application level using Ping and Pong messages.

It is recommended to send a Ping message every 30 seconds.

The sender of a Ping message may disconnect if a Pong message is not received within 10 seconds.

Pong Message (0x05)

`string id = 1`
The message's globally unique identifier, generated by the sender
`string req_id = 2`
The id of the received Ping message

The Pong message is sent in response to the Ping message. It serves to keep the connection alive by explicitly notifying the other end that the receiver is still active.

Disconnecting Message (0x03)

`string id = 1`
The message's unique identifier, generated by the sender
`uint32 reason = 2`
The reason for the imminent disconnection
`string payload = 3`
Optional payload to specify the disconnection reason

The Disconnecting message is used to inform a connected peer that a disconnection is imminent and that the peer should disconnect immediately. A well-behaved host that sends a Disconnecting message allows the peer at least 2 seconds to disconnect before disconnecting itself.

reason is an optional parameter for specifying one of the following reasons for the disconnection:

Reason

Meaning

0x01

Response stalling

0x02

Incompatible client protocol version

0x03

Unexpected identity

0x04

Forbidden identity update

0x05

Connected to self

0x06

Not accepting new connections

0x07

Banned

0x08

Already connected

0x09

Shutdown

0x0a

Malformed version

0x0b

Authentication failure: invalid target node

0x0c

Authentication failure: invalid signature

0x0d

Wire protocol error

GetNodes Message (0x0a)

`string id = 1`
Message's globally unique identifier, generated by the sender

The GetNodes message is used to query a peer for its list of known, reachable OpenDEX nodes.

Nodes Message (0x0b)

`string id = 1`
Message's globally unique identifier, generated by the sender
`string req_id = 2`
Link to the id field from the received GetNodes message
`repeated Node nodes = 2`
The list of known nodes

The Nodes message is used to respond to the GetNodes message.

NodeStateUpdate Message (0x02)

`string id = 1`
Message's globally unique identifier, generated by the sender
`NodeState node_state = 2`
The updated node state.

The NodeStateUpdate message is used to tell a peer about a change in the node state. An example of an update is the removal or addition of a supported trading pair.

Custom types

NodeState type

`repeated Address addresses = 1`
The sender's listening TCP addresses
`repeated string pairs = 2`
The sender's list of trading pair symbols, constructed with the base currency first, followed by a '/' separator and the quote currency (e.g., [“LTC/BTC”, “DAI/BTC”])
`string connext_identifier = 3`
The sender's Connext identifier (e.g. `indra123abc`)
`map<string, string> lnd_pub_keys = 4`
The sender's list of LND public keys
`map<string, string> token_identifiers = 5`
Mapping between currency symbols and chain identifiers or ETH-ERC20 token contract addresses (e.g., { BTC: 'bitcoin-mainnet', LTC: 'litecoin-mainnet', ETH:,'0x0000000000000000000000000000000000000000' })
`map<string, LndUris> lnd_uris = 6`
Mapping between currency symbols to LND listening URIs (should be reachable from the internet e.g., { BTC: ['2w526cyown43ovsvsojdowheqmukbykrexzyccp6v6j4pm5ve3hjzrid.onion:9735'], LTC: '['lndltc.kilrau.com:9735', 'qiyibtczmuhutusmygvc2injxl7v4yfcodwj3pft63edycud5gr3giad.onion:10735']' })

Address type

`string host = 1`
`uint32 port = 2`

LndUris type

`repeated string lnd_uri = 1`

Node type

`string node_pub_key = 1`
The node's public key upon which its identity should be verified
`repeated Address addresses = 2`
The node's listening TCP addresses