Off-chain API
LIP | 1 |
Title | Off-chain API |
Author | Kevin Hurley (@kphfb), Dmitry Pimenov, George Danezis |
Status | Final |
Type | Informational |
Created | 05/29/2020 |
Summary
The Off-Chain Protocol is an API and payload specification that could be used to support compliance, privacy and scalability on blockchains. Note that this API is being published by the Libra Association on an “as is” basis. Publication of this API by the Libra Association does not mean that the Association is taking any position on whether the API addresses issues of compliance, privacy and scalability. Users of this API must make such determinations on their own.
Abstract / Motivation
The Off-Chain Protocol is an API and payload specification to support compliance, privacy and scalability on blockchains. It is executed between a pair of entities and allows them to privately exchange payment information before, while, or after settling it on a Blockchain. The entities can be inclusive of designated dealers (DDs) and Virtual Asset Service Providers (VASPs), such as wallets or exchanges.
The initial use-case for the Off-Chain Protocol relates to supporting compliance, and in particular supporting the implementation of the Travel Rule requirements that VASPs may be required to follow. Those requirements generally specify that when money transfers above a certain amount are executed by VASPs, some information about the sender and recipient of funds must become available to both VASPs. The Off-Chain Protocols allows VASPs to exchange this information privately.
A secondary use-case for the Off-Chain Protocol is to provide higher levels of privacy than those that can be achieved directly on a Blockchain. The exact details of the customer accounts involved in a payment, as well as personal information that may need to be exchanged to support compliance, remain off-chain. The information is exchanged within a secure, authenticated and encrypted, Channel and would only be made available to the parties that strictly require them.
In the future, the Off-Chain Protocol may be further extended to include functionality such as batching of transactions and additional payments functionality.
Specification
Off-Chain Protocol Design Principles
Scalability. Pursuant to the design of the initial version of the Off-Chain Protocol, all off-chain PaymentObjects that are ready for settlement, would be settled individually (gross) through a separate Blockchain transaction. However, the architecture of the Off-Chain Protocol allows in the future the introduction of netting batches of transactions and settling all of them through a single Blockchain transaction. This allows costs associated with multiple on-chain transactions to be kept low for VASPs, and allows for a number of user transactions or payment between VASPs that exceed the capacity of the underlying Blockchain. Additionally, batches enhance privacy via grouping together the number of transactions between VASPs and by only placing a single on-chain transaction which hides the individual transaction amounts.
Note that every transaction within a batch would first be agreed upon between the VASPs involved, and the VASPs would have full insight into the transactions and any travel rule information about those transactions.
Extensibility. This initial version of the Off-Chain Protocol accommodates simple payments where a customer of a VASP sends funds to the customer of another VASP over a limit, requiring some additional compliance-related information. However, in the future the blockchains may support more complex flows of funds between customers of VASPs as well as merchants. The Off-Chain Protocol can be augmented to support the transfer of rich meta-data relating to those flows between VASPs in a compliant, secure, private, scalable and extensible manner.
Generic Communication Framework. The Off-Chain Protocol is designed as a generic communication framework which can be utilized by any Blockchain and requires no ties to any specific blockchain. While the first potential usage of the Off-Chain Protocol is presented as within the Libra Blockchain, the Off-Chain Protocol makes few and well defined assumptions about the underlying Blockchain environment, which can be fulfilled by other Blockchains. The Off-Chain Protocol can therefore be re-purposed to support compliance, privacy and scalability use-cases between VASPs in other Blockchains, as well as in multiple blockchains simultaneously.
We describe a number of additional lower-level requirements throughout the remaining of the documents, such as ease of deployment through the use of established web technologies (like HTTP and JSON), tolerance to delays and crash-recovery failures of either VASPs, and compatibility with common cryptography and serialization schemes.
Terminology
Object: Equivalent to a record in a database. A payment is an example of an Object.
Command: An instruction sent over a Channel to mutate/create one or more Objects. In the case of a mutation, this Command will depend upon the current value of one of more existing Shared Objects.
Channel: The communication path between a pair of entities who execute Commands and track the evolution of a set of Shared Objects.
Shared Object: All Objects contained within a Command - for example PaymentObject
s, are considered as "Shared Objects" - meaning that either VASP may create a new Command to modify the Object, and will do so during the typical life-cycle of an Object - an example being the addition of required data within the KycDataObject from both VASPs to a payment Object. Both VASPs in a Channel can asynchronously attempt to initiate and execute Commands on Shared Objects.
Version: The state of an Object at a specific point in time. Every creation or mutation creates a new Version of the Object which was created/mutated. VASPs operate upon the latest Version of Shared Objects. Once a Version is mutated, it is no longer a valid Version upon which a new Command may depend - the newly updated value of the Version is the latest Version for that Object and must be acted upon.
reference_id: Every Object type contains a reference_id
field which is a unique reference ID for the Object. The reference ID is used as a unique identifier of the Object. The reference ID is always specified by the payment initiator VASP (the VASP which originally created the Object). This value should be unique, and formatted as “<creatorvasp_onchain_address_bech32><unique_id>”. For example, ”lbr1x23456abcd_seqABCD“. As an example, see the reference_id field on the PaymentObject. Unicode utf-8 encoded. "unique_id" should be 64 or fewer characters.
Overall Off-Chain Properties
The Off-Chain Protocol formulates methodologies to provide the following high-level properties:
Channel Establishment
The Off-Chain Protocol allows two entities A and B to form a communication Channel for manipulating a consistent database of Objects and agreeing on Commands that manipulate those Objects.
Command Issuance By Either Party
Both ends of the Channel can issue Commands upon Shared Objects.
Object Versioning
To ensure a consistent view of Object states, every Object is versioned. Mutations may only occur upon the latest Version of an Object.
Concurrency Control
The Off-Chain Protocol manages concurrent requests on Objects and ensures that Object states will not become out of sync.
Eventual Consistency
The Off-Chain Protocol ensures eventual consistency of Shared Objects.
Basic Off-Chain Building Blocks
On-Chain Account Setup
A VASP's on-chain account must be created and set up in accordance with the standard VASP account creation process.
Of particular note for off-chain APIs is the compliance key value. This is the Ed25519 key with which a VASP signs travel rule statements on-chain and the required information within the KycDataObject exchanged via the Off-Chain Protocol. During account creation, a VASP will set their compliance key which will be stored under the primary VASP account.
HTTP end-point
Each VASP exposes an HTTPS POST end point at https://<hostname>:<port>/<protocol_version>/<LocalVASPAddress>/<RemoteVASPAddress>/command
.
protocol_version
is initially 0 for this iteration of the off-chain APIs.LocalVASPAddress
andRemoteVASPAddress
are the bech32 VASP addresses of the respective VASPs - with the subaddress set to 0's as per LIP 5.
HTTP Headers
All HTTP requests and responses must contain a header X-Request-ID
with a unique ID for the request, used for tracking requests and debugging. Responses must have the same string in the X-Request-ID
header value as the requests they correspond to.
Payloads
The exposed endpoint receives CommandRequestObject
s in the POST body, and responds with CommandResponseObject
s in the HTTP response (See Request/Response Payload for more details. Single Command requests-responses are supported (HTTP1.0) but also pipelined request-responses are supported (HTTP1.1). The content type of the HTTP request/response is ignored. The version for the Off-Chain Protocol is the string v1
. All structures transmitted, nested within CommandRequestObject
and CommandResponseObject
are valid JSON serialized Objects and can be parsed and serialized using standard JSON libraries.
All transmitted requests/responses are signed by the sending party using the JWS Signature standard (with the Ed25519 / EdDSA ciphersuite, and compact
encoding). The party's on-chain compliance key shall be used to sign these messages. This ensures all information and meta-data about payments is authenticated and cannot be repudiated.
Basic Protocol Interaction
Assume two VASPs A and B. The basic protocol interaction consists of:
- An initiating VASP A creates a
CommandRequestObject
containing a Command of the desired type. Commands inform the other VASP what to create or mutate. Every Command includes one or more Objects to create or update. For each of those Objects, the Version of that Object is updated by the Command and the old Version becomes obsolete once the Command is applied successfully. - VASP A packages the Command via JWS using EdDSA and compact encoding.
- VASP A establishes a connection to VASP B and sends the packaged Command to VASP B in the body of an HTTP POST.
- VASP B listens for requests, and when received, verifies VASP A's signature and then processes the request to generate and send
CommandResponseObject
responses, with a success or failure status, through the HTTP response body. In case of an error that prevents VASP B from parsing an incomingCommandRequestObject
an HTTP error code is returned. - The initiating VASP A receives the response, verifies its signature, and processes it to assess whether it was successful or not.
If VASP A fails to receive a response from VASP B, or in the case of specific protocol failure codes, it must re-send the request at some cadence until a response is received to ensure eventual consistency.
Commands may flow in both directions - VASP A to VASP B or VASP B to VASP A - and may happen simultaneously in an asynchronous manner, providing Multi-Party Command Issuance. By only allowing Commands to build upon the latest agreed-upon Version of an Object, concurrent requests are possible with minimal contention - contention only occurs if simultaneous Commands operate upon the same Object(s) and contention in that case is resolved by nature of Client/Server Roles.
Command Sequencing
The low-level Off-Chain Protocol allows two VASPs to sequence request-responses for Commands originating from either VASP, in order to maintain a consistent database of Shared Objects. Sequencing a Command requires both VASPs to confirm it is valid, as well as its sequence in relation to other Commands operating upon the same Objects. Since Commands may operate upon multiple Objects, a Command only succeeds if the Command is able to be applied to every dependent Object - ensuring atomicity of the Command and consistency of the Objects, providing for Concurrency Control. Both VASPs in a Channel can asynchronously attempt to initiate and execute Commands on Shared Objects. All Commands upon Shared Objects which are exchanged between pairs of VASPs are sequenced relative to the prior state of each Shared Object in the Command. For example, a Command might depend on Object X having Version Y. If there has been a concurrent update to Object X and it has Version Z at the time the Command is processed, the Command will be rejected.
Command Sequencing: Object Versioning
To ensure a consistent view of Object states, every Object is Versioned. A Command can create or modify one or more Objects. Mutations may only occur upon the latest Version of an Object. When either VASP creates a request, they must denote which Objects are read and which are written during this Command. These are specified in the _reads
and _writes
fields of the Command.
As mentioned in Terminology, every Object type contains a reference_id
field which is a unique reference ID for the Object. The _reads
field is specified as a JSON mapping of Object "reference_id"
: "cid"
, where cid
(Command ID) is a random 16 byte string encoded in hex. The _writes
field represents the new mapping of Objects to a new cid
value. The new cid
value for an Object created or mutated by a Command is the cid
of the Request containing the Command that created or mutated this Object.
By specifying which Object Versions are read, the off-chain API maintains relative ordering of Commands on Objects and ensures a consistent view of the current state of Objects between parties. Each Object Version may only be mutated a single time, when a Command succeeds. Once it has been mutated, a new Version is created to represent the latest state of the Object, the old Version can be garbage-collected. This results in what is essentially a per-object sequencing.
Both _reads
and _writes
must be exact - meaning that if unused dependencies are specified or insufficient dependencies are specified, the Command will be rejected.
Sample generation of the cid
can be done with something similar to (in python3
syntax):
Protocol Server and Client Roles
In each Channel, one VASP takes the role of a protocol server and the other the role of a protocol client for the purposes of simplifying Shared Object locking / state management. Note that these roles are distinct to the HTTP client/server -- and both VASPs act as an HTTP server and client to listen and respond to requests. To avoid excessive locking and intermediate state management during API requests, by convention the server acts as the source of truth for the state of an Object. In practice, this means that in the case of lock contention on a Shared Object, the server Command is prioritized.
The protocol server/client role is determined by comparing their binary on-chain Address strings (we call those the binary address. The following rules are used to determine which entity serves as which party: The last bit of VASP A’s parent binary address w (where w = addr[15] & 0x1
) is XOR’d with the last bit in VASP B’s parent binary address x. This results in either 0 or 1. (Refer to https://github.com/libra/libra/tree/master/client/libra-dev#vasp-account-roles for additional information on parent/child VASP accounts).
If the result is 0, the lexicographically lower parent address is used as the server side.
If the result is 1, the lexicographically higher parent address is used as the server side. Lexicographic ordering determines which binary address is higher by comparing byte positions one by one, and returning the address with the first higher byte.
Network Error Handling
In the case of network failure, the sending party for this Command is required to re-send the Command until it gets a response from the counterparty VASP. An exponential backoff is suggested for Command re-sends. This retransmission strategy allows the Off-Chain Protocol to achieve eventual consistency of the Shared Objects. Retransmission must be done regardless of the role of the VASP (protocol client or protocol server).
Upon receipt of a Command that has already been processed (resulting in a success response or a Command error), the receiving side must reply with the same response as was previously issued. Requests that resulted in protocol errors (such as wait
) may result in different responses. For example if VASP A sends a Requests to VASP B, and VASP B responds with a wait
protocol error, later VASP A may re-send the same request, and VASP B may respond with a success
or a different error.
Request/Response Payload
All requests between VASPs are structured as CommandRequestObject
s and all responses are structured as CommandResponseObject
s. The resulting request takes a form of the following (prior to JWS):
A response would look like the following:
CommandRequestObject
All requests between VASPs are structured as CommandRequestObject
s.
Field | Type | Required? | Description |
---|---|---|---|
_ObjectType | str | Y | Fixed value: CommandRequestObject |
command_type | str | Y | A string representing the type of Command contained in the request. |
command | Command object | Y | The Command to sequence. |
cid | str | Y | A unique identifier for the Command. |
CommandResponseObject
All responses to a CommandRequestObject are in the form of a CommandResponseObject
Field | Type | Required? | Description |
---|---|---|---|
_ObjectType | str | Y | The fixed string CommandResponseObject . |
status | str | Y | Either success or failure . |
error | List of OffChainErrorObject | N | Details on errors when status == "failure" |
cid | str | N | The Command identifier to which this is a response. |
When the CommandResponseObject
status field is failure
, the error
field is included in the response to indicate the nature of the failure. The error
field (type OffChainError
) is a list of OffChainError objects. In case of failure to recover the request (such as a parsing failure or a signature failure) cid
may be omitted.
OffChainErrorObject
Represents an error that occurred in response to a Command.
Field | Type | Required? | Description |
---|---|---|---|
type | str (enum) | Y | Either "command_error" or "protocol_error" |
field | str | N | The field on which this error occurred |
code | str (enum) | Y | The error code of the corresponding error |
message | str | N | Additional details about this error |
'command_error' occurs in response to a Command failing to be applied - for example, invalid _reads
values, or high level validation errors. protocol_error
occurs in response to a failure related to the lower-level protocol. Note: Additional information detailing all errors will be added.
Travel Rule Data Exchange
In the initial version of the off-chain APIs, the usage is intended as a means of transferring travel-rule information between VASPs. The following will detail the request and response payloads utilized for this purpose.
Request/Response Payload
All requests between VASPs are structured as CommandRequestObject
s and all responses are structured as CommandResponseObject
s. For a travel-rule data exchange, the resulting request takes a form of the following:
A response would look like the following:
CommandRequestObject
For a travel rule data exchange, the command_type field is set to "PaymentCommand". The Command Object is a PaymentCommand
Object.
PaymentCommand Object
Field | Type | Required? | Description |
---|---|---|---|
_ObjectType | str | Y | The fixed string PaymentCommand |
payment | PaymentObject | Y | contains a PaymentObject that either creates a new payment or updates an existing payment. Note that strict validity check apply when updating payments, that are listed in the section below describing these Objects. An invalid update or initial payment Object results in a Command error |
_reads | JSON Object map | Y | Must be an Object containing mappings of reference_id to latest Version as represented by the cid of the latest Command which successfully mutated the Object referenced by the reference_id . The value in this field must match a cid previously specified by a Command by the _writes parameter on a prior Command for this payment Object. For a payment Command, only zero or one _reads values should be specified since payments are only dependent upon at most prior Version of an Object. A list with any other number of items results in a Command error. If the list is empty this payment Command defines a new payment. If the list contains one item, then this Command updates the shared PaymentObject . |
_writes | JSON object map | Y | For a payment Command, the _writes field may only be a single key-value pair since a payment Command only operates upon one Object. This field maps the reference_id of the Object being written to its new Version. |
PaymentObject
The structure in this Object can be a full payment or just the fields of an existing payment Object that need to be changed. Some fields are immutable after they are defined once (see below). Others can by updated multiple times. Updating immutable fields with a different value results in a Command error, but it is acceptable to re-send the same value.
Field | Type | Required? | Description |
---|---|---|---|
sender/receiver | PaymentActorObject | Required for payment creation | Information about the sender/receiver in this payment |
reference_id | str | Y | Unique reference ID of this payment on the payment initiator VASP (the VASP which originally created this payment Object). This value should be unique, and formatted as “<creatorvasp_onchain_address_bech32><unique_id>”. For example, ”lbr1x23456abcd_seqABCD“. This field is mandatory on payment creation and immutable after that. "unique_id" should be 64 or fewer characters. |
original_payment_reference_id | str | N | Used for updates to a payment after it has been committed on chain. For example, used for refunds. The reference ID of the original payment will be placed into this field. This value is optional on payment creation and invalid on updates. |
recipient_signature | str | N | Signature of the recipient of this transaction encoded in hex. The is signed with the compliance key of the recipient VASP and is used for on-chain attestation from the recipient party. This may be omitted on blockchains which do not require on-chain attestation. Generated via Recipient Signature |
action | PaymentActionObject | Y | Number of cryptocurrency + currency type (LBR, etc.)1 + type of action to take. This field is mandatory and immutable |
description | str | N | Description of the payment. To be displayed to the user. Unicode utf-8 encoded max length of 255 characters. This field is optional but can only be written once. |
PaymentActorObject
A PaymentActorObject
represents a participant in a payment - either sender or receiver. It also includes the status of the actor, indicates missing information or willingness to settle or abort the payment, and the Know-Your-Customer information of the customer involved in the payment.
Field | Type | Required? | Description |
---|---|---|---|
address | str | Y | Address of the sender/receiver account. Addresses may be single use or valid for a limited time, and therefore VASPs should not rely on them remaining stable across time or different VASP addresses. The addresses are encoded using bech32. The bech32 address encodes both the address of the VASP as well as the specific user's subaddress. They should be no longer than 80 characters. Mandatory and immutable. For Libra addresses, refer to "account identifier" section in LIP-5 for format. |
kyc_data | KycDataObject | N | The KYC data for this account. This field is optional but immutable once it is set. |
status | StatusObject | Y | Status of the payment from the perspective of this actor. This field can only be set by the respective sender/receiver VASP and represents the status on the sender/receiver VASP side. This field is mandatory by this respective actor (either sender or receiver side) and mutable. |
metadata | list of str | Y | Can be specified by the respective VASP to hold metadata that the sender/receiver VASP wishes to associate with this payment. This is a mandatory field but can be set to an empty list (i.e. [] ). New string-typed entries can be appended at the end of the list, but not deleted. |
KYCDataObject
A KYCDataObject
represents the required information for a single subaddress. Proof of non-repudiation is provided by the signatures included in the JWS payloads. The only mandatory fields are payload_type
, payload_version
and type
. All other fields are optional from the point of view of the protocol -- however they may need to be included for another VASP to be ready to settle the payment.
Field | Type | Required? | Description |
---|---|---|---|
payload_type | str | Y | Used to help determine what type of data this will deserialize into. Always set to KYC_DATA. |
payload_version | str | Y | Version identifier to allow modifications to KYC data Object without needing to bump version of entire API set. Set to 1 |
type | str | Y | Required field, must be either “individual” or “entity” |
given_name | str | N | Legal given name of the user for which this KYC data Object applies. |
surname | str | N | Legal surname of the user for which this KYC data Object applies. |
address | AddressObject | N | Physical address data for this account |
dob | str | N | Date of birth for the holder of this account. Specified as an ISO 8601 calendar date format: https://en.wikipedia.org/wiki/ISO_8601 |
place_of_birth | AddressObject | N | Place of birth for this user. line1 and line2 fields should not be populated for this usage of the address Object |
national_id | NationalIdObject | N | National ID information for the holder of this account |
legal_entity_name | str | N | Name of the legal entity. Used when subaddress represents a legal entity rather than an individual. KYCDataObject should only include one of legal_entity_name OR given_name/surname |
additional_kyc_data | str | N | Freeform KYC data. If a soft-match occurs, this field should be used to specify additional KYC data which can be used to clear the soft-match. It is suggested that this data be JSON, XML, or another human-readable form. |
AddressObject
Represents a physical address
Field | Type | Required? | Description |
---|---|---|---|
city | str | N | The city, district, suburb, town, or village |
country | str | N | Two-letter country code (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) |
line1 | str | N | Address line 1 |
line2 | str | N | Address line 2 - apartment, unit, etc. |
postal_code | str | N | ZIP or postal code |
state | str | N | State, county, province, region. |
NationalIdObject
Represents a national ID.
Field | Type | Required? | Description |
---|---|---|---|
id_value | str | Y | Indicates the national ID value - for example, a social security number |
country | str | N | Two-letter country code (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) |
type | str | N | Indicates the type of the ID |
PaymentActionObject
Field | Type | Required? | Description |
---|---|---|---|
amount | uint | Y | Amount of the transfer. Base units are the same as for on-chain transactions for this currency. For example, if LibraUSD is represented on-chain where “1” equals 1e-6 dollars, then “1” equals the same amount here. For any currency, the on-chain mapping must be used for amounts. |
currency | enum | Y | One of the supported on-chain currency types - ex. LBR, etc. 1 |
action | enum | Y | Populated in the request. This value indicates the requested action to perform, and the only valid value is charge . |
timestamp | uint | Y | Unix timestamp indicating the time that the payment Command was created. |
StatusObject
Field | Type | Required? | Description |
---|---|---|---|
status | str enum | Y | Status of the payment from the perspective of this actor. This field can only be set by the respective sender/receiver VASP and represents the status on the sender/receiver VASP side. This field is mandatory by this respective actor (either sender or receiver side) and mutable. Valid values are specified in StatusEnum |
abort_code | str (enum) | N | In the case of an abort status, this field may be used to describe the reason for the abort. Represents the error code of the corresponding error |
abort_message | str | N | Additional details about this error. To be used only when code is populated |
StatusEnum
Valid values are:
none
- No status is yet set from this actor.needs_kyc_data
- KYC data about the subaddresses is required by this actor.needs_recipient_signature
- Can only be associated with the sender actor. Means that the sender still requires that the recipient VASP provide the signature so that the transaction can be put on-chain.ready_for_settlement
- Transaction is ready for settlement according to this actor (i.e. the required signatures/KYC data have been provided)abort
- Indicates the actor wishes to abort this payment, instead of settling it.pending_review
- Payment is pending review.soft_match
- Actor's KYC data resulted in a soft-match. The VASP associated with this actor will need to determine what next steps are required, and if it decides to send additional KYC information to clear the soft-match does so via the KYCObject field of additional_kyc_data. If not sent within SLA window, this transaction will be aborted.
Valid Status Transitions. Each side of the transaction is only allowed to mutate their own status (sender or receiver), and upon payment creation may only set the status of the other party to none
. Subsequently, each party may only modify their own status to a higher or equal status in the order none
, (needs_kyc_data
, needs_recipient_signature
, pending_review
, soft_match
), (ready_for_settlement
, abort
).
A status of abort
on either side is terminal and the payment must not be changed subsequently.
A status of ready_for_settlement
on both sides is also terminal, and indicates that the payment should be settled on-chain and not modified further. It is therefore safe for a VASP sending funds to initiate an On-Chain payment to settle an Off-chain payment after it observed the other party setting their status to ready_for_settlement
and it is also willing to go past this state.
A state of pending_review
may be set to indicate a manual review is taking place. This status is informational and the other VASP is not expected to take any action. The pending_review
status may result in any of soft_match
, ready_for_settlement
, or abort
.
In the event of a soft match, the VASP associated with this actor will need to determine what next steps are required, and if it decides to send additional KYC information to clear the soft-match does so via the KYCObject field of additional_kyc_data. After human review of this data, this state may result in any of ready_for_settlement or abort (abort if the soft-match was unable to be cleared). If data is not received within a reasonable SLA (suggested to be 24 hours), this state will result in abort. The VASP providing the additional KYC data is also allowed to abort the transaction at any point if they do not have additional KYC data or do not wish to supply it.
Recipient Signature
Once the recipient side is comfortable that it has received appropriate information and is ready for the transaction to go on-chain, the recipient side will provide their signature in order to support dual attestation. The receiver VASP will sign with the receiver compliance private key. The following is a concrete example of how to generate the Travel Rule dual attestation signable and the recipient signature using Libra Python Client SDK:
The receiver_metadata_signature
will then be encoded in hex and placed into the recipient_signature
field of the PaymentObject in the off-chain API, thus providing it to the sender so that the sender VASP may submit the transaction on-chain.
On-chain Transaction Submission
Once both sender and receiver have a status of 'ready_for_settlement', the transaction may then be submitted on-chain by the sender VASP. This submission will utilize the recipient_signature
which was provided by the receiver VASP (generated via Recipient Signature. The sender VASP will now generate a transaction via the following and then submit the transaction on-chain:
For additional details, see https://lip.libra.org/lip-4/#c-to-c-transaction-flow
Reference Implementation
A reference implementation of the Off-Chain Protocol is located at https://github.com/libra/off-chain-reference.
Disclaimers
THIS API AND REFERENCE IMPLEMENTATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, COMPLIANCE WITH LAW, ACCURACY, COMPLETENESS, OR NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS.
The Libra Association disclaims all liability relating to this API and to the implementation of this API, including the reference implementation, and disclaims all liability for cost of procurement of substitute services, lost profits, loss of use, loss of data or any incidental, consequential, direct, indirect, or special damages, whether under contract, tort, warranty or otherwise, arising in any way out of use or reliance upon this API, the reference implementation, or any information herein.
The compliance processes described in this LIP are for informational purposes only and do not reflect the specific compliance obligations of VASPs under applicable regulatory frameworks, their compliance programs, and/or standards imposed by Libra Networks.
Copyright Notice
This documentation is made available under the Creative Commons Attribution 4.0 International (CC BY 4.0) license (available at https://creativecommons.org/licenses/by/4.0/).