Skip to content

feat(api): add support for websockets #48

@rixcian

Description

@rixcian

Summary

Create a WebSocket server for ar.io gateways to enable users/developers to subscribe to events on Arweave.

Motivation

There's no service that would enable event subscription in the Arweave ecosystem via websockets.

Use cases for devs

  • Wallets can subscribe to an event when a certain transaction gets mined
  • Other indexers or backends can subscribe to transactions with specific tags
  • Other use-cases coming from a fact that you can particularly sub to any event, e.g. new block mined, new tx mined that meets specific ANS, ...

Other motivations

  • The big advantage of WebSockets is that after the handshake procedure, the overhead of individual messages is low, making it good for sending a high number of requests.
  • Currently, there is no real-world use for indexed data in the database; this feature would finally give indexing some meaning
  • As mentioned above, there's no solution for ws in the space right now, so it would be a good selling point for ar.io
  • Note: This feature will be totally optional by enabling the WS_ENABLED variable in .env

Specification

Because WebSockets doesn't follow any strict rules/protocols there's a need to create its own communication protocol.

This is a type specification of each message in this new protocol:

{
  "method": "<specify the type of action; e.g. subscribe, unsubscribe, error states, etc.>",
  "event": "<specify type of event you want to sub; only used in 'ar_subscribe method'>",
  "params": "<specify parameters of the event; for every event/method it's different>",
  "result": "<specify the result data; used only by server for returning data>"
}

Example of communication

It works by subscribing to particular events. The gateway will return a subscription ID. For each event that matches the subscription, a notification with relevant data is sent together with the subscription ID.

  1. Create subscription (this example: subscribe to all new txs):
{
  "method": "ar_subscribe",
  "event": "new_tx",
  "params": null,
}
  1. Response from ar.io gateway (returns a subscription id - uuid v4):
{
  "method": "ar_subscribe_accepted",
  // Subscription ID
  "result": "bd52673d-832f-4b34-a79d-79058e7f6989",
}
  1. After the creation of the subscription, the client will receive a notification in this format:
{
  "method": "ar_subscribe_msg",
  "params": {
    "subscription": "bd52673d-832f-4b34-a79d-79058e7f6989"
  },
  "result": [
    { "id": "FPDHr4gKD...yG2I", "owner": "sb7fS07r5...AVm0", "tags": [...], ... },
    { "id": "4d0G6Nk4z...hm44", "owner": "pnqyui51Q...jLvQ", "tags": [...], ... },
    ...
  ],
}
  1. If the client wants to cancel the subscription:
{
  "method": "ar_unsubscribe",
  "params": {
    "subscription": "bd52673d-832f-4b34-a79d-79058e7f6989",
  },
}

Event methods

  • ar_subscribe
  • ar_subscribe_accepted
  • ar_subscribe_denied
  • ar_subscribe_msg
  • ar_unsubscribe

Event types

  • new_tx
    • params:
      • owner: string
      • target: string
      • tags: { name: string, value: string }[]
    • result: Transaction[]
  • new_block
    • params: none
    • result: Block

Rate-limiting

  • Gateway operator could be able to somehow rate-limit the ws server by:

This would probably need some discussion if it's needed or not.

ENVs

WS_ENABLED | type: boolean | default: false | desc: Start the WebSocket server
WS_PORT | type: number | default: 3333 | desc: Port of the WebSocket server
WS_CORS | type: string | default: * | desc: Allow access from specific origin

Implementation details

  • Use of the ws module in Node.js should be sufficient
  • Use of redis (or dragonflydb) in-memory db for storing subscription IDs and params or use just own implementation of some structure?

Considerations

  • Notifications are sent for current events and not for past events
  • Subscriptions are coupled to a connection. If the connection is closed all subscriptions that are created over this connection are removed

It's the first blueprint, so there will likely be some blind spots that I've missed. Feedback is highly appreciated. :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions