forked from Consensys/gpact
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve Message Observer design doc (#99)
* Update Message Observer documentation * Improve message observer design document
- Loading branch information
Showing
2 changed files
with
73 additions
and
27 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,103 @@ | ||
### Overview | ||
The Message Observer is a [protocol-specific component](./relayer-design.md#design-considerations) that subscribes to state-change events on a Blockchain, annotates these events with additional metadata, transforms them to a common message format and sends them to the Relayer core for processing. The component abstracts away blockchain specific details from the Relayer core and offers a plugin-point for supporting new network types and messaging schemes, without affecting other Relayer components. | ||
## Overview | ||
|
||
An instance of this service can be very specialised (e.g. specific blockchain network and a bridging contract), or more general in nature. The former is conceptually simpler to work with and is the recommended approach for implementing, instantiating and operating observers. | ||
The Message Observer is a [protocol-specific component](./relayer-design.md#design-considerations) that subscribes to | ||
state-change events on a Blockchain, annotates these events with additional metadata, transforms them to a common | ||
message format and sends them to the [*Relayer Core*](relayer-design.md#relayer-core) for processing. The component | ||
abstracts away blockchain specific details from the *Relayer Core* and offers a plugin-point for supporting new network | ||
types and messaging schemes, without affecting other Relayer components. | ||
|
||
An instance of this service can be very specialised (e.g. specific blockchain network and a bridging contract), or more | ||
general in nature. The former is conceptually simpler to work with and is the recommended approach for implementing, | ||
instantiating and operating observers. | ||
|
||
## Key Features | ||
|
||
### Key Features | ||
The Message Observer performs the following set of core functionalities: | ||
|
||
- Listen to relevant events originating from a source network | ||
- Process and transform such events to a [common, protocol-agnostic message format](./relayer-design.md#message-format) | ||
- Send messages to the core service for further processing, routing and dispatching | ||
|
||
### Design | ||
A Message Observer communicates with the Relayer core asynchronously through message queues. | ||
It consists of four key interfaces: `Event Watcher`, `Event Handler`, `Event Transformer` and `Message Handler`. Each of these interfaces can be extended to create observers with more complex behaviours. | ||
## Design | ||
|
||
A *Message Observer* communicates with the *Relayer Core* asynchronously through message queues. It consists of four key | ||
interfaces: *Event Watcher*, *Event Handler*, *Event Transformer* and *Message Handler*. Each of these interfaces can | ||
be extended to create observers with more complex behaviours. | ||
|
||
<p align="center"> | ||
<img src="images/protocol-adapter-observer.png" width="700"/> | ||
</p> | ||
<p align="center">Figure 1: Message Observer can be extended to observe different types of state-change events (example-only)</p> | ||
|
||
### Event Watcher | ||
|
||
#### Event Watcher | ||
An `Event Watcher` is responsible for listening to events of interest in a given blockchain, and passing them to an `Event Handler` for processing. Watchers can be designed or configured to filter for different types of events. There are currently two implementations of this interface, `Realtime Event Watcher` and `Finalised Event Watcher`. The distinction between the two implementations is based on whether or not block confirmations are required. | ||
An *Event Watcher* is responsible for listening to events of interest in a given blockchain, and passing them to an * | ||
Event Handler* for processing. Watchers can be designed or configured to filter for different types of events. There are | ||
currently two implementations of this interface, *Realtime Event Watcher* and *Finalised Event Watcher*. The distinction | ||
between the two implementations is based on whether or not block confirmations are required. | ||
|
||
Implementations of the *Event Watcher* interface offer two important *guarantees* to downstream consumers: | ||
|
||
Implementations of the `Event Watcher` interface offer two important *guarantees* to downstream consumers: | ||
1. Only events that match configured filter criteria are delivered (e.g. specific contract address, event type) | ||
1. All relevant events will eventually be delivered | ||
1. Events delivered will be unique | ||
|
||
However, it does **not** guarantee that: | ||
|
||
- Events delivered will be unique | ||
- Events are delivered sequentially | ||
- Events will not be affected by reorgs | ||
|
||
##### Realtime Event Watcher | ||
Listens for relevant events in realtime (as they are included into a block) and sends them over to an `Event Handler` for processing. This offers a very low latency mechanism for working with events. However, it is very likely that such events could be affected by reorgs, and so this component offers no assurance around *finality* of relayed events. | ||
#### Realtime Event Watcher | ||
|
||
Listens for relevant events in realtime (as they are included into a block) and sends them over to an *Event Handler* | ||
for processing. This offers a very low latency mechanism for working with events. However, it is very likely that such | ||
events could be affected by reorgs, and so this component offers no assurance around *finality* of relayed events. | ||
|
||
#### Finalised Event Watcher | ||
|
||
Unlike a *Realtime Event Watcher*, a *Finalised Event Watcher* waits until an event receives a specified number of block | ||
confirmations before relaying the event to a handler. The figure below illustrates how this works for a *Finalised Event Watcher* configured to wait for *n+1* confirmations. | ||
Provided block confirmations are set sufficiently high, this component offers better confidence around *finality*. However, this comes at the cost of higher latency. | ||
|
||
<p align="center"> | ||
<img src="images/finalized-event-watcher.png" width="780"/> | ||
</p> | ||
<p align="center">Figure 2: Finalised Event Watcher processes blocks only once they receive a configured number of confirmations (n+1 in this example)</p> | ||
|
||
### Event Handler | ||
|
||
Is responsible for initiating (or even managing) a workflow that takes a raw event from an *Event Watcher*, processing | ||
it and ultimately sending it to the Relayer core. It relies on other component types for this purpose, i.e. other *Event | ||
Handlers*, *Event Transformers* and *Message Handlers*. There is currently a one implementation of this interface, * | ||
Simple Event Handler*, which executes a simple workflow that first transforms a raw event to a common message format ( | ||
using an *Event Transformer*) and then passes the message to a *Message Handler*. | ||
|
||
##### Finalised Event Watcher | ||
Unlike a `Realtime Event Watcher`, a `Finalised Event Watcher` waits until an event receives a specified number of block confirmations before relaying the event to a handler. Provided block confirmations are set sufficiently high, this component offers better confidence around *finality*. However, this comes at the cost of higher latency. | ||
### Event Transformer | ||
|
||
// todo: diagram and better explanation of how this component works. | ||
Converts a raw blockchain event to a [common, protocol-agnostic message format](./relayer-design.md#message-format), | ||
that can be consumed by downstream Relayer components. The primary guarantee offered by this component is that its | ||
output is always deterministic, for a given event. | ||
|
||
#### Event Handler | ||
Is responsible for initiating (or even managing) a workflow that takes a raw event from an `Event Watcher`, processing it and ultimately sending it to the Relayer core. It relies on other component types for this purpose, i.e. other `Event Handlers`, `Event Transformers` and `Message Handlers`. There is currently a one implementation of this interface, `Simple Event Handler`, which executes a simple workflow that first transforms a raw event to a common message format (using an `Event Transformer`) and then passes the message to a `Message Handler`. | ||
The event transformer is also responsible for computing a deterministic message ID for a given event. The important | ||
properties that the employed ID scheme offers are: | ||
|
||
#### Event Transformer | ||
Converts a raw blockchain event to a [common, protocol-agnostic message format](./relayer-design.md#message-format), that can be consumed by downstream Relayer components. The primary guarantee offered by this component is that its output is always deterministic, for a given event. | ||
1. Deterministic: The message ID should be deterministic for two reasons: a) different Relayers should be able to | ||
independently compute the same ID for a given event b) a duplicate event can easily be detected by downstream | ||
processes. | ||
2. Semantic and reversible: Not only should an ID be computable from an event, but given a message ID an entity should | ||
be able to determine the specific event on the blockchain being referenced. One of the benefits of this is that, | ||
communication overheads between relayers when orchestrating joint signatures can be reduced by just referencing | ||
message IDs instead of sending complete message payloads. | ||
|
||
The event transformer is also responsible for computing a deterministic message ID for a given event. The important properties that the employed ID scheme offers are: | ||
1. Deterministic: The message ID should be deterministic for two reasons: a) different Relayers should be able to independently compute the same ID for a given event b) a duplicate event can easily be detected by downstream processes. | ||
2. Semantic and reversible: Not only should an ID be computable from an event, but given a message ID an entity should be able to determine the specific event on the blockchain being referenced. One of the benefits of this is that, communication overheads between relayers when orchestrating joint signatures can be reduced by just referencing message IDs instead of sending complete message payloads. | ||
However, the degree to which the ID scheme can guarantee the above properties is reliant on upstream processes | ||
mitigating the possibility of reorgs. | ||
|
||
However, the degree to which the ID scheme can guarantee the above properties is reliant on upstream processes mitigating the possibility of reorgs. | ||
The current ID scheme implemented for a message involves the following structure: * | ||
{network_id}/{contract_address}/{block_number}/{tx_index}/{log_index}*. | ||
|
||
The current ID scheme implemented for a message involves the following structure: `{network_id}/{contract_address}/{block_number}/{tx_index}/{log_index}`. | ||
### Message Handler | ||
|
||
#### Message Handler | ||
Similar to the `Event Handler` the `Message Handler` is responsible for initiating (or even managing) a workflow that takes a message, processes it and ultimately enqueues it onto a message queue for consumption by the Relayer core. There is currently only a single implementation of this interface, `Message Enqueue Handler`, which sends a given message to a specified queue. | ||
Similar to the *Event Handler* the *Message Handler* is responsible for initiating (or even managing) a workflow that | ||
takes a message, processes it and ultimately enqueues it onto a message queue for consumption by the Relayer core. There | ||
is currently only a single implementation of this interface, *Message Enqueue Handler*, which sends a given message to a | ||
specified queue. |