State: draft Created: 2020-11-17
The syncer
's main function is to maintain the protocol state and the blockchain state in sync.
This RFC describes the syncer interface: tasks
and blockchain events
. Through this interface, a daemon bi-directionally interacts with a blockchain.
A syncer is responsible for carrying out assigned tasks
received through incoming tasks
messages, which are its sole input messages from the daemon
. To achieve that goal a syncer connects to a blockchain, through a full node or equivalent, and uses e.g. RPC calls and 0MQ notification streams to compute and emit blockchain events
messages, its output messages.
Tasks MUST follow those rules:
- The same task MUST be publishable multiple times without causing contradictory side effects
- A task published more than once MUST always produce an equivalent set of events
- Tasks MUST have a defined lifetime
Tasks are available from any syncer, yet their parameters vary depending on the network. Parameters are provided for Bitcoin and Monero to demonstrate how the protocol would be implemented. Any codebase working with Bitcoin or Monero SHOULD use the following definitions.
This RFC lists available tasks that MUST be supported by syncers, unless noted otherwise, and their respective output messages: blockchain events
. A task produces zero or more events
during its lifetime.
Every task is accompanied by an id
, a positive integer that fits into the 32-bit signed integer space. This integer has no rules on its generation or any pattern required by its usage. Every emitted event has its own id
field corresponding to the task which caused it.
The error
event is valid for every single task, and it takes the task's ID and provides an integer code
, as well as optionally a string message
.
This document frequently references epochs. The definition of epochs is dependent on the protocol in question. For Bitcoin, Monero, and other blockchain-based systems, the epoch is the block height. For systems without the context of a block height, Unix timestamps SHOULD be used.
Aborts the task with id id
.
- type: ? (
abort
) - data:
- [
i32
:id
]: The task identifier that shall be aborted
- [
To update a task with identifier id
, Syncer must receive an abort
task aborting with id
parameter, and subsequently submit a new instance of this task which shall have a new id
.
watch_height
asks the syncer for notifications about updates to the blockchain's height
and associated block
id. This task MUST be implemented for any coin with a blockchain. This task MAY be implemented for any coin without a blockchain. If it is not implemented, an error
event must be sent in response to any attempt to start this task.
Required parameters are:
- type: ? (
watch_height
) - data:
- [
i32
:id
]: The task identifier - [
u64
:lifetime
]: Epoch at which the syncer SHOULD drop this task. Until then, this task MUST be maintained, barring the case where anabort
task aborts it.
- [
Parameters may be added to specify which blockchain, in order to support any network utilizing multiple.
watch_address
asks the syncer for notifications about when a specified address is involved in a transaction.
Required parameters are:
- type: ? (
watch_address
) - data:
- [
i32
:id
]: The task identifier - [
u64
:lifetime
]: Epoch at which the syncer SHOULD drop this task. Until then, this task MUST be maintained, barring the case where anabort
task aborts it. - [
bool
:include_tx
]: Include the full serialized raw transaction if true. Do not include if false.
- [
For Bitcoin, the following additional parameters are defined:
- data:
- [
u16
:address_len
] - [
address_len * byte
:address
]: The address to watch. - [
u64
:from_height
]: Previous blocks to scan for transactions sent to the specified address.
- [
Bitcoin also has this task return historical transactions, with a possible rate limit being implementation-dependent.
For Monero, the following parameters:
- data:
- [
u16
:point_len
] - [
point_len * byte
:public_spend
]: The public spend key of the address. - [
u16
:scalar_len
] - [
scalar_len * byte
:private_view
]: The private view key of the address. - [
u64
:from_height
]: Previous blocks to scan for transactions sent to the specified address.
- [
address_transaction
events are emitted as a response to the watch_address
task.
watch_transaction
asks a syncer for updates on the status of a transaction.
Required parameters are:
- type: ? (
watch_transaction
) - data:
- [
i32
:id
]: The task identifier - [
sha256
:hash
]: Transaction hash. - [
u16
:confirmation_bound
]: Upper bound on the confirmation count until which the syncer should report updates on the block depth of the transaction. This task MUST be maintained until this threshold is reached or untillifetime
has passed. - [
u64
:lifetime
]: Epoch at which the syncer SHOULD drop this task. This task MUST be maintained until this threshold is reached or untilconfirmation_bound
has been reached.
- [
Once a transaction is seen by the syncer, and passed the confirmation threshold, a transaction_confirmations
event is emitted.
broadcast_transaction
tells a syncer to broadcast a transaction. The syncer MUST broadcast the transaction unless the transaction already in the blockchain or the mempool.
- type: ? (
broadcast_transaction
) - data:
- [
i32
:id
]: The task identifier - [
u16
:tx_len
] - [
tx_len * byte
:tx
]: The raw transaction in its serialized format.
- [
The blockchain event in response is the transaction_broadcasted
event.
The function of the Syncer is to emit events to accomplish its assigned tasks. Syncer's must emit events targeting the daemon (at a later stage of the project, potentially the client as well). Blockchain Events are produced by syncers in response to certain types of tasks
. A task MAY produce multiple blockchain event
messages. The blockchain event
messages are as defined below.
Event in response to abort
task, emitted only once.
- type: ? (
task_aborted
) - data:
- [
i32
:id
]: The task identifier aborted - [
i32
:success_abort
]: The status code, return 0 if task is aborted successfully
- [
watch_height
task was previously defined. That task emits height_changed
blockchain events. Upon new block arrival and reorgs, a height_changed
event is emitted. It contains:
- type: ? (
height_changed
) - data:
- [
i32
:id
]: The task identifier that emits this event - [
sha256
:block
]: Current block hash - [
u64
:height
]: Current blockchain height.
- [
Upon task reception, syncers MUST send a height_changed
event immediately to signify the current height.
Once a watch_address
address is involved in a transaction, address_transaction
is emitted. It contains:
- type: ? (
address_transaction
) - data:
- [
i32
:id
]: The task identifier that emits this event - [
sha256
:hash
]: Transaction ID. - [
u64
:amount
]: Value of the amount sent to the specified address. - [
u16
:tx_len
] - [
tx_len * byte
:tx
]: Include the raw transaction in its serialized format if the correspondingwatch_address
task'sinclude_tx
field is set totrue
, use0x0
, if it is set to false.
- [
Further fields may be defined depending on the asset. Any asset based on a blockchain MUST also have:
- data:
- [
sha256
:block
]: Hash of the block mining the transaction
- [
In response to the watch_transaction
task.
- type: ? (
transaction_confirmations
) - data:
- [
i32
:id
]: The task identifier that emits this event - [
sha256
:block
]: Hash of the block mining the transaction. - [
i32
:confirmations
]: Number of blocks afterblock
, return-1
forNone
- [
- Semantics:
- For transaction not seen on mempool, emit
(0x0, None)
- For transaction seen on mempool but not mined, emit
(0x0, Some(0))
- For transaction mined, emit
(tx_block, Some(confirmations))
wheretx_block
is the block that the transaction got mined, andconfirmations
is the number of blocks extendingtx_block
- When
confirmations
>=confirmation_bound
, emit(tx_block, Some(confirmation_bound))
. Hereafter,Task
is considered successful and terminates. At that time, transaction is considered final (=irreversible).
- For transaction not seen on mempool, emit
Thus:
for each transaction being watched by Syncer:
for every new block arrival extending the chain that the transaction was mined:
if confirmations <= confirmation_bound:
Syncer emits: `(tx_block, Some(confirmations))`
Upon block reorg reverting transaction out of blockchain, emit (0x0, Some(0))
if transaction shows back in mempool or (0x0, None)
if it's not in the mempool.
The broadcast_transaction
task defined above produces upon successful transaction broadcast a success output message: transaction_broadcasted
blockchain event.
- type: ? (
transaction_broadcasted
) - data:
- [
i32
:id
]: The task identifier that emits this event - [
u16
:tx_len
] - [
tx_len * byte
:tx
]: The raw transaction in its serialized format. - [
i32
:success_broadcast
]: The status code, return 0 if broadcast is successful
- [