mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 04:01:02 +00:00
High-Level Documentation (#565)
* High level docs - start. * Clean up README * Start adding details to high level docs * More docs on the header sync pallet * Testing scenarios document. * Add some scenarios. * Add multi-sig scenario. * Start writing about message dispatch pallet * Move content from old README into PoA specific doc * Apply suggestions from code review Co-authored-by: Andreas Doerr <adoerr@users.noreply.github.com> * GRANDPA for consistency. * Describe scenario steps. * WiP * Add notes about block production and forks * Update. * Add sequence diagram for Millau to Rialto transfer * Clean up header sync pallet overview * Remove leftover example code * Clean up testing scenarios and amend sequence diagram. * Linking docs. * Add some more docs. * Do a bit of cleanup on the high-level docs * Clean up the testing scenario * Fix typos in flow charts * Fix small typo * Fix indentation of Rust block * Another attempt at rendering block correctly * TIL about lazy list numbering in Markdown * Add list numbers across sections * Start counting from correct number * Update README to use correct path to local scripts * Wrap ASCII art in code block Co-authored-by: Tomasz Drwięga <tomasz@parity.io> Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Andreas Doerr <adoerr@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
5a790c9874
commit
d47658c92e
+80
-162
@@ -2,20 +2,18 @@
|
|||||||
|
|
||||||
This is a collection of components for building bridges.
|
This is a collection of components for building bridges.
|
||||||
|
|
||||||
These components include runtime modules to help you construct your bridge's runtime, as well as
|
These components include Substrate pallets for syncing headers, passing arbitrary messages, as well
|
||||||
bridge relays for cross-chain communication.
|
as libraries for building relayers to provide cross-chain communication capabilities.
|
||||||
|
|
||||||
A bridge node is also available. The node can be used to run a test network which has support for bridging Ethereum
|
Three bridge nodes are also available. The nodes can be used to run test networks which bridge other
|
||||||
PoA chains to Substrate. We're working on expanding this functionality in the future.
|
Substrate chains or Ethereum Proof-of-Authority chains.
|
||||||
|
|
||||||
🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧
|
🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
|
- [High-Level Architecture](#high-level-architecture)
|
||||||
- [Project Layout](#project-layout)
|
- [Project Layout](#project-layout)
|
||||||
- [Rialto Runtime](#rialto-runtime)
|
|
||||||
- [Ethereum Node](#ethereum-node)
|
|
||||||
- [Bridge Relay](#bridge-relay)
|
|
||||||
- [Running the Bridge](#running-the-bridge)
|
- [Running the Bridge](#running-the-bridge)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -40,199 +38,119 @@ If you need more information about setting up your development environment Subst
|
|||||||
[Getting Started](https://substrate.dev/docs/en/knowledgebase/getting-started/) page is a good
|
[Getting Started](https://substrate.dev/docs/en/knowledgebase/getting-started/) page is a good
|
||||||
resource.
|
resource.
|
||||||
|
|
||||||
|
## High-Level Architecture
|
||||||
|
|
||||||
|
This repo has support for bridging foreign chains together using a combination of Substrate pallets
|
||||||
|
and external processes called relayers. A bridge chain is one that is able to follow the consensus
|
||||||
|
of a foreign chain independently. For example, consider the case below where we want to bridge two
|
||||||
|
Substrate based chains.
|
||||||
|
|
||||||
|
```
|
||||||
|
+---------------+ +---------------+
|
||||||
|
| | | |
|
||||||
|
| Rialto | | Millau |
|
||||||
|
| | | |
|
||||||
|
+-------+-------+ +-------+-------+
|
||||||
|
^ ^
|
||||||
|
| +---------------+ |
|
||||||
|
| | | |
|
||||||
|
+-----> | Bridge Relay | <-------+
|
||||||
|
| |
|
||||||
|
+---------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
The Millau chain must be able to accept Rialto headers and verify their integrity. It does this by
|
||||||
|
using a runtime module designed to track GRANDPA finality. Since two blockchains can't interact
|
||||||
|
directly they need an external service, called a relayer, to communicate. The relayer will subscribe
|
||||||
|
to new Rialto headers via RPC and submit them to the Millau chain for verification.
|
||||||
|
|
||||||
|
Take a look at [Bridge High Level Documentation](./docs/high-level-overview.md) for more in-depth
|
||||||
|
description of the bridge interaction.
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
Here's an overview of how the project is laid out. The main bits are the `node`, which is the actual
|
Here's an overview of how the project is laid out. The main bits are the `node`, which is the actual
|
||||||
"blockchain", the `modules` which are used to build the blockchain's logic (a.k.a the runtime) and
|
"blockchain", the `modules` which are used to build the blockchain's logic (a.k.a the runtime) and
|
||||||
the `relays` which are used to pass messages between chains.
|
the `relays` which are used to pass messages between chains.
|
||||||
|
|
||||||
```
|
```
|
||||||
├── bin
|
├── bin // Node and Runtime for the various Substrate chains
|
||||||
│ └── node // Bridge ready chain implementation
|
│ └── ...
|
||||||
├── modules // Runtime Modules
|
├── deployments // Useful tools for deploying test networks
|
||||||
│ ├── ethereum // Manage Ethereum PoA chain info
|
│ └── ...
|
||||||
│ ├── ethereum-contract // Ethereum built-in for validating Substrate block info
|
├── diagrams // Pretty pictures of the project architecture
|
||||||
│ ├── currency-exchange // Cross-chain fund transfers
|
│ └── ...
|
||||||
│ └── substrate // Manage Substrate chain info
|
├── modules // Substrate Runtime Modules (a.k.a Pallets)
|
||||||
├── primitives // Shared runtime and node code
|
│ ├── ethereum // Ethereum PoA Header Sync Module
|
||||||
│ └── ethereum-poa // Helpers for Ethereum PoA
|
│ ├── substrate // Substrate Based Chain Header Sync Module
|
||||||
├── relays // Cross-chain communication
|
│ ├── message-lane // Cross Chain Message Passing
|
||||||
│ ├── ethereum // Sync and communicate between Ethereum PoA + Substrate chains
|
│ └── ...
|
||||||
│ └── substrate // 🚧 WIP 🚧
|
├── primitives // Code shared between modules, runtimes, and relays
|
||||||
```
|
│ └── ...
|
||||||
|
├── relays // Application for sending headers and messages between chains
|
||||||
## Rialto Runtime
|
│ └── ...
|
||||||
The node runtime consists of several runtime modules, however not all of them are used at the same
|
└── scripts // Useful development and maintenence scripts
|
||||||
time. When running an Ethereum PoA to Substrate bridge the modules required are the Ethereum module
|
|
||||||
and the currency exchange module. When running a Substrate to Substrate bridge the Substrate and
|
|
||||||
currency exchange modules are required.
|
|
||||||
|
|
||||||
Below is a brief description of each of the runtime modules.
|
|
||||||
|
|
||||||
### Ethereum Bridge Runtime Module
|
|
||||||
The main job of this runtime module is to keep track of useful information an Ethereum PoA chain
|
|
||||||
which has been submitted by a bridge relayer. This includes:
|
|
||||||
|
|
||||||
- Ethereum headers and their status (e.g are they the best header, are they finalized, etc.)
|
|
||||||
- Current validator set, and upcoming validator sets
|
|
||||||
|
|
||||||
This runtime module has more responsibilties than simply storing headers and validator sets. It is
|
|
||||||
able to perform checks on the incoming headers to verify their general integrity, as well as whether
|
|
||||||
or not they've been finalized by the authorities on the PoA chain.
|
|
||||||
|
|
||||||
This module is laid out as so:
|
|
||||||
|
|
||||||
```
|
|
||||||
├── ethereum
|
|
||||||
│ └── src
|
|
||||||
│ ├── error.rs // Runtime error handling
|
|
||||||
│ ├── finality.rs // Manage finality operations
|
|
||||||
│ ├── import.rs // Import new Ethereum headers
|
|
||||||
│ ├── lib.rs // Store headers and validator set info
|
|
||||||
│ ├── validators.rs // Track current and future PoA validator sets
|
|
||||||
│ └── verification.rs // Verify validity of incoming Ethereum headers
|
|
||||||
```
|
|
||||||
|
|
||||||
### Currency Exchange Runtime Module
|
|
||||||
The currency exchange module is used to faciliate cross-chain funds transfers. It works by accepting
|
|
||||||
a transaction which proves that funds were locked on one chain, and releases a corresponding amount
|
|
||||||
of funds on the recieving chain.
|
|
||||||
|
|
||||||
For example: Alice would like to send funds from chain A to chain B. What she would do is send a
|
|
||||||
transaction to chain A indicating that she would like to send funds to an address on chain B. This
|
|
||||||
transaction would contain the amount of funds she would like to send, as well as the address of the
|
|
||||||
recipient on chain B. These funds would now be locked on chain A. Once the block containing this
|
|
||||||
"locked-funds" transaction is finalized it can be relayed to chain B. Chain B will verify that this
|
|
||||||
transaction was included in a finalized block on chain A, and if successful deposit funds into the
|
|
||||||
recipient account on chain B.
|
|
||||||
|
|
||||||
Chain B would need a way to convert from a foreign currency to its local currency. How this is done
|
|
||||||
is left to the runtime developer for chain B.
|
|
||||||
|
|
||||||
This module is one example of how an on-chain light client can be used to prove a particular action
|
|
||||||
was taken on a foreign chain. In particular it enables transfers of the foreign chain's native
|
|
||||||
currency, but more sophisticated modules such as ERC20 token transfers or arbitrary message transfers
|
|
||||||
are being worked on as well.
|
|
||||||
|
|
||||||
### Substrate Bridge Runtime Module
|
|
||||||
👷 Under Construction 👷♀️
|
|
||||||
|
|
||||||
## Ethereum Node
|
|
||||||
On the Ethereum side of things, we require two things. First, a Solidity smart contract to track the
|
|
||||||
Substrate headers which have been submitted to the bridge (by the relay), and a built-in contract to
|
|
||||||
be able to verify that headers have been finalized by the Grandpa finality gadget. Together this
|
|
||||||
allows the Ethereum PoA chain to verify the integrity and finality of incoming Substrate headers.
|
|
||||||
|
|
||||||
The Solidity smart contract is not part of this repo, but can be found
|
|
||||||
[here](https://github.com/svyatonik/substrate-bridge-sol/blob/master/substrate-bridge.sol) if you're
|
|
||||||
curious. We have the contract ABI in the `ethereum/relays/res` directory.
|
|
||||||
|
|
||||||
## Bridge Relay
|
|
||||||
The bridge relay is responsible for syncing the chains which are being bridged, and passing messages
|
|
||||||
between them. The current implementation of the relay supportings syncing and interacting with
|
|
||||||
Ethereum PoA and Substrate chains.
|
|
||||||
|
|
||||||
The folder structure of the bridge relay is as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
├── relays
|
|
||||||
│ ├── ethereum
|
|
||||||
│ │ ├── res
|
|
||||||
│ │ │ └── ...
|
|
||||||
│ │ └── src
|
|
||||||
│ │ ├── ethereum_client.rs // Interface for Ethereum RPC
|
|
||||||
│ │ ├── ethereum_deploy_contract.rs // Utility for deploying bridge contract to Ethereum
|
|
||||||
│ │ ├── ethereum_exchange.rs // Relay proof of PoA -> Substrate exchange transactions
|
|
||||||
│ │ ├── ethereum_sync_loop.rs // Sync headers from Ethereum, submit to Substrate
|
|
||||||
│ │ ├── ethereum_types.rs // Useful Ethereum types
|
|
||||||
│ │ ├── exchange.rs // Relay proof of exchange transactions
|
|
||||||
│ │ ├── headers.rs // Track synced and incoming block headers
|
|
||||||
│ │ ├── main.rs // Entry point to binary
|
|
||||||
│ │ ├── substrate_client.rs // Interface for Substrate RPC
|
|
||||||
│ │ ├── substrate_sync_loop.rs // Sync headers from Substrate, submit to Ethereum
|
|
||||||
│ │ ├── substrate_types.rs // Useful Ethereum types
|
|
||||||
│ │ ├── sync.rs // Sync configuration and helpers
|
|
||||||
│ │ ├── sync_loop.rs // Header synchronization between source and target chains
|
|
||||||
│ │ ├── sync_types.rs // Useful sync types
|
|
||||||
│ │ └── utils.rs // General utilities
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running the Bridge
|
## Running the Bridge
|
||||||
|
|
||||||
To run the Bridge you need to be able to connect the bridge relay node to the RPC interface of nodes
|
To run the Bridge you need to be able to connect the bridge relay node to the RPC interface of nodes
|
||||||
on each side of the bridge (home & foreign chain). An easy way to build all the required nodes is
|
on each side of the bridge (source and target chain).
|
||||||
through Docker.
|
|
||||||
|
|
||||||
### Local Development Build
|
There are 3 ways to run the bridge, described below:
|
||||||
|
- building & running from source,
|
||||||
|
- building or using Docker images for each individual component,
|
||||||
|
- running a Docker Compose setup (recommended).
|
||||||
|
|
||||||
#### Building
|
### Building
|
||||||
|
|
||||||
First you'll need to build the bridge node and relay. This can be done as follows:
|
First you'll need to build the bridge nodes and relay. This can be done as follows:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# In `parity-bridges-common` folder
|
# In `parity-bridges-common` folder
|
||||||
cargo build -p rialto-bridge-node
|
cargo build -p rialto-bridge-node
|
||||||
cargo build -p ethereum-poa-relay
|
cargo build -p millau-bridge-node
|
||||||
|
cargo build -p substrate-relay
|
||||||
```
|
```
|
||||||
|
|
||||||
Next you'll need to clone the following [fork of OpenEthereum](https://github.com/paritytech/openethereum).
|
### Running
|
||||||
If you're doing development which only involves the Ethereum to Substrate side of the bridge you may
|
|
||||||
use the `master` branch. Otherwise you'll need to checkout the `substrate-builtins-stubs` branch.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Should be at the same level as `parity-bridges-common` folder
|
|
||||||
git clone https://github.com/paritytech/openethereum.git openethereum
|
|
||||||
git fetch
|
|
||||||
git checkout substrate-builtins-stubs
|
|
||||||
```
|
|
||||||
|
|
||||||
If you've checked out the `substrate-builtins-stubs` branch make sure you've cloned the OpenEthereum
|
|
||||||
repo at the same level as `parity-bridges-common` since it references the repo.
|
|
||||||
|
|
||||||
Next you'll need to build the Ethereum node:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In `openethereum` folder
|
|
||||||
cargo build
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Running
|
|
||||||
|
|
||||||
To run a simple dev network you'll can use the scripts located in
|
To run a simple dev network you'll can use the scripts located in
|
||||||
[the `scripts` folder](./scripts). Since the relay connects to both the Substrate and Ethereum
|
[the `scripts` folder](./scripts). Since the relay connects to both Substrate chains it must be run
|
||||||
chains it must be run last.
|
last.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# In `parity-bridges-common` folder
|
# In `parity-bridges-common` folder
|
||||||
./scripts/run-openethereum-node.sh
|
./deployments/local-scripts/run-rialto-bridge-node.sh
|
||||||
./scripts/run-rialto-bridge-node.sh
|
./deployments/local-scripts/run-millau-bridge-node.sh
|
||||||
./scripts/run-eth2sub-relay.sh
|
./deployments/local-scripts/run-millau-to-rialto-relay.sh
|
||||||
|
./deployments/local-scripts/run-rialto-to-millau-relay.sh
|
||||||
```
|
```
|
||||||
At this point you should see the relayer submitting blocks from the Ethereum chain
|
|
||||||
to the Substrate chain.
|
At this point you should see the relayer submitting blocks from the Millau Substrate chain to the
|
||||||
|
Rialto Substrate chain and vice-versa.
|
||||||
|
|
||||||
### Local Docker Build
|
### Local Docker Build
|
||||||
If you want to make a Docker container using your local source files you can run the following
|
If you want to make a Docker container using your local source files you can run the following
|
||||||
command at the top level of the repository:
|
command at the top level of the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build . -t bridge-relay-dev
|
docker build . -t local/rialto-bridge-node --build-arg PROJECT=rialto-bridge-node
|
||||||
|
docker build . -t local/millau-bridge-node --build-arg PROJECT=millau-bridge-node
|
||||||
|
docker build . -t local/substrate-relay --build-arg PROJECT=substrate-relay
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also build and run the Substrate based node as follows:
|
You can then run the network as follows:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build . -t bridge-node-dev --build-arg PROJECT=rialto-bridge-node
|
docker run -it local/rialto-bridge-node --dev --tmp
|
||||||
```
|
docker run -it local/millau-bridge-node --dev --tmp
|
||||||
|
docker run -it local/substrate-relay
|
||||||
To run the Substrate node you can do the following:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -it bridge-node-dev --dev --tmp
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice that the `docker run` command will accept all the normal Substrate flags. For local
|
Notice that the `docker run` command will accept all the normal Substrate flags. For local
|
||||||
development you should at minimum run with the `--dev` flag or else no blocks will be produced.
|
development you should at minimum run with the `--dev` flag or else no blocks will be produced.
|
||||||
|
|
||||||
### Full Network Docker Setup
|
### Full Network Docker Compose Setup
|
||||||
See [Deployments README](./deployments/README.md) to learn more about how to run
|
|
||||||
a more sophisticated test network using `docker-compose` setup.
|
For a more sophisticated deployment which includes bidirectional header sync, message passing,
|
||||||
|
monitoring dashboards, etc. see the [Deployments README](./deployments/README.md).
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
# High-Level Bridge Documentation
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Trustless connecting between two Substrate-based chains using GRANDPA finality.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Even though we support two-way bridging, the documentation will generally talk about a one-sided
|
||||||
|
interaction. That's to say, we will only talk about syncing headers and messages from a _source_
|
||||||
|
chain to a _target_ chain. This is because the two-sided interaction is really just the one-sided
|
||||||
|
interaction with the source and target chains switched.
|
||||||
|
|
||||||
|
To understand the full interaction with the bridge, take a look at the
|
||||||
|
[testing scenarios](./testing-scenarios.md) document. It describes potential use cases and describes
|
||||||
|
how each of the layers outlined below is involved.
|
||||||
|
|
||||||
|
The bridge is built from various components. Here is a quick overview of the important ones.
|
||||||
|
|
||||||
|
### Header Sync
|
||||||
|
|
||||||
|
A light client of the source chain built into the target chain's runtime. It is a single FRAME
|
||||||
|
pallet. It provides a "source of truth" about the source chain headers which have been finalized.
|
||||||
|
This is useful for higher level applications.
|
||||||
|
|
||||||
|
### Headers Relayer
|
||||||
|
|
||||||
|
A standalone application connected to both chains. It submits every source chain header it sees to
|
||||||
|
the target chain through RPC.
|
||||||
|
|
||||||
|
### Message Delivery
|
||||||
|
|
||||||
|
A FRAME pallet built on top of the header sync pallet. It allows users to submit messages to the
|
||||||
|
source chain, which are to be delivered to the target chain. The delivery protocol doesn't care
|
||||||
|
about the payload more than it has to. Handles replay protection and message ordering.
|
||||||
|
|
||||||
|
### Message Dispatch
|
||||||
|
|
||||||
|
A FRAME pallet responsible for interpreting the payload of delivered messages.
|
||||||
|
|
||||||
|
### Message Relayer
|
||||||
|
|
||||||
|
A standalone application handling delivery of the messages from source chain to the target chain.
|
||||||
|
|
||||||
|
## Processes
|
||||||
|
|
||||||
|
High level sequence charts of the process can be found in [a separate document](./high-level.html).
|
||||||
|
|
||||||
|
### Substrate (GRANDPA) Header Sync
|
||||||
|
|
||||||
|
The header sync pallet (`pallet-substrate-bridge`) is an on-chain light client for chains which use
|
||||||
|
GRANDPA finality. It is part of the target chain's runtime, and accepts headers from the source
|
||||||
|
chain. Its main goals are to accept valid headers, track GRANDPA finality set changes, and verify
|
||||||
|
GRANDPA finality proofs (a.k.a justifications).
|
||||||
|
|
||||||
|
The pallet does not care about what block production mechanism is used for the source chain
|
||||||
|
(e.g Aura or BABE) as long as it uses the GRANDPA finality gadget. Due to this it is possible for
|
||||||
|
the pallet to import (but not necessarily finalize) headers which are _not_ valid according to the
|
||||||
|
source chain's block production mechanism.
|
||||||
|
|
||||||
|
The pallet has support for tracking forks and uses the longest chain rule to determine what the
|
||||||
|
canonical chain is. The pallet allows headers to be imported on a different fork from the canonical
|
||||||
|
one as long as the headers being imported don't conflict with already finalized headers (for
|
||||||
|
example, it will not allow importing a header at a lower height than the best finalized header).
|
||||||
|
|
||||||
|
When tracking authority set changes, the pallet - unlike the full GRANDPA protocol - does not
|
||||||
|
support tracking multiple authority set changes across forks. Each fork can have at most one pending
|
||||||
|
authority set change. This is done to prevent DoS attacks if GRANDPA on the source chain were to
|
||||||
|
stall for a long time (the pallet would have to do a lot of expensive ancestry checks to catch up).
|
||||||
|
|
||||||
|
Referer to the [pallet documentation](../modules/substrate/src/lib.rs) for more details.
|
||||||
|
|
||||||
|
#### Header Relayer strategy
|
||||||
|
|
||||||
|
There is currently no reward strategy for the relayers at all. They also are not required to be
|
||||||
|
staked or registered on-chain, unlike in other bridge designs. We consider the header sync to be
|
||||||
|
an essential part of the bridge and the incentivisation should be happening on the higher layers.
|
||||||
|
|
||||||
|
At the moment, signed transactions are the only way to submit headers to the header sync pallet.
|
||||||
|
However, in the future we would like to use unsigned transactions for headers delivery. This will
|
||||||
|
allow transaction de-duplication to be done at the transaction pool level and also remove the cost
|
||||||
|
for message relayers to run header relayers.
|
||||||
|
|
||||||
|
### Message Passing
|
||||||
|
|
||||||
|
Once header sync is maintained, the target side of the bridge can receive and verify proofs about
|
||||||
|
events happening on the source chain, or its internal state. On top of this, we built a message
|
||||||
|
passing protocol which consists of two parts described in following sections: message delivery and
|
||||||
|
message dispatch.
|
||||||
|
|
||||||
|
#### Message Lanes Delivery
|
||||||
|
|
||||||
|
The [Message delivery pallet](../modules/message-lane/src/lib.rs) is responsible for queueing up
|
||||||
|
messages and delivering them in order on the target chain. It also dispatches messages, but we will
|
||||||
|
cover that in the next section.
|
||||||
|
|
||||||
|
The pallet supports multiple lanes (channels) where messages can be added. Every lane can be
|
||||||
|
considered completely independent from others, which allows them to make progress in parallel.
|
||||||
|
Different lanes can be configured to validated messages differently (e.g higher rewards, specific
|
||||||
|
types of payload, etc.) and may be associated with a particular "user application" built on top of
|
||||||
|
the bridge. Note that messages in the same lane MUST be delivered _in the same order_ they were
|
||||||
|
queued up.
|
||||||
|
|
||||||
|
The message delivery protocol does not care about the payload it transports and can be coupled
|
||||||
|
with an arbitrary message dispatch mechanism that will interpret and execute the payload if delivery
|
||||||
|
conditions are met. Each delivery on the target chain is confirmed back to the source chain by the
|
||||||
|
relayer. This is so that she can collect the reward for delivering these messages.
|
||||||
|
|
||||||
|
Users of the pallet add their messages to an "outbound lane" on the source chain. When a block is
|
||||||
|
finalized message relayers are responsible for reading the current queue of messages and submitting
|
||||||
|
some (or all) of them to the "inbound lane" of the target chain. Each message has a `nonce`
|
||||||
|
associated with it, which serves as the ordering of messages. The inbound lane stores the last
|
||||||
|
delivered nonce to prevent replaying messages. To succesfuly deliver the message to the inbound lane
|
||||||
|
on target chain the relayer has to present present a storage proof which shows that the message was
|
||||||
|
part of the outbound lane on the source chain.
|
||||||
|
|
||||||
|
During delivery of messages they are immediately dispatched on the target chain and the relayer is
|
||||||
|
required to declare the correct `weight` to cater for all messages dispatch and pay all required
|
||||||
|
fees of the target chain. To make sure the relayer is incentivised to do so, on the source chain:
|
||||||
|
- the user provides a declared dispatch weight of the payload
|
||||||
|
- the pallet calculates the expected fee on the target chain based on the declared weight
|
||||||
|
- the pallet converts the target fee into source tokens (based on a price oracle) and reserves
|
||||||
|
enough tokens to cover for the delivery, dispatch, confirmation and additional relayers reward.
|
||||||
|
|
||||||
|
If the declared weight turns out to be too low on the target chain the message is delivered but
|
||||||
|
it immediately fails to dispatch. The fee and reward is collected by the relayer upon confirmation
|
||||||
|
of delivery.
|
||||||
|
|
||||||
|
Due to the fact that message lanes require delivery confirmation transactions, they also strictly
|
||||||
|
require bi-directional header sync (i.e. you can't use message delivery with one-way header sync).
|
||||||
|
|
||||||
|
#### Dispatching Messages
|
||||||
|
|
||||||
|
The [Message dispatch pallet](../modules/call-dispatch/src/lib.rs) is used to perform the actions
|
||||||
|
specified by messages which have come over the bridge. For Substrate-based chains this means
|
||||||
|
interpreting the source chain's message as a `Call` on the target chain.
|
||||||
|
|
||||||
|
An example `Call` of the target chain would look something like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
target_runtime::Call::Balances(target_runtime::pallet_balances::Call::transfer(recipient, amount))
|
||||||
|
```
|
||||||
|
|
||||||
|
When sending a `Call` it must first be SCALE encoded and then sent to the source chain. The `Call`
|
||||||
|
is then delivered by the message lane delivery mechanism from the source chain to the target chain.
|
||||||
|
When a message is received the inbound message lane on the target chain will try and decode the
|
||||||
|
message payload into a `Call` enum. If it's successful it will be dispatched after we check that the
|
||||||
|
weight of the call does not exceed the weight declared by the sender. The relayer pays fees for
|
||||||
|
executing the transaction on the target chain, but her costs should be covered by the sender on the
|
||||||
|
source chain.
|
||||||
|
|
||||||
|
When dispatching messages there are three Origins which can be used by the target chain:
|
||||||
|
1. Root Origin
|
||||||
|
2. Source Origin
|
||||||
|
3. Target Origin
|
||||||
|
|
||||||
|
Senders of a message can indicate which one of the three origins they would like to dispatch their
|
||||||
|
message with. However, there are restrictions on who/what is allowed to dispatch messages with a
|
||||||
|
particular origin.
|
||||||
|
|
||||||
|
The Root origin represents the source chain's Root account on the target chain. This origin can can
|
||||||
|
only be dispatched on the target chain if the "send message" request was made by the Root origin of
|
||||||
|
the source chain - otherwise the message will fail to be dispatched.
|
||||||
|
|
||||||
|
The Source origin represents an account without a private key on the target chain. This account will
|
||||||
|
be generated/derived using the account ID of the sender on the source chain. We don't necessarily
|
||||||
|
require the source account id to be associated with a private key on the source chain either. This
|
||||||
|
is useful for representing things such as source chain proxies or pallets.
|
||||||
|
|
||||||
|
The Target origin represents an account with a private key on the target chain. The sender on the
|
||||||
|
source chain needs to prove ownership of this account by using their target chain private key to
|
||||||
|
sign: `(Call, SourceChainAccountId).encode()`. This will be included in the message payload and
|
||||||
|
verified by the target chain before dispatch.
|
||||||
|
|
||||||
|
See [`CallOrigin` documentation](../modules/call-dispatch/src/lib.rs) for more details.
|
||||||
|
|
||||||
|
#### Message Relayers Strategy
|
||||||
@@ -134,7 +134,7 @@ fn fork_does_not_allow_competing_finality_proofs() {
|
|||||||
//
|
//
|
||||||
// Not allowed to import 3 until we get F2
|
// Not allowed to import 3 until we get F2
|
||||||
//
|
//
|
||||||
// Note: Grandpa would technically allow 3 to be imported as long as it didn't try and enact an
|
// Note: GRANDPA would technically allow 3 to be imported as long as it didn't try and enact an
|
||||||
// authority set change. However, since we expect finality proofs to be imported quickly we've
|
// authority set change. However, since we expect finality proofs to be imported quickly we've
|
||||||
// decided to simplify our import process and disallow header imports until we get a finality proof.
|
// decided to simplify our import process and disallow header imports until we get a finality proof.
|
||||||
#[test]
|
#[test]
|
||||||
@@ -161,9 +161,9 @@ fn fork_waits_for_finality_proof_before_importing_header_past_one_which_enacts_a
|
|||||||
//
|
//
|
||||||
// [1] <- [2: S|1] <- [3: S|0]
|
// [1] <- [2: S|1] <- [3: S|0]
|
||||||
//
|
//
|
||||||
// Grandpa can have multiple authority set changes pending on the same fork. However, we've decided
|
// GRANDPA can have multiple authority set changes pending on the same fork. However, we've decided
|
||||||
// to introduce a limit of _one_ pending authority set change per fork in order to simplify pallet
|
// to introduce a limit of _one_ pending authority set change per fork in order to simplify pallet
|
||||||
// logic and to prevent DoS attacks if Grandpa finality were to temporarily stall for a long time
|
// logic and to prevent DoS attacks if GRANDPA finality were to temporarily stall for a long time
|
||||||
// (we'd have to perform a lot of expensive ancestry checks to catch back up).
|
// (we'd have to perform a lot of expensive ancestry checks to catch back up).
|
||||||
#[test]
|
#[test]
|
||||||
fn fork_does_not_allow_multiple_scheduled_changes_on_the_same_fork() {
|
fn fork_does_not_allow_multiple_scheduled_changes_on_the_same_fork() {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Module for checking Grandpa Finality Proofs.
|
//! Module for checking GRANDPA Finality Proofs.
|
||||||
//!
|
//!
|
||||||
//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin
|
//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin
|
||||||
//! will ever be moved to the sp_finality_grandpa, we should reuse that implementation.
|
//! will ever be moved to the sp_finality_grandpa, we should reuse that implementation.
|
||||||
@@ -123,7 +123,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Grandpa Justification is a proof that a given header was finalized
|
/// A GRANDPA Justification is a proof that a given header was finalized
|
||||||
/// at a certain height and with a certain set of authorities.
|
/// at a certain height and with a certain set of authorities.
|
||||||
///
|
///
|
||||||
/// This particular proof is used to prove that headers on a bridged chain
|
/// This particular proof is used to prove that headers on a bridged chain
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
//! It has a simple interface for achieving this. First it can import headers to the runtime
|
//! It has a simple interface for achieving this. First it can import headers to the runtime
|
||||||
//! storage. During this it will check the validity of the headers and ensure they don't conflict
|
//! storage. During this it will check the validity of the headers and ensure they don't conflict
|
||||||
//! with any existing headers (e.g they're on a different finalized chain). Secondly it can finalize
|
//! with any existing headers (e.g they're on a different finalized chain). Secondly it can finalize
|
||||||
//! an already imported header (and its ancestors) given a valid Grandpa justification.
|
//! an already imported header (and its ancestors) given a valid GRANDPA justification.
|
||||||
//!
|
//!
|
||||||
//! With these two functions the pallet is able to form a "source of truth" for what headers have
|
//! With these two functions the pallet is able to form a "source of truth" for what headers have
|
||||||
//! been finalized on a given Substrate chain. This can be a useful source of info for other
|
//! been finalized on a given Substrate chain. This can be a useful source of info for other
|
||||||
@@ -94,17 +94,17 @@ decl_storage! {
|
|||||||
/// Hash of the best finalized header.
|
/// Hash of the best finalized header.
|
||||||
BestFinalized: BridgedBlockHash<T>;
|
BestFinalized: BridgedBlockHash<T>;
|
||||||
/// The set of header IDs (number, hash) which enact an authority set change and therefore
|
/// The set of header IDs (number, hash) which enact an authority set change and therefore
|
||||||
/// require a Grandpa justification.
|
/// require a GRANDPA justification.
|
||||||
RequiresJustification: map hasher(identity) BridgedBlockHash<T> => BridgedBlockNumber<T>;
|
RequiresJustification: map hasher(identity) BridgedBlockHash<T> => BridgedBlockNumber<T>;
|
||||||
/// Headers which have been imported into the pallet.
|
/// Headers which have been imported into the pallet.
|
||||||
ImportedHeaders: map hasher(identity) BridgedBlockHash<T> => Option<ImportedHeader<BridgedHeader<T>>>;
|
ImportedHeaders: map hasher(identity) BridgedBlockHash<T> => Option<ImportedHeader<BridgedHeader<T>>>;
|
||||||
/// The current Grandpa Authority set.
|
/// The current GRANDPA Authority set.
|
||||||
CurrentAuthoritySet: AuthoritySet;
|
CurrentAuthoritySet: AuthoritySet;
|
||||||
/// The next scheduled authority set change for a given fork.
|
/// The next scheduled authority set change for a given fork.
|
||||||
///
|
///
|
||||||
/// The fork is indicated by the header which _signals_ the change (key in the mapping).
|
/// The fork is indicated by the header which _signals_ the change (key in the mapping).
|
||||||
/// Note that this is different than a header which _enacts_ a change.
|
/// Note that this is different than a header which _enacts_ a change.
|
||||||
// Grandpa doesn't require there to always be a pending change. In fact, most of the time
|
// GRANDPA doesn't require there to always be a pending change. In fact, most of the time
|
||||||
// there will be no pending change available.
|
// there will be no pending change available.
|
||||||
NextScheduledChange: map hasher(identity) BridgedBlockHash<T> => Option<ScheduledChange<BridgedBlockNumber<T>>>;
|
NextScheduledChange: map hasher(identity) BridgedBlockHash<T> => Option<ScheduledChange<BridgedBlockNumber<T>>>;
|
||||||
/// Optional pallet owner.
|
/// Optional pallet owner.
|
||||||
@@ -448,10 +448,10 @@ pub trait BridgeStorage {
|
|||||||
/// Returns None if it is not known to the pallet.
|
/// Returns None if it is not known to the pallet.
|
||||||
fn header_by_hash(&self, hash: <Self::Header as HeaderT>::Hash) -> Option<ImportedHeader<Self::Header>>;
|
fn header_by_hash(&self, hash: <Self::Header as HeaderT>::Hash) -> Option<ImportedHeader<Self::Header>>;
|
||||||
|
|
||||||
/// Get the current Grandpa authority set.
|
/// Get the current GRANDPA authority set.
|
||||||
fn current_authority_set(&self) -> AuthoritySet;
|
fn current_authority_set(&self) -> AuthoritySet;
|
||||||
|
|
||||||
/// Update the current Grandpa authority set.
|
/// Update the current GRANDPA authority set.
|
||||||
///
|
///
|
||||||
/// Should only be updated when a scheduled change has been triggered.
|
/// Should only be updated when a scheduled change has been triggered.
|
||||||
fn update_current_authority_set(&self, new_set: AuthoritySet);
|
fn update_current_authority_set(&self, new_set: AuthoritySet);
|
||||||
@@ -462,13 +462,13 @@ pub trait BridgeStorage {
|
|||||||
#[allow(clippy::result_unit_err)]
|
#[allow(clippy::result_unit_err)]
|
||||||
fn enact_authority_set(&mut self, signal_hash: <Self::Header as HeaderT>::Hash) -> Result<(), ()>;
|
fn enact_authority_set(&mut self, signal_hash: <Self::Header as HeaderT>::Hash) -> Result<(), ()>;
|
||||||
|
|
||||||
/// Get the next scheduled Grandpa authority set change.
|
/// Get the next scheduled GRANDPA authority set change.
|
||||||
fn scheduled_set_change(
|
fn scheduled_set_change(
|
||||||
&self,
|
&self,
|
||||||
signal_hash: <Self::Header as HeaderT>::Hash,
|
signal_hash: <Self::Header as HeaderT>::Hash,
|
||||||
) -> Option<ScheduledChange<<Self::Header as HeaderT>::Number>>;
|
) -> Option<ScheduledChange<<Self::Header as HeaderT>::Number>>;
|
||||||
|
|
||||||
/// Schedule a Grandpa authority set change in the future.
|
/// Schedule a GRANDPA authority set change in the future.
|
||||||
///
|
///
|
||||||
/// Takes the hash of the header which scheduled this particular change.
|
/// Takes the hash of the header which scheduled this particular change.
|
||||||
fn schedule_next_set_change(
|
fn schedule_next_set_change(
|
||||||
|
|||||||
@@ -42,24 +42,24 @@ pub struct InitializationData<H: HeaderT> {
|
|||||||
pub is_halted: bool,
|
pub is_halted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Grandpa Authority List and ID.
|
/// A GRANDPA Authority List and ID.
|
||||||
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
pub struct AuthoritySet {
|
pub struct AuthoritySet {
|
||||||
/// List of Grandpa authorities for the current round.
|
/// List of GRANDPA authorities for the current round.
|
||||||
pub authorities: AuthorityList,
|
pub authorities: AuthorityList,
|
||||||
/// Monotonic identifier of the current Grandpa authority set.
|
/// Monotonic identifier of the current GRANDPA authority set.
|
||||||
pub set_id: SetId,
|
pub set_id: SetId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthoritySet {
|
impl AuthoritySet {
|
||||||
/// Create a new Grandpa Authority Set.
|
/// Create a new GRANDPA Authority Set.
|
||||||
pub fn new(authorities: AuthorityList, set_id: SetId) -> Self {
|
pub fn new(authorities: AuthorityList, set_id: SetId) -> Self {
|
||||||
Self { authorities, set_id }
|
Self { authorities, set_id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keeps track of when the next Grandpa authority set change will occur.
|
/// Keeps track of when the next GRANDPA authority set change will occur.
|
||||||
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
pub struct ScheduledChange<N> {
|
pub struct ScheduledChange<N> {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
//!
|
//!
|
||||||
//! When importing headers it performs checks to ensure that no invariants are broken (like
|
//! When importing headers it performs checks to ensure that no invariants are broken (like
|
||||||
//! importing the same header twice). When it imports finality proofs it will ensure that the proof
|
//! importing the same header twice). When it imports finality proofs it will ensure that the proof
|
||||||
//! has been signed off by the correct Grandpa authorities, and also enact any authority set changes
|
//! has been signed off by the correct GRANDPA authorities, and also enact any authority set changes
|
||||||
//! if required.
|
//! if required.
|
||||||
|
|
||||||
use crate::justification::verify_justification;
|
use crate::justification::verify_justification;
|
||||||
@@ -34,8 +34,8 @@ use sp_std::{prelude::Vec, vec};
|
|||||||
|
|
||||||
/// The finality proof used by the pallet.
|
/// The finality proof used by the pallet.
|
||||||
///
|
///
|
||||||
/// For a Substrate based chain using Grandpa this will
|
/// For a Substrate based chain using GRANDPA this will
|
||||||
/// be an encoded Grandpa Justification.
|
/// be an encoded GRANDPA Justification.
|
||||||
#[derive(RuntimeDebug)]
|
#[derive(RuntimeDebug)]
|
||||||
pub struct FinalityProof(Vec<u8>);
|
pub struct FinalityProof(Vec<u8>);
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ where
|
|||||||
// we need to make a note of it.
|
// we need to make a note of it.
|
||||||
//
|
//
|
||||||
// Note: This assumes that we can only have one authority set change pending per fork at a
|
// Note: This assumes that we can only have one authority set change pending per fork at a
|
||||||
// time. While this is not strictly true of Grandpa (it can have multiple pending changes,
|
// time. While this is not strictly true of GRANDPA (it can have multiple pending changes,
|
||||||
// even across forks), this assumption simplifies our tracking of authority set changes.
|
// even across forks), this assumption simplifies our tracking of authority set changes.
|
||||||
let mut signal_hash = parent_header.signal_hash;
|
let mut signal_hash = parent_header.signal_hash;
|
||||||
let scheduled_change = find_scheduled_change(&header);
|
let scheduled_change = find_scheduled_change(&header);
|
||||||
@@ -213,7 +213,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that a previously imported header can be finalized with the given Grandpa finality
|
/// Verify that a previously imported header can be finalized with the given GRANDPA finality
|
||||||
/// proof. If the header enacts an authority set change the change will be applied once the
|
/// proof. If the header enacts an authority set change the change will be applied once the
|
||||||
/// header has been finalized.
|
/// header has been finalized.
|
||||||
pub fn import_finality_proof(&mut self, hash: H::Hash, proof: FinalityProof) -> Result<(), FinalizationError> {
|
pub fn import_finality_proof(&mut self, hash: H::Hash, proof: FinalityProof) -> Result<(), FinalizationError> {
|
||||||
@@ -680,7 +680,7 @@ mod tests {
|
|||||||
let mut storage = PalletStorage::<TestRuntime>::new();
|
let mut storage = PalletStorage::<TestRuntime>::new();
|
||||||
let _imported_headers = write_default_headers(&mut storage, vec![1]);
|
let _imported_headers = write_default_headers(&mut storage, vec![1]);
|
||||||
|
|
||||||
// Nothing special about this header, yet Grandpa may have created a justification
|
// Nothing special about this header, yet GRANDPA may have created a justification
|
||||||
// for it since it does that periodically
|
// for it since it does that periodically
|
||||||
let header = test_header(2);
|
let header = test_header(2);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub trait Chain: Send + Sync + 'static {
|
|||||||
// See here for more info:
|
// See here for more info:
|
||||||
// https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number
|
// https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number
|
||||||
//
|
//
|
||||||
// Note that the `AsPrimitive<usize>` trait is required by the Grandpa justification
|
// Note that the `AsPrimitive<usize>` trait is required by the GRANDPA justification
|
||||||
// verifier, and is not usually part of a Substrate Header's Number type.
|
// verifier, and is not usually part of a Substrate Header's Number type.
|
||||||
type BlockNumber: Parameter
|
type BlockNumber: Parameter
|
||||||
+ Member
|
+ Member
|
||||||
|
|||||||
Reference in New Issue
Block a user