Transport

Overview

Raiden is a network agnostic protocol. Protocol messages can in general be transferred over any network, e.g. ip, mesh networks, etc., where the only requirement is access to an Ethereum node. For efficient and reliable messaging, the reference implementation of Raiden currently only supports Matrix, an open standard for peer-to-peer messaging based on a server federation.

Requirements

  • Unicast Messages
  • Broadcast Messages
  • E2E encryption for unicast messages
  • Authentication (i.e. messages should be linkable to an Ethereum account)
  • Low latency (~100ms)
  • Scalability
  • Spam protection / Sybil Attack resistance
  • Decentralization (no single point of failure / censorship resistance)
  • Off the shelf solution, well maintained
  • JS + Python SDK
  • Open Source / Open Protocol

Current Solution: Federation of Matrix Homeservers

https://matrix.org/docs/guides/faq.html

Matrix is a federated open source messaging system, which supports group communication (multicast) via chat rooms. Direct messages are modeled as 2 participants in a private chat room. Homeservers can be extended with custom logic (application services, password providers) e.g. to enforce certain rules (or message formats) in a room. It provides JS and Python bindings and communication is done via REST API and HTTP long polling.

Use in Raiden

Identity

The identity verification MUST not be tied to Matrix identities. Even though Matrix provides an identity system, it is a possible central point of failure. All state-changing messages passed between participants MUST be signed using the private key of the ethereum account, using Matrix only as a transport layer.

The messages MUST be validated using ecrecover by receiving parties.

The conventions below provide the means for the discovery process, and affect only the transport layer (thus not tying the whole stack to Matrix).

Authentication

A Matrix userId is required to be of the form @<eth-address>:<homeserver-uri>, an @, followed by the lowercased 0x prefixed ethereum address of the node and the homeserver uri, separated from the username by a colon.

To prevent malicious name squatting all Matrix servers joining the Raiden federation must enforce the following rules:

  1. Account registration must be disabled
  2. A password provider that ensures only users in control of the private key corresponding to their node address can log in. This is done by using an ec-recoverable signature of the server name the Raiden node is connecting to (without any protocol prefix) as the password. The password provider must verify the following:
    1. The user-id matches the format described above.
    2. The homeserver_uri part of the user-id matches the local hostname.
    3. The password is a valid 0x prefixed, hex encoded ec-recoverable signature of the local hostname.
    4. The recovered address matches the eth-address part of the user-id.
  3. Every Raiden node must set it’s Matrix displayName to a 0x prefixed hex encoded ec-recoverable signature of their complete user-id.

Example:

username = web3.eth.defaultAccount  # 0-left-padded
# 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
password = web3.eth.sign(server_uri)
matrix.login_with_password(username, password)
userid = "@" + username + ":" + server_uri
matrix.get_user(userid).set_display_name(web3.eth.sign(userid))

Discovery

All Raiden nodes must join a globally federated discovery room to allow for server-side user directory searches. The name of the discovery room is of the form raiden_<network_name_or_id>_discovery (e.g. raiden_mainnet_discovery).

User search results need to be checked by validating their displayName signature.

The discovery rooms are not intended to exchange messages in. Clients should use the server-side filtering mechanism to exclude message data of the discovery rooms.

Presence

Matrix allows to check for the presence of a user. Clients should listen for changes in presence status of users of interest (e.g. peers), and update user status if required (e.g. gone offline), which will allow the Raiden node to avoid trying to use this user e.g. for mediating transfers.

Sending transfer messages to other nodes

There are three different ways to send messages between Raiden nodes:

  • Messages in matrix chat rooms
  • Matrix toDevice messages
  • Messages over WebRTC channels

Of those, only the matrix rooms based messages are a requirement, while the other options are pure optimizations that reduce latency.

Matrix rooms

Private channel rooms have random room_ids in the format !<random_room_id>:<homeserver-uri>, where the random room_id is generated by the server. To avoid race conditions only the node with the lexicographically lower address will create rooms and invite peers.

Since users may roam to other homeservers, participants should keep listening for user-join events in the discovery room. When a new user for an address of interest joins it should be invited into a communications room.

Matrix toDevice messages

Matrix also supports toDevice messages. These are not stored permanently as part of a shared communication history and are delivered exactly once to each client device.

As Raiden does not rely on the messaging history, this is a valid alternative and usually shows lower latency and reduces load on the Matrix federation nodes.

Nodes that support toDevice messages signal that with the toDevice capability. They are also expected to set their deviceId to "RAIDEN", so that clients sending toDevice messages can specify RAIDEN as the target deviceId.

WebRTC messaging

To further optimize the communication, exchanging messages in a peer-to-peer manner is possible with WebRTC. In that case matrix is only used to initiate the WebRTC connection (either via private rooms, or toDevice messages).

Nodes that support WebRTC messages signal that with the webRTC capability.

Chat Rooms

Peer discovery room

One per network. Participants can discover peers willing to open more channels. It may be implemented in the future as one presence/peer discovery room per token network, but it’d complicate the room-ownership/creation/server problem (rooms need to belong to a server. Whose server? Who created it? Who has admin rights for it?).

Monitoring Service Updates Room

Raiden nodes can submit a MonitoringRequest to the Monitoring Service room when they go offline. The Monitoring Service will submit their balance proof on their behalf.

Pathfinding Service Updates Room

Raiden Nodes publish PFSCapacityUpdate and PFSFeeUpdate to the Pathfinding Service room. The Pathfinding Service can compute efficient routes throughout the network and provide these routes to requesting nodes.

Capabilities

Raiden clients need a way to signal their capabilities to other nodes. This is done by encoding the capabilities in the avatar_url field of the user profile.

Serialization for use in avatar_url

The following template is used to encode the capabilities in the avatar URL field:

mxc://raiden.network/cap?{capabilities_url_encoded}

Here {capabilities_url_encoded} is the url query parameter encoding of the capabilities.

Rules for url encoding:

  • boolean values are encoded as truthy values, e.g. "0" and "1"
  • other values are encoded as strings
  • lists of values are allowed

Deserialization

The final interpretation of capability values is up to the receiving client, or rather the specified capability. It’s expected that clients use truthiness of the supplied value when decoding boolean values.

Handling of unknown values

  • Intentionally omitting (falsy or whatever) known default values is discouraged. Client implementations are asked to explicitly state all known capabilities.
  • Client implementations have to deal with receiving new/unknown capabilities gracefully, i.e. they should expect the peer to act backwards compatible.
  • Client implementations have to deal with not receiving known capabilities gracefully, i.e. assume the peer implementation is going to exert legacy behavior and therefore act backwards compatible.

Example

avatar_url = "mxc://raiden.network/cap?Delivered=0&Mediate=1&Receive=1&webRTC=1&list_capability=one&list_capability=two"
capabilities_decoded = {
    'Delivered': False,
    'Mediate': True,
    'Receive': True,
    'webRTC': True,
    'list_capability': ['one', 'two']
}