mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 02:51:01 +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
+81
-163
@@ -2,20 +2,18 @@
|
||||
|
||||
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
|
||||
bridge relays for cross-chain communication.
|
||||
These components include Substrate pallets for syncing headers, passing arbitrary messages, as well
|
||||
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
|
||||
PoA chains to Substrate. We're working on expanding this functionality in the future.
|
||||
Three bridge nodes are also available. The nodes can be used to run test networks which bridge other
|
||||
Substrate chains or Ethereum Proof-of-Authority chains.
|
||||
|
||||
🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧
|
||||
|
||||
## Contents
|
||||
- [Installation](#installation)
|
||||
- [High-Level Architecture](#high-level-architecture)
|
||||
- [Project Layout](#project-layout)
|
||||
- [Rialto Runtime](#rialto-runtime)
|
||||
- [Ethereum Node](#ethereum-node)
|
||||
- [Bridge Relay](#bridge-relay)
|
||||
- [Running the Bridge](#running-the-bridge)
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
the `relays` which are used to pass messages between chains.
|
||||
|
||||
```
|
||||
├── bin
|
||||
│ └── node // Bridge ready chain implementation
|
||||
├── modules // Runtime Modules
|
||||
│ ├── ethereum // Manage Ethereum PoA chain info
|
||||
│ ├── ethereum-contract // Ethereum built-in for validating Substrate block info
|
||||
│ ├── currency-exchange // Cross-chain fund transfers
|
||||
│ └── substrate // Manage Substrate chain info
|
||||
├── primitives // Shared runtime and node code
|
||||
│ └── ethereum-poa // Helpers for Ethereum PoA
|
||||
├── relays // Cross-chain communication
|
||||
│ ├── ethereum // Sync and communicate between Ethereum PoA + Substrate chains
|
||||
│ └── substrate // 🚧 WIP 🚧
|
||||
```
|
||||
|
||||
## Rialto Runtime
|
||||
The node runtime consists of several runtime modules, however not all of them are used at the same
|
||||
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
|
||||
```
|
||||
├── bin // Node and Runtime for the various Substrate chains
|
||||
│ └── ...
|
||||
├── deployments // Useful tools for deploying test networks
|
||||
│ └── ...
|
||||
├── diagrams // Pretty pictures of the project architecture
|
||||
│ └── ...
|
||||
├── modules // Substrate Runtime Modules (a.k.a Pallets)
|
||||
│ ├── ethereum // Ethereum PoA Header Sync Module
|
||||
│ ├── substrate // Substrate Based Chain Header Sync Module
|
||||
│ ├── message-lane // Cross Chain Message Passing
|
||||
│ └── ...
|
||||
├── primitives // Code shared between modules, runtimes, and relays
|
||||
│ └── ...
|
||||
├── relays // Application for sending headers and messages between chains
|
||||
│ └── ...
|
||||
└── scripts // Useful development and maintenence scripts
|
||||
```
|
||||
|
||||
## Running the Bridge
|
||||
|
||||
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
|
||||
through Docker.
|
||||
on each side of the bridge (source and target chain).
|
||||
|
||||
### 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
|
||||
# In `parity-bridges-common` folder
|
||||
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).
|
||||
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
|
||||
### Running
|
||||
|
||||
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
|
||||
chains it must be run last.
|
||||
[the `scripts` folder](./scripts). Since the relay connects to both Substrate chains it must be run
|
||||
last.
|
||||
|
||||
```bash
|
||||
# In `parity-bridges-common` folder
|
||||
./scripts/run-openethereum-node.sh
|
||||
./scripts/run-rialto-bridge-node.sh
|
||||
./scripts/run-eth2sub-relay.sh
|
||||
./deployments/local-scripts/run-rialto-bridge-node.sh
|
||||
./deployments/local-scripts/run-millau-bridge-node.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
|
||||
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:
|
||||
|
||||
```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
|
||||
docker build . -t bridge-node-dev --build-arg PROJECT=rialto-bridge-node
|
||||
```
|
||||
|
||||
To run the Substrate node you can do the following:
|
||||
|
||||
```bash
|
||||
docker run -it bridge-node-dev --dev --tmp
|
||||
docker run -it local/rialto-bridge-node --dev --tmp
|
||||
docker run -it local/millau-bridge-node --dev --tmp
|
||||
docker run -it local/substrate-relay
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Full Network Docker Setup
|
||||
See [Deployments README](./deployments/README.md) to learn more about how to run
|
||||
a more sophisticated test network using `docker-compose` setup.
|
||||
### Full Network 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
|
||||
//
|
||||
// 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
|
||||
// decided to simplify our import process and disallow header imports until we get a finality proof.
|
||||
#[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]
|
||||
//
|
||||
// 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
|
||||
// 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).
|
||||
#[test]
|
||||
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
|
||||
// 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
|
||||
//! will ever be moved to the sp_finality_grandpa, we should reuse that implementation.
|
||||
@@ -123,7 +123,7 @@ where
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
//! 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
|
||||
//! 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
|
||||
//! 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.
|
||||
BestFinalized: BridgedBlockHash<T>;
|
||||
/// 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>;
|
||||
/// Headers which have been imported into the pallet.
|
||||
ImportedHeaders: map hasher(identity) BridgedBlockHash<T> => Option<ImportedHeader<BridgedHeader<T>>>;
|
||||
/// The current Grandpa Authority set.
|
||||
/// The current GRANDPA Authority set.
|
||||
CurrentAuthoritySet: AuthoritySet;
|
||||
/// 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).
|
||||
/// 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.
|
||||
NextScheduledChange: map hasher(identity) BridgedBlockHash<T> => Option<ScheduledChange<BridgedBlockNumber<T>>>;
|
||||
/// Optional pallet owner.
|
||||
@@ -448,10 +448,10 @@ pub trait BridgeStorage {
|
||||
/// 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>>;
|
||||
|
||||
/// Get the current Grandpa authority set.
|
||||
/// Get the current GRANDPA authority set.
|
||||
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.
|
||||
fn update_current_authority_set(&self, new_set: AuthoritySet);
|
||||
@@ -462,13 +462,13 @@ pub trait BridgeStorage {
|
||||
#[allow(clippy::result_unit_err)]
|
||||
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(
|
||||
&self,
|
||||
signal_hash: <Self::Header as HeaderT>::Hash,
|
||||
) -> 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.
|
||||
fn schedule_next_set_change(
|
||||
|
||||
@@ -42,24 +42,24 @@ pub struct InitializationData<H: HeaderT> {
|
||||
pub is_halted: bool,
|
||||
}
|
||||
|
||||
/// A Grandpa Authority List and ID.
|
||||
/// A GRANDPA Authority List and ID.
|
||||
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct AuthoritySet {
|
||||
/// List of Grandpa authorities for the current round.
|
||||
/// List of GRANDPA authorities for the current round.
|
||||
pub authorities: AuthorityList,
|
||||
/// Monotonic identifier of the current Grandpa authority set.
|
||||
/// Monotonic identifier of the current GRANDPA authority set.
|
||||
pub set_id: SetId,
|
||||
}
|
||||
|
||||
impl AuthoritySet {
|
||||
/// Create a new Grandpa Authority Set.
|
||||
/// Create a new GRANDPA Authority Set.
|
||||
pub fn new(authorities: AuthorityList, set_id: SetId) -> Self {
|
||||
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)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct ScheduledChange<N> {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! 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
|
||||
//! 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.
|
||||
|
||||
use crate::justification::verify_justification;
|
||||
@@ -34,8 +34,8 @@ use sp_std::{prelude::Vec, vec};
|
||||
|
||||
/// The finality proof used by the pallet.
|
||||
///
|
||||
/// For a Substrate based chain using Grandpa this will
|
||||
/// be an encoded Grandpa Justification.
|
||||
/// For a Substrate based chain using GRANDPA this will
|
||||
/// be an encoded GRANDPA Justification.
|
||||
#[derive(RuntimeDebug)]
|
||||
pub struct FinalityProof(Vec<u8>);
|
||||
|
||||
@@ -139,7 +139,7 @@ where
|
||||
// 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
|
||||
// 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.
|
||||
let mut signal_hash = parent_header.signal_hash;
|
||||
let scheduled_change = find_scheduled_change(&header);
|
||||
@@ -213,7 +213,7 @@ where
|
||||
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
|
||||
/// header has been finalized.
|
||||
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 _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
|
||||
let header = test_header(2);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ pub trait Chain: Send + Sync + 'static {
|
||||
// See here for more info:
|
||||
// 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.
|
||||
type BlockNumber: Parameter
|
||||
+ Member
|
||||
|
||||
Reference in New Issue
Block a user