The five-bells-ledger
WebSocket API provides a way for real-time access to the ledger.
The URL for the websocket endpoint is available by GET
ting the ledger's main URI (metadata endpoint) and inspecting the urls.websocket
property. Example:
GET / HTTP/1.1
Host: ledger.example
HTTP/1.1 200 OK
Content-Type: application/json
{
"urls": {
"websocket": "wss://ledger.example/websocket"
}
}
Unfortunately, browsers do not support setting a custom auth header (for Basic
auth from a browser client) for websocket connections. So in order to support any type of client, we need to use token-based authentication.
The client first makes a regular HTTP request to obtain a token:
POST /authenticate HTTP/1.1
Accept: application/json
Authorization: Basic c3RlZmFuOnN0ZWZhbg==
HTTP 200 OK
Content-Type: application/json
{ "token": "...xyz..." }
Note that the authenticate request fails if the user does not successfully authenticate. Anonymous requests are not allowed.
The client then uses this token as a query parameter when initiating the websocket connection:
GET /websocket?token=...xyz... HTTP/1.1
Connection: Upgrade
Upgrade: websocket
HTTP 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
The websocket client is now treated as the user who initiated the /authenticate
request. Anonymous connections (without token) are disallowed.
The token is simply an opaque string from the client's perspective. The server may use a technology like JWT to generate the token.
In JSON-RPC 2.0 requests always go from "client" to "server" and responses always go from "server" to "client". Each response must be solicited by a prior request. In order to implement a publish/subscribe flow, the ledger must be able to send unsolicited messages. Therefore, the ledger and the TCP client must be acting as both JSON-RPC client and JSON-RPC server.
The examples in this document use the following convention:
--> data sent from Client to Ledger
<-- data sent from Ledger to Client
subscribe_account({ eventType: String, accounts: String[] }) ⇒ null
Subscribe to events related to zero or more accounts.
Parameters
-
eventType
- Scope of the subscription. Valid scopes are:"transfer.create"
- Triggered for newly created transfer"transfer.update"
- Triggered for transfer changes (e.g. state changes)
Note that a wildcard may be used to refer to multiple events, e.g.
transfer.*
would subscribe to all transfer events. -
accounts
- List of accounts to subscribe to.Admins are allowed to subscribe to any account whereas regular users are allowed only to subscribe to their own account.
--> {
"jsonrpc": "2.0",
"method": "subscribe_account",
"params": [{
"eventType": "transfer.*",
"accounts": ["https://ledger.example/accounts/alice"]
}],
"id": 1
}
<-- {
"jsonrpc": "2.0",
"result": 19,
"id": 1
}
<-- {
"jsonrpc": "2.0",
"method": "notify",
"params": [{
"id": "https://ledger.example/notifications/c92f2a2c-b21d-4e6c-96e7-4f6d6df4bee9",
"event": "transfer.update",
"resource": {
"id": "https://ledger.example/transfers/155dff3f-4915-44df-a707-acc4b527bcbd",
"ledger": "http://localhost",
"debits": [{
"account": "https://ledger.example/accounts/alice",
"amount": "10",
"authorized": true
}],
"credits": [{
"account": "https://ledger.example/accounts/bob",
"amount": "10"
}],
"state": "executed"
}
}],
"id": null
}
<-- {
"jsonrpc": "2.0",
"method": "notify",
"params": [{
"id": "https://ledger.example/notifications/52a42d6f-8d9c-4c05-b31c-cccc8bbdb30d",
"event": "transfer.update",
"resource": { /* transfer resource */ },
"related_resources": {
"execution_condition_fulfillment": "[ fulfillment ]",
"cancellation_condition_fulfillment": "[ fulfillment ]"
}
}],
"id": null
}
subscribe_all_accounts({ eventType: String }) ⇒ null
Subscribe to account-related events from all accounts. Requires admin permission.
Does
the ledger and the TCP client must be acting as both JSON-RPC client and JSON-RPC server
mean thatJSON-RPC
isn't really meant for this use case?For the
eventType
, would you consider the transfer being executed, rejected or cancelled an "update"?What is the
result: 19
in the subscribe response?