mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 04:09:23 +00:00
Document the processor/tributary/coordinator/serai flow
This commit is contained in:
@@ -393,11 +393,11 @@ pub async fn handle_processors<D: Db, Pro: Processor, P: P2p>(
|
|||||||
|
|
||||||
let tributaries = tributaries.read().await;
|
let tributaries = tributaries.read().await;
|
||||||
let Some(tributary) = tributaries.get(&genesis) else {
|
let Some(tributary) = tributaries.get(&genesis) else {
|
||||||
// TODO: This can happen since Substrate tells the Processor to generate commitments
|
// TODO: This can happen since Substrate tells the Processor to generate commitments
|
||||||
// at the same time it tells the Tributary to be created
|
// at the same time it tells the Tributary to be created
|
||||||
// There's no guarantee the Tributary will have been created though
|
// There's no guarantee the Tributary will have been created though
|
||||||
panic!("processor is operating on tributary we don't have");
|
panic!("processor is operating on tributary we don't have");
|
||||||
};
|
};
|
||||||
|
|
||||||
let tributary = tributary.tributary.read().await;
|
let tributary = tributary.tributary.read().await;
|
||||||
if tributary
|
if tributary
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ async fn handle_block<D: Db, Pro: Processor>(
|
|||||||
// TODO: We can also full slash if shares before all commitments, or share before the
|
// TODO: We can also full slash if shares before all commitments, or share before the
|
||||||
// necessary preprocesses
|
// necessary preprocesses
|
||||||
|
|
||||||
|
// TODO: If this is shares, we need to check they are part of the selected signing set
|
||||||
|
|
||||||
// Store this data
|
// Store this data
|
||||||
let received =
|
let received =
|
||||||
TributaryDb::<D>::set_data(label, &mut txn, genesis, id, attempt, signed.signer, &bytes);
|
TributaryDb::<D>::set_data(label, &mut txn, genesis, id, attempt, signed.signer, &bytes);
|
||||||
|
|||||||
40
docs/coordinator/Coordinator.md
Normal file
40
docs/coordinator/Coordinator.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Coordinator
|
||||||
|
|
||||||
|
The coordinator is a service which communicates with all of the processors,
|
||||||
|
all of the other coordinators over a secondary P2P network, and with the Serai
|
||||||
|
node.
|
||||||
|
|
||||||
|
This document primarily details its flow with regards to the Serai node and
|
||||||
|
processor.
|
||||||
|
|
||||||
|
## New Set Event
|
||||||
|
|
||||||
|
On `validator_sets::pallet::Event::NewSet`, the coordinator spawns a tributary
|
||||||
|
for the new set. It additionally sends the processor
|
||||||
|
`key_gen::CoordinatorMessage::GenerateKey`.
|
||||||
|
|
||||||
|
## Generated Key Pair
|
||||||
|
|
||||||
|
On `key_gen::ProcessorMessage::GeneratedKeyPair`, a
|
||||||
|
`validator_sets::pallet::vote` transaction is made to vote in the new key.
|
||||||
|
|
||||||
|
The Serai blockchain needs to know the key pair in order for it to be able to
|
||||||
|
publish `Batch`s. Additionally, having the Serai blockchain confirm the keys
|
||||||
|
provides a BFT consensus guarantee. While the tributary itself could also offer
|
||||||
|
a BFT consensus guarantee, there's no point when we'd then get BFT consensus
|
||||||
|
on the Serai blockchain anyways.
|
||||||
|
|
||||||
|
## Key Generation Event
|
||||||
|
|
||||||
|
On `validator_sets::pallet::Event::KeyGen`, the coordinator sends
|
||||||
|
`substrate::CoordinatorMessage::ConfirmKeyPair` to the processor.
|
||||||
|
|
||||||
|
# Update
|
||||||
|
|
||||||
|
On `key_gen::ProcessorMessage::Update`, the coordinator publishes an unsigned
|
||||||
|
transaction containing the signed batch to the Serai blockchain.
|
||||||
|
|
||||||
|
# Sign Completed
|
||||||
|
|
||||||
|
On `sign::ProcessorMessage::Completed`, the coordinator broadcasts the
|
||||||
|
contained information to all validators.
|
||||||
91
docs/coordinator/Tributary.md
Normal file
91
docs/coordinator/Tributary.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Tributary
|
||||||
|
|
||||||
|
A tributary is a side-chain, created for a specific multisig instance, used
|
||||||
|
as a verifiable broadcast layer.
|
||||||
|
|
||||||
|
## Transactions
|
||||||
|
|
||||||
|
### Key Gen Commitments
|
||||||
|
|
||||||
|
`DkgCommitments` is created when a processor sends the coordinator
|
||||||
|
`key_gen::ProcessorMessage::Commitments`. When all validators participating in
|
||||||
|
a multisig publish `DkgCommitments`, the coordinator sends the processor
|
||||||
|
`key_gen::CoordinatorMessage::Commitments`.
|
||||||
|
|
||||||
|
### Key Gen Shares
|
||||||
|
|
||||||
|
`DkgShares` is created when a processor sends the coordinator
|
||||||
|
`key_gen::ProcessorMessage::Shares`. When all validators participating in
|
||||||
|
a multisig publish `DkgShares`, the coordinator sends the processor
|
||||||
|
`key_gen::CoordinatorMessage::Shares`.
|
||||||
|
|
||||||
|
### External Block
|
||||||
|
|
||||||
|
When *TODO*, a `ExternalBlock` transaction is provided. This is used to have
|
||||||
|
the group acknowledge and synchronize around the block, without the overhead of
|
||||||
|
voting in its acknowledgment.
|
||||||
|
|
||||||
|
When a `ExternalBlock` transaction is included, participants are allowed to
|
||||||
|
publish transactions to produce a threshold signature for the block's `Batch`.
|
||||||
|
|
||||||
|
### Substrate Block
|
||||||
|
|
||||||
|
`SubstrateBlock` is provided when the processor sends the coordinator
|
||||||
|
`substrate::ProcessorMessage::SubstrateBlockAck`.
|
||||||
|
|
||||||
|
When a `SubstrateBlock` transaction is included, participants are allowed to
|
||||||
|
publish transactions for the signing protocols it causes.
|
||||||
|
|
||||||
|
### Batch Preprocess
|
||||||
|
|
||||||
|
`BatchPreprocess` is created when a processor sends the coordinator
|
||||||
|
`coordinator::ProcessorMessage::BatchPreprocess` and an `ExternalBlock`
|
||||||
|
transaction allowing the batch to be signed has already been included on chain.
|
||||||
|
|
||||||
|
When `t` validators have published `BatchPreprocess` transactions, a
|
||||||
|
`coordinator::ProcessorMessage::BatchPreprocesses` is sent to the processor.
|
||||||
|
|
||||||
|
### Batch Share
|
||||||
|
|
||||||
|
`BatchShare` is created when a processor sends the coordinator
|
||||||
|
`coordinator::ProcessorMessage::BatchShare`. The relevant `ExternalBlock`
|
||||||
|
transaction having already been included on chain follows from
|
||||||
|
`coordinator::ProcessorMessage::BatchShare` being a response to a message which
|
||||||
|
also has that precondition.
|
||||||
|
|
||||||
|
When the `t` validators who first published `BatchPreprocess` transactions have
|
||||||
|
published `BatchShare` transactions, a
|
||||||
|
`coordinator::ProcessorMessage::BatchShares` with the relevant shares is sent
|
||||||
|
to the processor.
|
||||||
|
|
||||||
|
### Sign Preprocess
|
||||||
|
|
||||||
|
`SignPreprocess` is created when a processor sends the coordinator
|
||||||
|
`sign::ProcessorMessage::Preprocess` and a `SubstrateBlock` transaction
|
||||||
|
allowing the transaction to be signed has already been included on chain.
|
||||||
|
|
||||||
|
When `t` validators have published `SignPreprocess` transactions, a
|
||||||
|
`sign::ProcessorMessage::Preprocesses` is sent to the processor.
|
||||||
|
|
||||||
|
### Sign Share
|
||||||
|
|
||||||
|
`SignShare` is created when a processor sends the coordinator
|
||||||
|
`sign::ProcessorMessage::Share`. The relevant `SubstrateBlock` transaction
|
||||||
|
having already been included on chain follows from
|
||||||
|
`sign::ProcessorMessage::Share` being a response to a message which
|
||||||
|
also has that precondition.
|
||||||
|
|
||||||
|
When the `t` validators who first published `SignPreprocess` transactions have
|
||||||
|
published `SignShare` transactions, a `sign::ProcessorMessage::Shares` with the
|
||||||
|
relevant shares is sent to the processor.
|
||||||
|
|
||||||
|
## Re-attempts
|
||||||
|
|
||||||
|
The key generation and signing protocols, whether batch or transaction, may
|
||||||
|
fail if a validator goes offline or takes too long to respond. Accordingly,
|
||||||
|
the tributary will schedule re-attempts. These are communicated with
|
||||||
|
`key_gen::CoordinatorMessage::GenerateKey`,
|
||||||
|
`coordinator::CoordinatorMessage::BatchReattempt`, and
|
||||||
|
`sign::CoordinatorMessage::Reattempt`.
|
||||||
|
|
||||||
|
TODO: Document the re-attempt scheduling logic.
|
||||||
118
docs/processor/Processor.md
Normal file
118
docs/processor/Processor.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Processor
|
||||||
|
|
||||||
|
The processor is a service which has an instance spawned per network. It is
|
||||||
|
responsible for several tasks, from scanning the connected network to signing
|
||||||
|
transactions with payments.
|
||||||
|
|
||||||
|
This document primarily discusses its flow with regards to the coordinator.
|
||||||
|
|
||||||
|
## Generate Key
|
||||||
|
|
||||||
|
On `key_gen::CoordinatorMessage::GenerateKey`, the processor begins a pair of
|
||||||
|
instances of the distributed key generation protocol specified in the FROST
|
||||||
|
paper.
|
||||||
|
|
||||||
|
The first instance is for a key to use on the connected network. The second
|
||||||
|
instance is for a Ristretto public key used to publish data to the Serai
|
||||||
|
blockchain. This pair of FROST DKG instances is considered a single instance of
|
||||||
|
Serai's overall key generation protocol.
|
||||||
|
|
||||||
|
The commitments for both protocols are sent to the coordinator in a single
|
||||||
|
`key_gen::ProcessorMessage::Commitments`.
|
||||||
|
|
||||||
|
## Key Gen Commitments
|
||||||
|
|
||||||
|
On `key_gen::CoordinatorMessage::Commitments`, the processor continues the
|
||||||
|
specified key generation instance. The secret shares for each fellow
|
||||||
|
participant are sent to the coordinator in a
|
||||||
|
`key_gen::ProcessorMessage::Shares`.
|
||||||
|
|
||||||
|
### Key Gen Shares
|
||||||
|
|
||||||
|
On `key_gen::CoordinatorMessage::Shares`, the processor completes the specified
|
||||||
|
key generation instance. The generated key pair is sent to the coordinator in a
|
||||||
|
`key_gen::ProcessorMessage::GeneratedKeyPair`.
|
||||||
|
|
||||||
|
## Confirm Key Pair
|
||||||
|
|
||||||
|
On `substrate::CoordinatorMessage::ConfirmKeyPair`, the processor starts using
|
||||||
|
the newly confirmed key, scanning blocks on the connected network for
|
||||||
|
transfers to it.
|
||||||
|
|
||||||
|
## Connected Network Block
|
||||||
|
|
||||||
|
When the connected network has a new block, which is considered finalized
|
||||||
|
(either due to being literally finalized or due to having a sufficient amount
|
||||||
|
of confirmations), it's scanned.
|
||||||
|
|
||||||
|
Outputs to the key of Serai's multisig are saved to the database. Outputs which
|
||||||
|
newly transfer into Serai are used to build a `Batch` for the block. The
|
||||||
|
processor then begins a threshold signature protocol with its key pair's
|
||||||
|
Ristretto key to sign the batch. The protocol's preprocess is sent to the
|
||||||
|
coordinator in a `coordinator::ProcessorMessage::BatchPreprocess`.
|
||||||
|
|
||||||
|
As a design comment, we *may* be able to sign now possible, already scheduled,
|
||||||
|
branch/leaf transactions at this point. Doing so would be giving a mutable
|
||||||
|
borrow over the scheduler to both the external network and the Serai network,
|
||||||
|
and would accordingly be unsafe. We may want to look at splitting the scheduler
|
||||||
|
in two, in order to reduce latency (TODO).
|
||||||
|
|
||||||
|
## Batch Preprocesses
|
||||||
|
|
||||||
|
On `coordinator::CoordinatorMessage::BatchPreprocesses`, the processor
|
||||||
|
continues the specified batch signing protocol, sending
|
||||||
|
`coordinator::ProcessorMessage::BatchShare` to the coordinator.
|
||||||
|
|
||||||
|
## Batch Shares
|
||||||
|
|
||||||
|
On `coordinator::CoordinatorMessage::BatchShares`, the processor
|
||||||
|
completes the specified batch signing protocol. If successful, the processor
|
||||||
|
stops signing for this batch and sends `substrate::ProcessorMessage::Update` to
|
||||||
|
the coordinator.
|
||||||
|
|
||||||
|
## Batch Re-attempt
|
||||||
|
|
||||||
|
On `coordinator::CoordinatorMessage::BatchReattempt`, the processor will create
|
||||||
|
a new instance of the batch signing protocol. The new protocol's preprocess is
|
||||||
|
sent to the coordinator in a `coordinator::ProcessorMessage::BatchPreprocess`.
|
||||||
|
|
||||||
|
## Substrate Block
|
||||||
|
|
||||||
|
On `substrate::CoordinatorMessage::SubstrateBlock`, the processor:
|
||||||
|
|
||||||
|
1) Marks all blocks, up to the external block now considered finalized by
|
||||||
|
Serai, as having had their batches signed.
|
||||||
|
2) Adds the new outputs from newly finalized blocks to the scheduler, along
|
||||||
|
with the necessary payments from `Burn` events on Serai.
|
||||||
|
|
||||||
|
The processor also sends a `substrate::ProcessorMessage::SubstrateBlockAck`,
|
||||||
|
containing the IDs of all plans now being signed for, to the coordinator.
|
||||||
|
|
||||||
|
## Sign Preprocesses
|
||||||
|
|
||||||
|
On `sign::CoordinatorMessage::Preprocesses`, the processor continues the
|
||||||
|
specified transaction signing protocol, sending `sign::ProcessorMessage::Share`
|
||||||
|
to the coordinator.
|
||||||
|
|
||||||
|
## Sign Shares
|
||||||
|
|
||||||
|
On `sign::CoordinatorMessage::Shares`, the processor completes the specified
|
||||||
|
transaction signing protocol. If successful, the processor stops signing for
|
||||||
|
this transaction and publishes the signed transaction. Then,
|
||||||
|
`sign::ProcessorMessage::Completed` is sent to the coordinator, to be
|
||||||
|
broadcasted to all validators so everyone can observe the transaction was
|
||||||
|
signed and stop locally attempting to do so.
|
||||||
|
|
||||||
|
## Sign Re-attempt
|
||||||
|
|
||||||
|
On `sign::CoordinatorMessage::Reattempt`, the processor will create a new
|
||||||
|
a new instance of the transaction signing protocol. The new protocol's
|
||||||
|
preprocess is sent to the coordinator in a
|
||||||
|
`sign::ProcessorMessage::Preprocess`.
|
||||||
|
|
||||||
|
## Sign Completed
|
||||||
|
|
||||||
|
On `sign::CoordinatorMessage::Completed`, the processor verifies the included
|
||||||
|
transaction hash actually refers to an accepted transaction which completes the
|
||||||
|
plan it was supposed to. If so, the processor stops locally signing for the
|
||||||
|
transaction, and emits `sign::ProcessorMessage::Completed`.
|
||||||
Reference in New Issue
Block a user