Messages

Overview

This is the specification of the messages used in the Raiden protocol.

There are data structures which reappear in different messages:

A mediated transfer begins with a LockedTransfer message.

We will explain the assembly of a LockedTransfer message step-by-step below. The further messages within the transfer are based on it:

Further messages in the protocol are:

Encoding, signing and transport

All messages are encoded in a JSON format and sent via the Matrix transport layer.

The encoding used by the transport layer is independent of this specification, as long as the signatures using the data are encoded in the EVM big endian format.

The messages of the protocol can be divided in three groups with different format/hashing/signing conventions:

  • Envelope messages, which contain a balance proof which can be sent to a contract. The balance proof in turn contains an additional hash, which is a hash over the rest of the message. Each envelope message has a defined packed data format to compute the additional hash. The format always starts with the 1-byte command id. Envelope messages are: LockedTransfer, RefundTransfer, Unlock and LockExpired.

  • The second group is messages that will never result in on-chain transactions, as they contain no information that could be forwarded to a contract. There are four types of such messages, which we will call internal messages: SecretRequest, RevealSecret, Processed, Delivered and WithdrawExpired. Internal messages have a packed data format in which they are signed. The format always starts with the message type’s 1-byte command id, but unlike the packing format in envelope messages described above, the command id is followed by a padding of three zero bytes.

  • In addition, there are two withdraw-related messages: WithdrawRequest and WithdrawConfirmation. They have a signature format starting with the token network address, the chain id and a message type constant, which is an unsigned 256-bit integer. The signatures of both messages are used when withdrawing tokens in the TokenNetwork contract.

    Since WithdrawExpired signatures are not used on-chain, they don’t follow this format but the one for internal messages.

Data Structures

Off-chain Balance Proof

Data required by the smart contracts to update the payment channel end of the participant that signed the balance proof. Messages into smart contracts contain a shorter form called On-chain Balance Proof.

The off-chain balance proof consists of the balance data, the channel’s canonical identifier, the signature, the additional hash and a nonce.

The signature must be valid and is defined as:

ecdsa_recoverable(privkey, keccak256(balance_hash || nonce || additional_hash || channel_identifier || token_network_address || chain_id))

where additional_hash is the hash of the whole message being signed.

Fields

Field Name Field Type Description
nonce uint256 Strictly monotonic value used to order transfers. The nonce starts at 1
transferred_amount uint256 Total transferred amount in the history of the channel (monotonic value)
locked_amount uint256 Current locked amount
locksroot bytes32 Hash of the pending locks encoded and concatenated
token_network_address address Address of the TokenNetwork contract
channel_identifier uint256 Channel identifier inside the TokenNetwork contract
additional_hash bytes32 Hash of the message
signature bytes Elliptic Curve 256k1 signature on the above data
chain_id uint256 Chain identifier as defined in EIP155
  • The channel_identifier, token_network_address and chain_id together are a globally unique identifier of the channel, also known as the canonical identifier.
  • The balance data consists of transferred_amount, locked_amount and locksroot.

HashTimeLock

This data structure describes a hash time lock with which a transfer is secured. The locked_amount can be unlocked with the secret matching secrethash until expiration is reached.

Invariants

  • Expiration must be larger than the current block number and smaller than the channel’s settlement period.

Hash

  • keccak256(expiration || amount || secrethash)

Fields

Field Name Field Type Description
expiration uint256 Block number until which transfer can be settled
locked_amount uint256 amount of tokens held by the lock
secrethash bytes32 keccak256 hash of the secret

Messages

Locked Transfer

A Locked Transfer is a message used to reserve tokens for a mediated transfer to another node called the target.

Locked Transfer message

The message is always sent to the next mediating node, altered and forwarded until the target is reached.

In order to create a valid, signed JSON message, four consecutive steps are conducted.

  1. Compute the additional hash
  2. Compute the balance_hash from the balance data
  3. Create the balance_proof with additional_hash and balance_hash
  4. Pack and sign the balance_proof to get the signature of the Locked Transfer

The LockedTransfer message consists of the fields of a hash time lock, an off-chain balance proof and the following:

Field Name Type Description
message_identifier uint64 An ID for Delivered and Processed acknowledgments
payment_identifier uint64 An identifier for the payment that the initiator specifies
token address Address of the token contract
recipient address Destination for this hop of the transfer
target address Final destination of the payment
initiator address Initiator of the transfer and party who knows the secret
fee uint256 Total available fee for remaining mediators
metadata object Routing information

The metadata is a JSON object, which contains routing information under the key routes. The value of routes must be a list of route metadata objects. Each route metadata object is again a json object with a key named route, under which a list of ethereum addresses can be found. (EIP55-checksum addresses with 0x-prefix as usual). The last of the addresses in each list must be the target of the transfer, the former the desired mediators in order.

1. Additional Hash

The data will be packed as follows to compute the additional hash:

Field Type Size (bytes)
command_id (7 for LockedTransfer) uint8 1
message_identifier uint64 8
payment_identifier uint64 8
expiration uint256 32
token_network_address address 20
token address 20
recipient address 20
target address 20
initiator address 20
secrethash bytes32 32
amount uint256 32
fee uint256 32
metadata_hash bytes32 32

The metadata_hash is defined using RLP. It is given as:

metadata_hash = sha3(rlp(list of route_hashes))
route_hash = sha3(rlp(list of addresses in binary form))

This will be used to generate the the data field called additional_hash, which is a required part of the process to create the message signature. It is computed as the keccak256-hash of the data structure given above:

additional_hash = keccak256(pack(additional_hash_data))

Note

The additional_hash is sometimes called message_hash in the reference implementation.

2. Balance Hash

Before we generate the message signature another hash needs to be created. This is the balance_hash that is generated using the balance data:

Field Data Size
transferred_amount uint256 32
locked_amount uint256 32
locksroot bytes32 32

In order to create the balance_hash you first need to pack the balance data:

packed_balance = pack(balance_data)
balance_hash = keccak256(packed_balance)

3. Balance Proof

The signature of a Locked Transfer is created by signing the packed form of a balance_proof.

A balance_proof contains the following fields - using our example data. Notice that the fields are the same as in the off-chain balance proof datastructure, except there is no signature yet and the balance data has been hashed into balance_hash.

Field Type Size
token_network_address address 20
chain_id uint256 32
msg_type (1 for balance proof) uint256 32
channel_identifier uint256 32
balance_hash bytes32 32
nonce uint256 32
additional_hash bytes32 32

4. Signature

Lastly we pack the balance_proof and sign it, to obtain the signature field of our LockedTransfer message:

packed_balance_proof = pack(balance_proof)
signature = eth_sign(privkey=private_key, data=packed_balance_proof)

Preconditions for LockedTransfer

For a Locked Transfer to be considered valid there are the following conditions. The message will be rejected otherwise:

[1]If the locked amount is increased by more, then funds may get locked in the channel. If the locked amount is increased by less, then the recipient will reject the message as it may mean it received the funds with an on-chain unlock. The initiator will stipulate the fees based on the available routes and incorporate it in the lock’s amount. Note that with permissive routing it is not possible to predetermine the exact fee amount, as the initiator does not know which nodes are available, thus an estimated value is used.
[2]If the amount is higher then the recipient will reject it, as it means he will be spending money it does not own.

Example

Consider an example network of three participants A, B and C, where A has a channel with B and B has a channel with C. A wants to send 50 wei of a token to C, using B as a mediator. So he will send a LockedTransfer to B (recipient), where C is specified as the target. After receiving the message, B sends a new LockedTransfer message to C.

Our example accounts are:

Name Role Address Private Key
A initiator 0x540B51eDc5900B8012091cc7c83caf2cb243aa86 377261472824796f2c4f6a73753136587b5624777a4537503b39324a227e227d
B mediator 0x811957b07304d335B271feeBF46754696694b09e 7c250a70410d7245412f6d576b614d275f0b277953433250777323204940540c
C target 0x2A915FDA69746F515b46C520eD511401d5CCD5e2 2e20593e0b5923294a6d6f3223604433382b782b736e3d63233c2d3a2d357041

Our example token is deployed at 0x05ab44f56e36b2edff7b36801d509ca0067f3f6d and the TokenNetwork contract at 0x67b0dd5217da3f7028e0c9463fdafbf0181e1e0a.

The LockedTransfer message generated by A looks like this:

{
   "message_identifier": 8492128289064395926,
   "signature": "0xff15d4f1b8dd01638e5287af408600d7b6e6b9cd7f9d5ef5a3dd3f6fdb72d8805377d8279e202f43139ca1a8f953009868558e8247fb61f8ca879353240b503a1c",
   "chain_id": 337,
   "nonce": 1,
   "transferred_amount": 0,
   "locked_amount": 50,
   "locksroot": "0x07fe6255272aa923234b651199a2d7a277e3b1af3156f18c3bc0ba45d27fa380",
   "channel_identifier": 20,
   "token_network_address": "0x67b0dd5217da3f7028e0c9463fdafbf0181e1e0a",
   "payment_identifier": 1,
   "token": "0x05ab44f56e36b2edff7b36801d509ca0067f3f6d",
   "recipient": "0x2a915fda69746f515b46c520ed511401d5ccd5e2",
   "lock": {
      "amount": 50,
      "expiration": 1288153,
      "secrethash": "0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0"
   },
   "target": "0x811957b07304d335b271feebf46754696694b09e",
   "initiator": "0x540b51edc5900b8012091cc7c83caf2cb243aa86",
   "fee": 0,
   "metadata": {
      "routes": [
         {
            "route": [
               "0x2a915fda69746f515b46c520ed511401d5ccd5e2",
               "0x811957b07304d335b271feebf46754696694b09e"
            ]
         }
      ]
   }
}

Let’s recapitulate how the signature in this message has been computed.

The data for the additional hash is:

cmdid = 7
message_identifier = 8492128289064395926
payment_identifier = 1
expiration = 1288153
token_network_address = 0x67b0dd5217da3f7028e0c9463fdafbf0181e1e0a
token = 0x05ab44f56e36b2edff7b36801d509ca0067f3f6d
recipient = 0x2a915fda69746f515b46c520ed511401d5ccd5e2
target = 0x811957b07304d335b271feebf46754696694b09e
initiator = 0x540b51edc5900b8012091cc7c83caf2cb243aa86
secrethash = 0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0
amount = 50
fee = 0
metadata_hash = 0x48a094f09ca6f63f59bf2c4f226ebb95c304e06d694586b3bc81b2c627a1db5a

So the packed data for the additional_hash is:

0x0775da19af88baa4960000000000000001000000000000000000000000000000000000000000000000000000000013a7d905ab44f56e36b2edff7b36801d509ca0067f3f6d2a915fda69746f515b46c520ed511401d5ccd5e2811957b07304d335b271feebf46754696694b09e540b51edc5900b8012091cc7c83caf2cb243aa86d4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf00000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000048a094f09ca6f63f59bf2c4f226ebb95c304e06d694586b3bc81b2c627a1db5a

Which yields the additional_hash:

0x1fe4b4ddd491bafd7e06b2c623b65b988e5bdf4daab043ac61a22cf6ee049816

The balance data is:

transferred_amount = 0
locked_amount = 50
locksroot = 0x07fe6255272aa923234b651199a2d7a277e3b1af3156f18c3bc0ba45d27fa380

In packed form:

0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003207fe6255272aa923234b651199a2d7a277e3b1af3156f18c3bc0ba45d27fa380

Which yields the balance_hash:

0x1b3d8af4b3557a84d2ac8997e869d395167cec0024c685ce1165f47aa031ec88

So this is the data to sign for the signature field:

token_network_address = 0x67b0dd5217da3f7028e0c9463fdafbf0181e1e0a
chain_id = 337
msg_type = 1
channel_identifier = 20
balance_hash = 0x1b3d8af4b3557a84d2ac8997e869d395167cec0024c685ce1165f47aa031ec88
nonce = 1
additional_hash = 0x1fe4b4ddd491bafd7e06b2c623b65b988e5bdf4daab043ac61a22cf6ee049816

In packed form:

0x67b0dd5217da3f7028e0c9463fdafbf0181e1e0a0000000000000000000000000000000000000000000000000000000000000151000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000141b3d8af4b3557a84d2ac8997e869d395167cec0024c685ce1165f47aa031ec8800000000000000000000000000000000000000000000000000000000000000011fe4b4ddd491bafd7e06b2c623b65b988e5bdf4daab043ac61a22cf6ee049816

Signing this with A’s private key yields the signature in the message.

Refund Transfer

The RefundTransfer message is very similiar to LockedTransfer, with the following differences:

  • there is no metadata field
  • when computing the additional_hash, there is thus no metadata_hash field at the end of the packed data, and
  • the command id is 8 instead of 7.

Lock Expired

Message used to inform partner that the Hash Time Lock has expired. Sent by the initiator to the mediator or target when the following conditions are met:

Preconditions

  • The current block reached the lock’s expiry block number plus NUMBER_OF_BLOCK_CONFIRMATIONS.
  • For the lock expired message to be sent, the initiator waits until the expiration + NUMBER_OF_BLOCK_CONFIRMATIONS * 2 is reached.
  • For the mediator or target, the lock expired is accepted once the current expiration + NUMBER_OF_BLOCK_CONFIRMATIONS is reached.
  • The initiator or mediator must wait until the lock removal block is reached.
  • The initiator, mediator or target must not have registered the secret on-chain before expiring the lock.
  • The nonce is increased by 1 in respect to the previous balance proof
  • The locksroot must change, the new value must be equal to the root of a new tree after the expired lock is removed.
  • The locked amount must decrease, the new value should be to the old value minus the lock’s amount.
  • The transferred amount must not change.

Message Fields

The LockExpired message consists of an off-chain balance proof and the following fields:

Field Name Field Type Description
message_identifier uint64 An ID for Delivered and Processed acknowledgments
recipient address Destination for this hop of the transfer
secrethash bytes32 From the transfer’s HashTimeLock

Additional Hash

The data will be packed as follows to compute the additional hash:

Field Type Size (bytes)
command_id (13 for LockExpired) uint8 1
message_identifier uint64 8
recipient address 20
secrethash bytes32 32

Secret Request

Message used to request the secret that unlocks a lock. Sent by the payment target to the initiator once a locked transfer is received.

Invariants

Fields and signature

SecretRequest is an internal message with the following fields plus a signature field:

Field Name Field Type Description
cmdid uint8 Value 3 (indicating Secret Request),
(padding) bytes3 three zero bytes
message identifier uint64 An ID used in Delivered and Processed acknowledgments
payment_identifier uint64 An identifier for the payment chosen by the initiator
lock_secrethash bytes32 Specifies which lock is being unlocked
payment_amount uint256 The amount received by the node once secret is revealed
expiration uint256 See HashTimeLock

The signature is obtained by signing the data packed in this format.

Example

In the above example of a mediated transfer, C will send a secret request to A. The data to sign would be:

cmdid = 0x03
padding = 0x000000
message_identifier = 8492128289064395926
payment_identifier = 1
secrethash = 0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0
amount = 50
expiration = 1288153

In packed form:

0x0300000075da19af88baa4960000000000000001d4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf00000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000013a7d9

Signing this with C’s private key yields:

0xfc3c0cd04b339936bb0001a8aff196b767ed49d8eaa3a57e53121f7077584846390c843bc16a04fab8d6e9f9f80004663e183899441a4f7a4e1509e9cdada7351c

Reveal Secret

Message used by the nodes to inform others that the secret is known. Used to request an updated balance proof with the transferred amount increased and the lock removed.

Fields and signature

RevealSecret is an internal message with the following fields plus a signature field:

Field Name Field Type Description
cmdid uint8 Value 11 (indicating Reveal Secret)
(padding) bytes3 three zero bytes.
message_identifier uint64 An ID use in Delivered and Processed acknowledgments
lock_secret bytes32 The secret that unlocks the lock

The signature is obtained by signing the data packed in this format.

Unlock

Non cancellable, Non expirable.

Invariants

Fields

The Unlock message consists of an off-chain balance proof and the following fields:

Field Name Field Type Description
message_identifier uint64 An ID used in Delivered and Processed acknowledgments
payment_identifier uint64 An identifier for the Payment chosen by the Initiator
lock_secret bytes32 The secret that unlocked the lock

Additional Hash

The data is packed as follows to compute the additional hash:

Field Type Size (bytes)
command_id (4 for Unlock) uint8 1
message_identifier uint64 8
recipient address 20
secrethash bytes32 32

Withdraw Request

This message is used by a channel participant node to request the other participant’s signature on a new increased total_withdraw value.

Preconditions

  • The channel for which the withdraw is requested must be open.
  • The total_withdraw value must only ever increase.
  • The participant’s channel unlocked balance must be larger or equal to withdraw_amount, which is calculated using new_total_withdraw - previous_total_withdraw.
  • The new total_withdraw value must not cause an underflow or overflow.
  • The message must be sent by one of the channel participants.
  • The nonce is increased by 1 with respect to the previous nonce.
  • The message sender address must be the same as participant.
  • The signature must be from the sender of the request.

Fields and signature

The table below specifies the format in which a WithdrawRequest is packed to compute the signature.

In addition to the signed fields listed below, the message has:

  • a nonce field
  • a message_identifier used for Processed and Delivered acknowledgements.
Field Name Field Type Description
token network address address Part of the canonical identifier of the channel
chain identifier uint256 Part of the canonical identifier of the channel
message type uint256 3 for withdraw messages
channel identifier uint256 Part of the canonical identifier of the channel
message identifier uint64 An ID used in Delivered and Processed acknowledgements
participant address The address of the withdraw requesting node
total_withdraw uint256 The new monotonic total_withdraw value
expiration uint256 The block number at which withdraw request is no longer usable on-chain.

Withdraw Confirmation

Message used by the Withdraw Request receiver to confirm the request after validating its input.

Preconditions

  • The channel for which the withdraw is confirmed should be open.
  • The received confirmation should map to a previously sent request.
  • The block at which withdraw expires should not have been reached.
  • The participant’s channel balance should still be larger or equal to withdraw_amount.
  • The new total_withdraw value should not cause an underflow or overflow.
  • The message should be sent by one of the channel participants.
  • The nonce is increased by 1 with respect to the previous nonce
  • The signature must be from the sender of the request.

Fields

The table below specifies the format in which a WithdrawConfirmation message is packed to be signed. The signatures of both channel participants are needed for the call to the smart contract’s setTotalWithdraw function.

In addition to the signed fields listed below, the message has:

  • a nonce field
  • a message_identifier used for Processed and Delivered acknowledgements.
Field Name Field Type Description
token network address address Part of the canonical identifier of the channel
chain identifier uint256 Part of the canonical identifier of the channel
message type uint256 3 for withdraw messages
channel identifier uint256 Part of the canonical identifier of the channel
participant address The address of the withdraw requesting node
total_withdraw uint256 The new monotonic total_withdraw value
expiration uint256 The block number at which withdraw request is no longer usable on-chain.

Withdraw Expired

This message is used by the withdraw-requesting node to inform the partner that the earliest-requested, non-confirmed withdraw has expired.

Preconditions

  • The channel for which the withdraw is confirmed should be open.
  • The sender waits expiration_block + NUMBER_OF_CONFIRMATION * 2 until the message is sent.
  • The receiver should only accept the expiration message if the block at which the withdraw expires is confirmed.
  • The received withdraw expiration should map to an existing withdraw state.
  • The message should be sent by one of the channel participants.
  • The nonce is increased by 1 with respect to the previous nonce
  • The signature must be from the sender of the request.

Fields

The table below specifies the format in which WithdrawExpired is packed to compute its signature.

Field Name Field Type Description
cmdid uint8 Value 17 (indicating Withdraw Expired),
(padding) bytes3 three zero bytes
nonce uint256 Strictly monotonic value used to order transfers.
message_identifier uint64 An ID for Delivered and Processed acknowledgments
token network address address Part of the canonical identifier of the channel
chain identifier uint256 Part of the canonical identifier of the channel
message type uint256 3 for withdraw messages
channel identifier uint256 Part of the canonical identifier of the channel
message identifier uint64 An ID used in Delivered and Processed acknowledgements
participant address The address of the withdraw requesting node
total_withdraw uint256 The new monotonic total_withdraw value
expiration uint256 The block number at which the withdraw request is no longer usable on-chain.

Processed/Delivered

The Processed and Delivered messages are sent to let other parties in a transfer know that a message has been processed/received.

Fields and signature

Processed and Delivered are internal messages with the following fields plus a signature:

Field Name Field Type Description
cmdid uint8 Value 0 for Processed or 12 for Delivered
(padding) bytes3 three zero bytes
message_identifier uint64 The identifier of the processed/delivered message.

The signature is obtained by signing the data packed in this format.

References

Message fromat specifications

All the tables in the fields sections of the message spec should match the reference implementation. For example, the packing of a locked transfer message can be found here.