Skip to content

RLN-IP 0004

RLN-IP: 4
Title: RLN Protobuf Message Definitions
Author: Anthony Culligan <anthony.culligan@setl.io>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/
Status: Active
Type: Process
Created: 2023-11-02
License: CC-BY-SA-4.0: [Creative Commons Attribution-ShareAlike]

Abstract

The following specifies the protobuf definitions for messages which form the set required to communication between RLN Domains to settle inter domain transactions

Messages

Envelope

A universal wrapper for the possible messages. Simplifies message decoding.

message Envelope {

  string version = 1
  [
    (validate.rules).string.min_len = 1
  ];

  optional EntityLocation recipient = 2;

  oneof contents {

    // Initial Transfer Request to the network
    ProposeTransferSet propose_transfer_set = 10;

    // Request to Participants for Settlement Steps
    RequestSteps request_steps = 13;

    // Response from Participant of Settlement Steps
    PossibleSteps possible_steps = 14;

    // Transfer request message after enrichment with full settlement path.
    Manifest manifest = 16;

    // Vote (Approval) message from Participant
    Vote vote = 17;

    // Finalised Transfer message to Participants
    Finalised finalised = 18;

  }

}

Initiating Proposal

Wrapper for a set of transfers to be processed atomically.

message ProposeTransferSet {
  /**
   * The correlation ID that uniquely identifies this group of transfers.
   * The correlation ID is provided by the client and applies to all messages across a Transfer Set settlement.
   */
  string correlation_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The ID of the proposing party.
   */
  Participant proposer = 3
  [
    (validate.rules).message.required = true
  ];

  /** The transfers that constitute this group. */
  repeated ProposeTransfer transfers = 4
  [
    (validate.rules).repeated.min_items = 1
  ];
}

A transfer of a single asset type from one party to another. One, of potentially many, transfers in a transfer set.

message ProposeTransfer {
  /**
   * The type of this transfer. The type must be understood by all parties to
   * the transfer. A party that does not recognise a proposal type is expected
   * to vote to reject it.
   *
   * REQUIRED.
   */
  string type = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The correlation ID that uniquely identifies this group of transfers.
   * The correlation ID is provided by the client and applies to all messages across a Transfer Set settlement.
   */
  string correlation_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The source of the transfer.
   */
  Party from = 3;

  /**
   * The destination of the transfer.
   */
  Party to = 4;


  /**
   * Details of this transaction. For example, the amount to be transferred,
   * or a list of identifiers of non-fungible tokens.
   */
  Payload payload = 5;

  /** The message that led to this transfer. e.g. SWIFT Message or internal settlement instruction. */
  SourceMessage source_message = 6;

  /**
   * The possible initial steps to route this proposed transfer.
   */
  optional PossibleSteps possible_steps = 7;

  /**
   * ID to uniquely identify this Transfer within the TransferSet.
   * Where there is only a single Transfer in the TransferSet, this ID may not be required.
   */
  optional string transfer_id = 8;
}

Settlement Query

Request for possible settlement destinations for a given transfer.

message RequestSteps {
  /**
   * The correlation ID that uniquely identifies this specific transfer.
   */
  string correlation_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * A unique ID for this specific request. This is used to match the "Possible Steps"
   * response to a specific Scheduler request.
   */
  string request_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The type of the transfer, as specified in the Propose Transfer message.
   */
  string type = 3
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * If true, the participant is being invited as an observer.
   */
  bool is_observer = 4;

  /**
   * If true, the routing is being built in the reverse direction from the
   * destination towards the source.
   */
  bool is_reverse = 5;

  /**
   * The ID of the original source party.
   */
  optional Participant from_participant = 6;

  /**
   * The ID of the original destination party.
   */
  optional Participant to_participant = 7;

  Payload proposed_payload = 8;

  optional SourceMessage source_message = 9;

  /**
   * The path that leads to the participant that is being asked to provide
   * the next link.
   *
   */
  repeated Link path_links = 10;

  /**
   * Observers to this path.
   */
  repeated Participant observers = 11;

  AccountAction account_action = 12;
}

Settlement Query Response

Response of settlement counterparties.

message PossibleSteps {
  /** Possible reasons for giving this response to route a transfer. */
  enum ResponseCode {
    /** No code specified. This indicates the response is malformed. */
    UNSPECIFIED = 0;

    /** No error. Everything is OK. */
    OK = 1;

    /** The transfer type was not recognised. */
    UNKNOWN_TYPE = 2;

    /** The transfer requires a step context, but none was provided. */
    MISSING_STEP_CONTEXT = 3;

    /** The transfer required a transfer context, but none was provided. */
    MISSING_TRANSFER_CONTEXT = 4;

    /** The step context was mis-specified. */
    BAD_STEP_CONTEXT = 5;

    /** The transfer context was mis-specified. */
    BAD_TRANSFER_CONTEXT = 6;

    /**
     * The request was understood but the participant cannot not route it.
     *
     * The ledger swarm will attempt to route by another path.
     */
    CANNOT_ROUTE = 7;

    /**
     * The request was understood but the participant refuses to route it.
     *
     * The ledger swarm will attempt to route by another path.
     */
    REFUSED = 8;

    /**
     * The request was understood but the participant either refuses to or
     * cannot route it. Furthermore, the participant wants to add observers
     * to the transfer, giving them the opportunity to vote to reject it.
     *
     * The response may specify steps provided they only list observers.
     *
     * The network will add the specified observers and attempt to route
     * by another path.
     */
    OBSERVE_ONLY = 9;

    /**
     * The participant cannot suggest further steps to the path, but is
     * willing to act as a connecting point for the path as constructed so
     * far.
     *
     * For example, a top-level entity might refuse to further expand a
     * forward path as that would lead to specifying all entities in the
     * domain. Instead the reverse path must be expanded until it arrives at
     * the same top-level entity.
     *
     * Alternatively, a gateway into a private domain would not list the
     * members of the private domain. If expanding the other end of the path
     * leads to the same gateway it can route it, but it will not reveal the
     * members of the private domain.
     */
    CANNOT_SUGGEST = 10;
  }

  /**
   * The correlation ID that uniquely identifies this specific transfer.
   */
  string correlation_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * A unique ID from the request to which this is a response. This is used to
   * match this "Possible Steps" response to a specific Scheduler request.
   *
   * Note: this is a required field in response to a "Request Steps", but not
   * required if part of a "Propose Transfer".
   */
  string request_id = 2;

  ResponseCode status = 3
  [
    (validate.rules).enum.defined_only = true
  ];

  /**
  Participant sending the response
   */
  Participant participant = 4;

  /**
   * An optional message associated with this response.
   */
  optional Message settlement_message = 5;

  /**
   * The steps that could be used to resolve a transfer. The most preferred
   * step is the first one listed, with each subsequent step being less
   * desirable than its predecessors.
   */
  repeated ObservedLink steps = 6;

  /**
   * How many seconds can this response be re-used for? If this value is
   * non-zero then the participant is happy for these possible steps to be
   * re-used as a response for any identical "Request Steps" message.
   *
   * A non-zero value does not guarantee re-use. A zero value does guarantee
   * that this response will not be re-used.
   */
  uint32 valid_for_seconds = 7;

  /**
   * Observer participants.
   *
   * Observers will not be asked to vote on a transfer and will be informed as to
   * whether it is accepted or rejected.
   * <p>Observers on an ObservedLink would apply in addition to these observers.</p>
   * <p>Observers on an ObservedLink will only apply if that link was on the final settlement path.</p>
   */
  repeated Participant observers = 8;
}

Request for Authorisation

Details of a proposed set of transfers, with settlement details, requiring approval.

/**
 * Manifest : Send a proposed (scheduled) Transferset to the Participants for approval.
 */
message Manifest {
  /**
   * The correlation ID that uniquely identifies this group of transfers.
   * The correlation ID is provided by the client and applies to all messages across a Transfer Set settlement.
   */
  string correlation_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * A unique ID for this specific manifest.
   * Used by the assembler to match Manifests to Votes.
   */
  string request_id = 2;

  /** The transfers that constitute this group. */
  repeated Transfer transfers = 3
  [
    (validate.rules).repeated.min_items = 1
  ];
}

Authorisation

Approval, or rejection, of a set of Transfers.

/**
 * A vote for or against a set of proposed transfers.
 */
message Vote {
  /**`
   * The correlation ID that uniquely identifies this group of transfers.
   * The correlation ID is provided by the client and applies to all messages across a Transfer Set settlement.
   */
  string correlation_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * A unique ID for this Vote.
   * Must be taken from the specific manifest received. Used by the assembler to match Votes to Manifests.
   */
  string request_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The party that is approving or rejecting.
   */
  Participant participant = 3;

  /**
   * If true, the transfer is approved.
   */
  bool is_approved = 4;

  /**
   * An optional message associated with the vote. This may provide a reason
   * for the rejection.
   */
  optional Message message = 5;

  /**
   * The signature of the vote.
   * The Swarm ID should be signed if approved.
   * The enclosing message is signed, which should be sufficient for Rejection.
   */
  optional Signature signature = 6;

  /**
   * The status of the vote.
   * The Participant can indicate if this vote is a repeat, or if the transfer has already been finalised.
   */
  Status status = 7;
}

Settlement Finality

Notification of commitment to a previously approved set of Transfers.

/**
 * Announcement of the finalisation of a transfer after sufficient votes are
 * gathered by the network.
 */
message Finalised {
  enum Status {
    APPROVED = 0;
    REJECTED = 1;
  }

  /**
   * The correlation ID that uniquely identifies this group of transfers.
   * The correlation ID is provided by the client and applies to all messages across a Transfer Set settlement.
   */
  string correlation_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  string request_id = 2;

  /** Was the transfer approved, rejected or something? */
  Status status = 3;

  int64 timestamp = 4;

  /**
   * A message which may provide some additional information about the final
   * decision.
   */
  optional Message message = 5;

  /**
   * The signatures of the participants who have approved the finalisation.
   */
  repeated Signature signatures = 6;

}

Supporting message classes

Participant

message Participant {
  /**
  * The ID of the party, as it is known to the network.
  */
  string id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  string domain = 2;
}

Party

message Party {
  /**
   * The ID of the party, as it is known to the network.
   *
   * REQUIRED.
   */
  Participant participant = 1
  [
    (validate.rules).message.required = true
  ];

  /**
  * The account via which the specific transaction is routed.
  * For example, it could specify a bank account or blockchain address.
  *
  * REQUIRED.
  */
  Account account = 2
  [
    (validate.rules).message.required = true
  ];
}

Account

message Account {
  oneof specification {
    option (validate.required) = true;

    /** A generic account. */
    GenericAccount account = 1;

    /** A block chain address. */
    BlockChainAddress address = 2;

    /** An account with a linked client account. */
    NostroVostro nostro_vostro = 3;
  }
}

AccountAction

enum AccountAction {
  UNKNOWN = 0;
  DEBIT = 1;
  CREDIT = 2;
}

GenericAccount

message GenericAccount {
  /**
   * The ID of the agency that holds this account.
   */
  string agent_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The ID of the account.
   */
  string account_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

}

BlockChainAddress

message BlockChainAddress {
  /**
   * The ID of the agency that manages this address.
   */
  string agent_id = 1;

  /**
   * An identifier for the block chain where the address's transactions are
   * recorded.
   */
  string chain_id = 2;

  /**
   * The address's identifier.
   */
  string address = 3
  [
    (validate.rules).string.min_len = 1
  ];
}

NostroVostro

message NostroVostro {
  /** The nostro (mine) account. */
  GenericAccount nostro = 1;

  /** The vostro (yours) account. */
  GenericAccount vostro = 2;
}

Payload

message Payload {
  oneof specification {
    /** A quantity of a named asset. */
    NamedAssetAmount asset_amount = 1;

    /** A quantity of cash. */
    CashAmount cash_amount = 2;

    /** A list of non-fungible tokens. */
    NftList nft_list = 3;

    /** A quantity of fungible tokens. */
    TokenAmount tokenAmount = 4;
  }
}

NamedAssetAmount

message NamedAssetAmount {
  string asset_id = 1
  [
    (validate.rules).string.min_len = 1
  ];

  Amount amount = 2
  [
    (validate.rules).message.required = true
  ];

  string asset_id_type = 3;

}

CashAmount

message CashAmount {
  /** The currency code. The code is required. If no currency applies, the
   * 'XXX' or 999 code should be used.
   */
  CurrencyCode currency = 1;

  /** The amount of cash. */
  Amount amount = 2
  [
    (validate.rules).message.required = true
  ];
}

NftList

message NftList {
  /**
   * An identifier for the block chain where the NFTs are recorded.
   */
  string chain_id = 1;

  /**
   * The ID of the class of NFTs.
   */
  string token_id = 2;

  /** The IDs of the NFTs being transferred. */
  repeated string nft_id = 3
  [
    (validate.rules).repeated.items.string.min_len = 1
  ];
}

TokenAmount

message TokenAmount {
  /**
   * An identifier for the block chain where the NFTs are recorded.
   * The chain ID is expected but not required, as the chain may be
   * implicit from the token ID, or the token could be represented on
   * multiple chains.
   */
  string chain_id = 1;

  /** The ID of the token. This field is required. */
  string token_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /** The amount of fungible tokens. This field is required. */
  Amount amount = 3
  [
    (validate.rules).message.required = true
  ];
}

Amount

message Amount {
  oneof representation {
    option (validate.required) = true;

    /** The amount as a 64-bit unsigned integer. */
    uint64 value = 1;

    /**
     * The amount as a byte array containing the binary
     * big-endian representation.
     */
    bytes bits = 2;
  };

  /** Number of decimal places in the amount. */
  uint32 scale = 3;
}

SourceMessage

message SourceMessage {
  /** The type of the message. For example, a PACS.008. */
  string type = 1;

  /** The actual message. */
  oneof storage {
    /* The original triggering message is included in this message. */
    string raw_message = 2;

    /* The URI from which the original message can be retrieved. */
    string archive_uri = 3;
  }
}
message Link {
  /** The Party ( Participant + Account ) that will handle the next step. */
  Party party = 1
  [
    (validate.rules).message.required = true
  ];

  /**
   * An optional message associated with this link.
   * <p>Settlement Message on Link would override Settlement Message on a PossibleSteps Message.</p>
   */
  optional Message settlement_message = 2;

  AccountAction account_action = 3;
}
message ObservedLink {

  /** The link to the next participant in the settlement path. */
  optional Link next = 1;

  /**
   * Additional observer participants.
   *
   * Observers will not be asked to vote on a transfer and will be informed as to
   * whether it is accepted or rejected.
   */
  repeated Participant observers = 2;
}

Message

message Message {
  /**
   * The message code that identifies the message type and indicates which
   * template should be used to generate a user interface message from
   * the parameters.
   */
  string code = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The parameters associated with the message.
   */
  google.protobuf.Struct parameters = 2;
}

Transfer

message Transfer {
  /**
  * The type of this transfer. The type must be understood by all parties to
  * the transfer. A party that does not recognise a proposal type is expected
  * to vote to reject it.
  *
  * Some transfer types may have only a "from" or a "to". For example, a
  * "token mint" operation might only have a "to" party.
  *
  * REQUIRED.
  */
  string type = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The correlation ID that uniquely identifies this group of transfers.
   * The correlation ID is provided by the client and applies to all messages across a Transfer Set settlement.
   */
  string correlation_id = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /**
   * The source of the transfer.
   */
  Party from = 3;

  /**
   * The destination of the transfer.
   */
  Party to = 4;

  /**
   * Details of this transaction. For example, the amount to be transferred,
   * or a list of identifiers of non-fungible tokens.
   *
   * REQUIRED
   */
  Payload payload = 5
  [
    (validate.rules).message.required = true
  ];

  /**
   * The observers to this transfer.
   */
  repeated Participant observers = 6;

  /**
   * The settlement route to this transfer.
   */
  repeated Link path_links = 7
  [
    (validate.rules).repeated.min_items = 1
  ];
}

Signature

message Signature {

  /** Signature algorithms accepted by the Ledger Swarm. */
  enum Algorithm {
    SHA256_WITH_RSA = 0;
    SHA384_WITH_RSA = 1;
    SHA512_WITH_RSA = 2;
    SHA512_256_WITH_RSA = 3;
    SHA3_256_WITH_RSA = 4;
    SHA3_384_WITH_RSA = 5;
    SHA3_512_WITH_RSA = 6;

    SHA256_WITH_DSA = 7;
    SHA384_WITH_DSA = 8;
    SHA512_WITH_DSA = 9;
    SHA3_256_WITH_DSA = 10;
    SHA3_384_WITH_DSA = 11;
    SHA3_512_WITH_DSA = 12;

    SHA256_WITH_ECDSA = 13;
    SHA384_WITH_ECDSA = 14;
    SHA512_WITH_ECDSA = 15;
    SHA3_256_WITH_ECDSA = 16;
    SHA3_384_WITH_ECDSA = 17;
    SHA3_512_WITH_ECDSA = 18;

    ED_25519 = 19;
    ED_448 = 20;
  }

  /** Payload to Sign. */
  string payload = 1
  [
    (validate.rules).string.min_len = 1
  ];

  /** Signature - Expected to be B64 Encoded. */
  string signature = 2
  [
    (validate.rules).string.min_len = 1
  ];

  /** PEM encoded X.590 Certificate chain for the signer. */
  string certificate = 3
  [
    (validate.rules).string.min_len = 1
  ];

  /** Algorithm used generate payload. */
  Algorithm algorithm = 4
  [
    (validate.rules).enum.defined_only = true
  ];

}