Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Initial version of bridging pallets as git subtree #2458

Merged
merged 23 commits into from
May 4, 2023
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
795fe4e
Initial version of bridges pallet as subtree of https://github.com/pa…
bkontur Apr 14, 2023
a9e2773
Squashed 'bridges/' content from commit d30927c08
bkontur Apr 17, 2023
71a1d47
Merge commit 'a9e27730296e3269b82cd315aed9d635610f076c' as 'bridges'
bkontur Apr 17, 2023
1e489dd
Updated REAMDE.md and BRIDGES.md (inspired by original https://github…
bkontur Apr 17, 2023
0670d9f
Squashed 'bridges/' changes from d30927c08..d3970944b
bkontur Apr 17, 2023
71aad1e
Merge commit '0670d9fa5ae67f6f01028440318a723d1b330720' into bridge-h…
bkontur Apr 17, 2023
f2277b6
Squashed 'bridges/' changes from d3970944b..2180797fb
bkontur Apr 18, 2023
873f5cc
Merge commit 'f2277b6f81208bd38a15cb74ebd535c4cd851b32' into bridge-h…
bkontur Apr 18, 2023
8f2b30d
Merge remote-tracking branch 'origin/master' into bridge-hub-subtree
bkontur Apr 18, 2023
ad6bfdf
Merge remote-tracking branch 'origin/master' into bridge-hub-subtree
bkontur Apr 27, 2023
9c5701c
Squashed 'bridges/' changes from 2180797fbf..4850aac8ce
bkontur Apr 27, 2023
a6bf890
Merge commit '9c5701c8ccafd307cae9abff43e8a3f86e855f47' into bridge-h…
bkontur Apr 27, 2023
6c29018
Merge commit 'fd69d9414cdc2586be3e26d7599dc50b587c7428' into bridge-h…
bkontur Apr 27, 2023
fd69d94
Squashed 'bridges/' changes from 4850aac8ce..66aaf0dd23
bkontur Apr 27, 2023
4f7c913
Merge remote-tracking branch 'origin/master' into bridge-hub-subtree
Apr 27, 2023
17fc1c1
Merge remote-tracking branch 'origin/master' into bridge-hub-subtree
bkontur May 3, 2023
8b62a00
Squashed 'bridges/' changes from 66aaf0dd23..557ecbcecc
bkontur May 3, 2023
00e45c5
Merge commit '8b62a005717fbe976832504b1f0c39eee19ac0a6' into bridge-h…
bkontur May 3, 2023
b93712d
fmt
bkontur May 3, 2023
e6497d4
Merge remote-tracking branch 'origin/master' into bridge-hub-subtree
bkontur May 3, 2023
65ee2dc
Squashed 'bridges/' changes from 557ecbcecc..04b3dda6aa
bkontur May 3, 2023
6dd9e36
Merge commit '65ee2dc77f5c0832f48b5331371483ff5c3c2574' into bridge-h…
bkontur May 3, 2023
1a986c5
".git/.scripts/commands/fmt/fmt.sh"
May 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/pr-custom-review.yml
Original file line number Diff line number Diff line change
@@ -19,12 +19,20 @@ rules:
check_type: changed_files
condition:
include: .*
# excluding files from 'Runtime files' and 'CI files' rules
# excluding files from 'Runtime files' and 'CI files' rules and `Bridges subtree files`
exclude: ^parachains/runtimes/assets/(statemine|statemint)/src/[^/]+\.rs$|^parachains/runtimes/bridge-hubs/(bridge-hub-kusama|bridge-hub-polkadot)/src/[^/]+\.rs$|^parachains/runtimes/collectives/collectives-polkadot/src/[^/]+\.rs$|^parachains/common/src/[^/]+\.rs$|^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.*
min_approvals: 2
teams:
- core-devs

# if there are any changes in the bridges subtree (in case of backport changes back to bridges repo)
- name: Bridges subtree files
check_type: changed_files
condition: ^bridges/.*
min_approvals: 1
teams:
- bridges-core

- name: CI files
check_type: changed_files
condition:
88 changes: 88 additions & 0 deletions BRIDGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Using Parity Bridges Common dependency (`git subtree`).

In `./bridges` sub-directory you can find a `git subtree` imported version of:
[parity-bridges-common](https://github.com/paritytech/parity-bridges-common/) repository.

(For regular Cumulus contributor 1. is relevant) \
(For Cumulus maintainer 1. and 2. are relevant) \
(For Bridges team 1. and 2. and 3. are relevant)

# 1. How to fix broken Bridges code?

To fix Bridges code simply create a commit in current (`Cumulus`) repo. Best if
the commit is isolated to changes in `./bridges` sub-directory, because it makes
it easier to import that change back to upstream repo.

(Any changes to `bridges` subtree require Bridges team approve and they should manage backport to Bridges repo)


# 2. How to pull latest Bridges code to the `bridges` subtree
(in practice)
```
cd <cumulus-git-repo-dir>
# this will update new git branches from bridges repo
# there could be unresolved conflicts, but dont worry,
# lots of them are caused because of removed unneeded files with patch step,
./scripts/bridges_update_subtree.sh fetch
# so, after fetch and before solving conflicts just run patch,
# this will remove unneeded files and checks if subtree modules compiles
./scripts/bridges_update_subtree.sh patch
# if there are conflicts, this could help,
# this removes locally deleted files at least (move changes to git stash for commit)
./scripts/bridges_update_subtree.sh merge
# (optional) when conflicts resolved, you can check build again - should pass
# also important: this updates global Cargo.lock
./scripts/bridges_update_subtree.sh patch
# add changes to the commit, first command `fetch` starts merge,
# so after all conflicts are solved and patch passes and compiles,
# then we need to finish merge with:
git merge --continue
````
# 3. How to pull latest Bridges code or contribute back?
(in theory)
Note that it's totally fine to ping the **Bridges Team** to do that for you. The point
of adding the code as `git subtree` is to **reduce maintenance cost** for Cumulus/Polkadot
developers.
If you still would like to either update the code to match latest code from the repo
or create an upstream PR read below. The following commands should be run in the
current (`polkadot`) repo.
1. Add Bridges repo as a local remote:
```
$ git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git
```
If you plan to contribute back, consider forking the repository on Github and adding
your personal fork as a remote as well.
```
$ git remote add -f my-bridges git@github.com:tomusdrw/parity-bridges-common.git
```
2. To update Bridges:
```
$ git fetch bridges master
$ git subtree pull --prefix=bridges bridges master --squash
````
We use `--squash` to avoid adding individual commits and rather squashing them
all into one.
3. Clean unneeded files here:
```
./bridges/scripts/verify-pallets-build.sh --ignore-git-state --no-revert
```
4. Contributing back to Bridges (creating upstream PR)
```
$ git subtree push --prefix=bridges my-bridges master
```
This command will push changes to your personal fork of Bridges repo, from where
you can simply create a PR to the main repo.
333 changes: 331 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -2,6 +2,11 @@
resolver = "2"

members = [
"bridges/bin/runtime-common",
"bridges/modules/grandpa",
"bridges/modules/messages",
"bridges/modules/parachains",
"bridges/modules/relayers",
"client/cli",
"client/consensus/aura",
"client/consensus/common",
26 changes: 26 additions & 0 deletions bridges/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
**/target/
**/.env
**/.env2
**/rust-toolchain
hfuzz_target
hfuzz_workspace
**/Cargo.lock

**/*.rs.bk

*.o
*.so
*.rlib
*.dll
.gdb_history

*.exe

.DS_Store

.cargo
.idea
.vscode
*.iml
*.swp
*.swo
80 changes: 80 additions & 0 deletions bridges/CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers
pledge to making participation in our project and our community a harassment-free experience for
everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity
and expression, level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit
permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

### Facilitation, Not Strongarming

We recognise that this software is merely a tool for users to create and maintain their blockchain
of preference. We see that blockchains are naturally community platforms with users being the
ultimate decision makers. We assert that good software will maximise user agency by facilitate
user-expression on the network. As such:

- This project will strive to give users as much choice as is both reasonable and possible over what
protocol they adhere to; but
- use of the project's technical forums, commenting systems, pull requests and issue trackers as a
means to express individual protocol preferences is forbidden.

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are
expected to take appropriate and fair corrective action in response to any instances of unacceptable
behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits,
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or
to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is
representing the project or its community. Examples of representing a project or community include
using an official project e-mail address, posting via an official social media account, or acting as
an appointed representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting
the project team at admin@parity.io. All complaints will be reviewed and investigated and will
result in a response that is deemed necessary and appropriate to the circumstances. The project team
is obligated to maintain confidentiality with regard to the reporter of an incident. Further
details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face
temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
675 changes: 675 additions & 0 deletions bridges/LICENSE

Large diffs are not rendered by default.

259 changes: 259 additions & 0 deletions bridges/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# Parity Bridges Common

This is a collection of components for building bridges.

These components include Substrate pallets for syncing headers, passing arbitrary messages, as well
as libraries for building relayers to provide cross-chain communication capabilities.

Three bridge nodes are also available. The nodes can be used to run test networks which bridge other
Substrate 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)
- [Running the Bridge](#running-the-bridge)
- [How to send a message](#how-to-send-a-message)
- [Community](#community)

## Installation

To get up and running you need both stable and nightly Rust. Rust nightly is used to build the Web
Assembly (WASM) runtime for the node. You can configure the WASM support as so:

```bash
rustup install nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
```

Once this is configured you can build and test the repo as follows:

```
git clone https://github.com/paritytech/parity-bridges-common.git
cd parity-bridges-common
cargo build --all
cargo test --all
```

Also you can build the repo with
[Parity CI Docker image](https://github.com/paritytech/scripts/tree/master/dockerfiles/bridges-ci):

```bash
docker pull paritytech/bridges-ci:production
mkdir ~/cache
chown 1000:1000 ~/cache #processes in the container runs as "nonroot" user with UID 1000
docker run --rm -it -w /shellhere/parity-bridges-common \
-v /home/$(whoami)/cache/:/cache/ \
-v "$(pwd)":/shellhere/parity-bridges-common \
-e CARGO_HOME=/cache/cargo/ \
-e SCCACHE_DIR=/cache/sccache/ \
-e CARGO_TARGET_DIR=/cache/target/ paritytech/bridges-ci:production cargo build --all
#artifacts can be found in ~/cache/target
```

If you want to reproduce other steps of CI process you can use the following
[guide](https://github.com/paritytech/scripts#reproduce-ci-locally).

If you need more information about setting up your development environment [Substrate's
Installation page](https://docs.substrate.io/main-docs/install/) 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 `bin`, 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 and Runtime for the various Substrate chains
│ └── ...
├── deployments // Useful tools for deploying test networks
│ └── ...
├── modules // Substrate Runtime Modules (a.k.a Pallets)
│ ├── beefy // On-Chain BEEFY Light Client (in progress)
│ ├── grandpa // On-Chain GRANDPA Light Client
│ ├── messages // Cross Chain Message Passing
│ ├── parachains // On-Chain Parachains Light Client
│ ├── relayers // Relayer rewards registry
│ └── ...
├── primitives // Code shared between modules, runtimes, and relays
│ └── ...
├── relays // Application for sending finality proofs and messages between chains
│ └── ...
└── scripts // Useful development and maintenance 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 (source and target chain).

There are 2 ways to run the bridge, described below:

- building & running from source: with this option, you'll be able to run the bridge between two standalone
chains that are running GRANDPA finality gadget to achieve finality;

- running a Docker Compose setup: this is a recommended option, where you'll see bridges with parachains,
complex relays and more.

### Using the Source

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 millau-bridge-node
cargo build -p substrate-relay
```

### Running a Dev network

We will launch a dev network to demonstrate how to relay a message between two Substrate based
chains (named Rialto and Millau).

To do this we will need two nodes, two relayers which will relay headers, and two relayers which
will relay messages.

#### Running from local scripts

To run a simple dev network you can use the scripts located in the
[`deployments/local-scripts` folder](./deployments/local-scripts).

First, we must run the two Substrate nodes.

```bash
# In `parity-bridges-common` folder
./deployments/local-scripts/run-rialto-node.sh
./deployments/local-scripts/run-millau-node.sh
```

After the nodes are up we can run the header relayers.

```bash
./deployments/local-scripts/relay-millau-to-rialto.sh
./deployments/local-scripts/relay-rialto-to-millau.sh
```

At this point you should see the relayer submitting headers from the Millau Substrate chain to the
Rialto Substrate chain.

```
# Header Relayer Logs
[Millau_to_Rialto_Sync] [date] DEBUG bridge Going to submit finality proof of Millau header #147 to Rialto
[...] [date] INFO bridge Synced 147 of 147 headers
[...] [date] DEBUG bridge Going to submit finality proof of Millau header #148 to Rialto
[...] [date] INFO bridge Synced 148 of 149 headers
```

Finally, we can run the message relayers.

```bash
./deployments/local-scripts/relay-messages-millau-to-rialto.sh
./deployments/local-scripts/relay-messages-rialto-to-millau.sh
```

You will also see the message lane relayers listening for new messages.

```
# Message Relayer Logs
[Millau_to_Rialto_MessageLane_00000000] [date] DEBUG bridge Asking Millau::ReceivingConfirmationsDelivery about best message nonces
[...] [date] INFO bridge Synced Some(2) of Some(3) nonces in Millau::MessagesDelivery -> Rialto::MessagesDelivery race
[...] [date] DEBUG bridge Asking Millau::MessagesDelivery about message nonces
[...] [date] DEBUG bridge Received best nonces from Millau::ReceivingConfirmationsDelivery: TargetClientNonces { latest_nonce: 0, nonces_data: () }
[...] [date] DEBUG bridge Asking Millau::ReceivingConfirmationsDelivery about finalized message nonces
[...] [date] DEBUG bridge Received finalized nonces from Millau::ReceivingConfirmationsDelivery: TargetClientNonces { latest_nonce: 0, nonces_data: () }
[...] [date] DEBUG bridge Received nonces from Millau::MessagesDelivery: SourceClientNonces { new_nonces: {}, confirmed_nonce: Some(0) }
[...] [date] DEBUG bridge Asking Millau node about its state
[...] [date] DEBUG bridge Received state from Millau node: ClientState { best_self: HeaderId(1593, 0xacac***), best_finalized_self: HeaderId(1590, 0x0be81d...), best_finalized_peer_at_best_self: HeaderId(0, 0xdcdd89...) }
```

To send a message see the ["How to send a message" section](#how-to-send-a-message).

### How to send a message

In this section we'll show you how to quickly send a bridge message. The message is just an encoded XCM
`Trap(43)` message.

```bash
# In `parity-bridges-common` folder
./scripts/send-message-from-millau-rialto.sh
```

After sending a message you will see the following logs showing a message was successfully sent:

```
INFO bridge Sending message to Rialto. Size: 11.
TRACE bridge Sent transaction to Millau node: 0x5e68...
```

And at the Rialto node logs you'll something like this:

```
... runtime::bridge-messages: Received messages: total=1, valid=1. Weight used: Weight(ref_time: 1215065371, proof_size: 48559)/Weight(ref_time: 1215065371, proof_size: 54703).
```

It means that the message has been delivered and dispatched. Message may be dispatched with an
error, though - the goal of our test bridge is to ensure that messages are successfully delivered
and all involved components are working.

## 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).

You should note that you can find images for all the bridge components published on
[Docker Hub](https://hub.docker.com/u/paritytech).

To run a Rialto node for example, you can use the following command:

```bash
docker run -p 30333:30333 -p 9933:9933 -p 9944:9944 \
-it paritytech/rialto-bridge-node --dev --tmp \
--rpc-cors=all --unsafe-rpc-external --unsafe-ws-external
```

## Community

Main hangout for the community is [Element](https://element.io/) (formerly Riot). Element is a chat
server like, for example, Discord. Most discussions around Polkadot and Substrate happen
in various Element "rooms" (channels). So, joining Element might be a good idea, anyway.

If you are interested in information exchange and development of Polkadot related bridges please
feel free to join the [Polkadot Bridges](https://app.element.io/#/room/#bridges:web3.foundation)
Element channel.

The [Substrate Technical](https://app.element.io/#/room/#substrate-technical:matrix.org) Element
channel is most suited for discussions regarding Substrate itself.
14 changes: 14 additions & 0 deletions bridges/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Security Policy

Thanks for helping make the Parity ecosystem more secure. Security is one of our first priorities.

## Reporting a vulnerability

If you find something that can be treated as a security vulnerability, please do not use the issue tracker or discuss it in the public forum as it can cause more damage, rather than giving real help to the ecosystem.

Security vulnerabilities should be reported by the [contact form](https://security-submission.parity.io/).

If you think that your report might be eligible for the Bug Bounty Program, please mark this during the submission. Please check up-to-date [Parity Bug Bounty Program rules](https://www.parity.io/bug-bounty) to find out the information about our Bug Bounty Program.

**Warning**: This is an unified SECURITY.md file for Paritytech GitHub Organization. The presence of this file does not mean that this repository is covered by the Bug Bounty program. Please always check the Bug Bounty Program scope for information.

92 changes: 92 additions & 0 deletions bridges/bin/runtime-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
[package]
name = "bridge-runtime-common"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
repository = "https://github.com/paritytech/parity-bridges-common/"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"

[dependencies]
codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] }
hash-db = { version = "0.16.0", default-features = false }
log = { version = "0.4.17", default-features = false }
scale-info = { version = "2.6.0", default-features = false, features = ["derive"] }
static_assertions = { version = "1.1", optional = true }

# Bridge dependencies

bp-header-chain = { path = "../../primitives/header-chain", default-features = false }
bp-messages = { path = "../../primitives/messages", default-features = false }
bp-parachains = { path = "../../primitives/parachains", default-features = false }
bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false }
bp-relayers = { path = "../../primitives/relayers", default-features = false }
bp-runtime = { path = "../../primitives/runtime", default-features = false }
pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false }
pallet-bridge-messages = { path = "../../modules/messages", default-features = false }
pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false }
pallet-bridge-relayers = { path = "../../modules/relayers", default-features = false }

# Substrate dependencies

frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }

# Polkadot dependencies
pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false }
xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false }
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false }

[dev-dependencies]
bp-test-utils = { path = "../../primitives/test-utils" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }

[features]
default = ["std"]
std = [
"bp-header-chain/std",
"bp-messages/std",
"bp-parachains/std",
"bp-polkadot-core/std",
"bp-runtime/std",
"codec/std",
"frame-support/std",
"frame-system/std",
"hash-db/std",
"log/std",
"pallet-bridge-grandpa/std",
"pallet-bridge-messages/std",
"pallet-bridge-parachains/std",
"pallet-bridge-relayers/std",
"pallet-transaction-payment/std",
"pallet-utility/std",
"pallet-xcm/std",
"scale-info/std",
"sp-api/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"sp-trie/std",
"xcm/std",
"xcm-builder/std",
"xcm-executor/std",
]
runtime-benchmarks = [
"pallet-bridge-grandpa/runtime-benchmarks",
"pallet-bridge-messages/runtime-benchmarks",
"pallet-bridge-parachains/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
]
integrity-test = [
"static_assertions",
]
361 changes: 361 additions & 0 deletions bridges/bin/runtime-common/src/integrity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Integrity tests for chain constants and pallets configuration.
//!
//! Most of the tests in this module assume that the bridge is using standard (see `crate::messages`
//! module for details) configuration.
use crate::{messages, messages::MessageBridge};

use bp_messages::{InboundLaneData, MessageNonce};
use bp_runtime::{Chain, ChainId};
use codec::Encode;
use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weight};
use frame_system::limits;
use pallet_bridge_messages::WeightInfoExt as _;
use sp_runtime::traits::SignedExtension;

/// Macro that ensures that the runtime configuration and chain primitives crate are sharing
/// the same types (index, block number, hash, hasher, account id and header).
#[macro_export]
macro_rules! assert_chain_types(
( runtime: $r:path, this_chain: $this:path ) => {
{
// if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard
// configuration is used), or something has broke existing configuration (meaning that all bridged chains
// and relays will stop functioning)
use frame_system::Config as SystemConfig;
use static_assertions::assert_type_eq_all;

assert_type_eq_all!(<$r as SystemConfig>::Index, bp_runtime::IndexOf<$this>);
assert_type_eq_all!(<$r as SystemConfig>::BlockNumber, bp_runtime::BlockNumberOf<$this>);
assert_type_eq_all!(<$r as SystemConfig>::Hash, bp_runtime::HashOf<$this>);
assert_type_eq_all!(<$r as SystemConfig>::Hashing, bp_runtime::HasherOf<$this>);
assert_type_eq_all!(<$r as SystemConfig>::AccountId, bp_runtime::AccountIdOf<$this>);
assert_type_eq_all!(<$r as SystemConfig>::Header, bp_runtime::HeaderOf<$this>);
}
}
);

/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given
/// chain.
#[macro_export]
macro_rules! assert_bridge_grandpa_pallet_types(
( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => {
{
// if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard
// configuration is used), or something has broke existing configuration (meaning that all bridged chains
// and relays will stop functioning)
use pallet_bridge_grandpa::Config as GrandpaConfig;
use static_assertions::assert_type_eq_all;

assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged);
}
}
);

/// Macro that ensures that the bridge messages pallet is configured properly to bridge using given
/// configuration.
#[macro_export]
macro_rules! assert_bridge_messages_pallet_types(
(
runtime: $r:path,
with_bridged_chain_messages_instance: $i:path,
bridge: $bridge:path
) => {
{
// if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard
// configuration is used), or something has broke existing configuration (meaning that all bridged chains
// and relays will stop functioning)
use $crate::messages::{
source::{FromThisChainMessagePayload, TargetHeaderChainAdapter},
target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter},
AccountIdOf, BalanceOf, BridgedChain, ThisChain,
};
use pallet_bridge_messages::Config as MessagesConfig;
use static_assertions::assert_type_eq_all;

assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload);

assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf<BridgedChain<$bridge>>);

assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>);
assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, SourceHeaderChainAdapter<$bridge>);
}
}
);

/// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`,
/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used
/// at the chain that is implementing complete standard messages bridge (i.e. with bridge GRANDPA
/// and messages pallets deployed).
#[macro_export]
macro_rules! assert_complete_bridge_types(
(
runtime: $r:path,
with_bridged_chain_grandpa_instance: $gi:path,
with_bridged_chain_messages_instance: $mi:path,
bridge: $bridge:path,
this_chain: $this:path,
bridged_chain: $bridged:path,
) => {
$crate::assert_chain_types!(runtime: $r, this_chain: $this);
$crate::assert_bridge_grandpa_pallet_types!(
runtime: $r,
with_bridged_chain_grandpa_instance: $gi,
bridged_chain: $bridged
);
$crate::assert_bridge_messages_pallet_types!(
runtime: $r,
with_bridged_chain_messages_instance: $mi,
bridge: $bridge
);
}
);

/// Parameters for asserting chain-related constants.
#[derive(Debug)]
pub struct AssertChainConstants {
/// Block length limits of the chain.
pub block_length: limits::BlockLength,
/// Block weight limits of the chain.
pub block_weights: limits::BlockWeights,
}

/// Test that our hardcoded, chain-related constants, are matching chain runtime configuration.
///
/// In particular, this test ensures that:
///
/// 1) block weight limits are matching;
/// 2) block size limits are matching.
pub fn assert_chain_constants<R>(params: AssertChainConstants)
where
R: frame_system::Config,
{
// we don't check runtime version here, because in our case we'll be building relay from one
// repo and runtime will live in another repo, along with outdated relay version. To avoid
// unneeded commits, let's not raise an error in case of version mismatch.

// if one of following assert fails, it means that we may need to upgrade bridged chain and
// relay to use updated constants. If constants are now smaller than before, it may lead to
// undeliverable messages.

// `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here.
assert_eq!(
R::BlockLength::get().encode(),
params.block_length.encode(),
"BlockLength from runtime ({:?}) differ from hardcoded: {:?}",
R::BlockLength::get(),
params.block_length,
);
// `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here
assert_eq!(
R::BlockWeights::get().encode(),
params.block_weights.encode(),
"BlockWeights from runtime ({:?}) differ from hardcoded: {:?}",
R::BlockWeights::get(),
params.block_weights,
);
}

/// Test that the constants, used in GRANDPA pallet configuration are valid.
pub fn assert_bridge_grandpa_pallet_constants<R, GI>()
where
R: pallet_bridge_grandpa::Config<GI>,
GI: 'static,
{
assert!(
R::HeadersToKeep::get() > 0,
"HeadersToKeep ({}) must be larger than zero",
R::HeadersToKeep::get(),
);
}

/// Parameters for asserting messages pallet constants.
#[derive(Debug)]
pub struct AssertBridgeMessagesPalletConstants {
/// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged
/// chain.
pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce,
/// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain.
pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce,
/// Identifier of the bridged chain.
pub bridged_chain_id: ChainId,
}

/// Test that the constants, used in messages pallet configuration are valid.
pub fn assert_bridge_messages_pallet_constants<R, MI>(params: AssertBridgeMessagesPalletConstants)
where
R: pallet_bridge_messages::Config<MI>,
MI: 'static,
{
assert!(
!R::ActiveOutboundLanes::get().is_empty(),
"ActiveOutboundLanes ({:?}) must not be empty",
R::ActiveOutboundLanes::get(),
);
assert!(
R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx,
"MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}",
R::MaxUnrewardedRelayerEntriesAtInboundLane::get(),
params.max_unrewarded_relayers_in_bridged_confirmation_tx,
);
assert!(
R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx,
"MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}",
R::MaxUnconfirmedMessagesAtInboundLane::get(),
params.max_unconfirmed_messages_in_bridged_confirmation_tx,
);
assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id);
}

/// Parameters for asserting bridge pallet names.
#[derive(Debug)]
pub struct AssertBridgePalletNames<'a> {
/// Name of the messages pallet, deployed at the bridged chain and used to bridge with this
/// chain.
pub with_this_chain_messages_pallet_name: &'a str,
/// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged
/// chain.
pub with_bridged_chain_grandpa_pallet_name: &'a str,
/// Name of the messages pallet, deployed at this chain and used to bridge with the bridged
/// chain.
pub with_bridged_chain_messages_pallet_name: &'a str,
}

/// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants
/// from chain primitives crates.
pub fn assert_bridge_pallet_names<B, R, GI, MI>(params: AssertBridgePalletNames)
where
B: MessageBridge,
R: pallet_bridge_grandpa::Config<GI> + pallet_bridge_messages::Config<MI>,
GI: 'static,
MI: 'static,
{
assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name);
assert_eq!(
pallet_bridge_grandpa::PalletOwner::<R, GI>::storage_value_final_key().to_vec(),
bp_runtime::storage_value_key(params.with_bridged_chain_grandpa_pallet_name, "PalletOwner",).0,
);
assert_eq!(
pallet_bridge_messages::PalletOwner::<R, MI>::storage_value_final_key().to_vec(),
bp_runtime::storage_value_key(
params.with_bridged_chain_messages_pallet_name,
"PalletOwner",
)
.0,
);
}

/// Parameters for asserting complete standard messages bridge.
#[derive(Debug)]
pub struct AssertCompleteBridgeConstants<'a> {
/// Parameters to assert this chain constants.
pub this_chain_constants: AssertChainConstants,
/// Parameters to assert messages pallet constants.
pub messages_pallet_constants: AssertBridgeMessagesPalletConstants,
/// Parameters to assert pallet names constants.
pub pallet_names: AssertBridgePalletNames<'a>,
}

/// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge
/// GRANDPA and messages pallets deployed).
pub fn assert_complete_bridge_constants<R, GI, MI, B>(params: AssertCompleteBridgeConstants)
where
R: frame_system::Config
+ pallet_bridge_grandpa::Config<GI>
+ pallet_bridge_messages::Config<MI>,
GI: 'static,
MI: 'static,
B: MessageBridge,
{
assert_chain_constants::<R>(params.this_chain_constants);
assert_bridge_grandpa_pallet_constants::<R, GI>();
assert_bridge_messages_pallet_constants::<R, MI>(params.messages_pallet_constants);
assert_bridge_pallet_names::<B, R, GI, MI>(params.pallet_names);
}

/// Check that the message lane weights are correct.
pub fn check_message_lane_weights<
C: Chain,
T: frame_system::Config + pallet_bridge_messages::Config<MessagesPalletInstance>,
MessagesPalletInstance: 'static,
>(
bridged_chain_extra_storage_proof_size: u32,
this_chain_max_unrewarded_relayers: MessageNonce,
this_chain_max_unconfirmed_messages: MessageNonce,
// whether `RefundBridgedParachainMessages` extension is deployed at runtime and is used for
// refunding this bridge transactions?
//
// in other words: pass true for all known production chains
runtime_includes_refund_extension: bool,
) {
type Weights<T, MI> = <T as pallet_bridge_messages::Config<MI>>::WeightInfo;

// check basic weight assumptions
pallet_bridge_messages::ensure_weights_are_correct::<Weights<T, MessagesPalletInstance>>();

// check that weights allow us to receive messages
let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size
.saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size()));
pallet_bridge_messages::ensure_able_to_receive_message::<Weights<T, MessagesPalletInstance>>(
C::max_extrinsic_size(),
C::max_extrinsic_weight(),
max_incoming_message_proof_size,
messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()),
);

// check that weights allow us to receive delivery confirmations
let max_incoming_inbound_lane_data_proof_size =
InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _);
pallet_bridge_messages::ensure_able_to_receive_confirmation::<Weights<T, MessagesPalletInstance>>(
C::max_extrinsic_size(),
C::max_extrinsic_weight(),
max_incoming_inbound_lane_data_proof_size,
this_chain_max_unrewarded_relayers,
this_chain_max_unconfirmed_messages,
);

// check that extra weights of delivery/confirmation transactions include the weight
// of `RefundBridgedParachainMessages` operations. This signed extension assumes the worst case
// (i.e. slashing if delivery transaction was invalid) and refunds some weight if
// assumption was wrong (i.e. if we did refund instead of slashing). This check
// ensures the extension will not refund weight when it doesn't need to (i.e. if pallet
// weights do not account weights of refund extension).
if runtime_includes_refund_extension {
assert_ne!(
Weights::<T, MessagesPalletInstance>::receive_messages_proof_overhead_from_runtime(),
Weight::zero()
);
assert_ne!(
Weights::<T, MessagesPalletInstance>::receive_messages_delivery_proof_overhead_from_runtime(),
Weight::zero()
);
}
}

/// Check that the `AdditionalSigned` type of a wrapped runtime is the same as the one of the
/// corresponding actual runtime.
///
/// This method doesn't perform any `assert`. If the condition is not true it will generate a
/// compile-time error.
pub fn check_additional_signed<SignedExt, IndirectSignedExt: SignedExtension>()
where
SignedExt: SignedExtension,
IndirectSignedExt: SignedExtension<AdditionalSigned = SignedExt::AdditionalSigned>,
{
}
259 changes: 259 additions & 0 deletions bridges/bin/runtime-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Common types/functions that may be used by runtimes of all bridged chains.
#![cfg_attr(not(feature = "std"), no_std)]

use crate::messages_call_ext::MessagesCallSubType;
use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType;
use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype;
use sp_runtime::transaction_validity::TransactionValidity;
use xcm::v3::NetworkId;

pub mod messages;
pub mod messages_api;
pub mod messages_benchmarking;
pub mod messages_call_ext;
pub mod messages_xcm_extension;
pub mod parachains_benchmarking;
pub mod priority_calculator;
pub mod refund_relayer_extension;

mod messages_generation;
mod mock;

#[cfg(feature = "integrity-test")]
pub mod integrity;

const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch";

/// A duplication of the `FilterCall` trait.
///
/// We need this trait in order to be able to implement it for the messages pallet,
/// since the implementation is done outside of the pallet crate.
pub trait BridgeRuntimeFilterCall<Call> {
/// Checks if a runtime call is valid.
fn validate(call: &Call) -> TransactionValidity;
}

impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> for pallet_bridge_grandpa::Pallet<T, I>
where
T: pallet_bridge_grandpa::Config<I>,
T::RuntimeCall: GrandpaCallSubType<T, I>,
{
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call)
}
}

impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
for pallet_bridge_parachains::Pallet<T, I>
where
T: pallet_bridge_parachains::Config<I>,
T::RuntimeCall: ParachainsCallSubtype<T, I>,
{
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call)
}
}

impl<T: pallet_bridge_messages::Config<I>, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
for pallet_bridge_messages::Pallet<T, I>
where
T::RuntimeCall: MessagesCallSubType<T, I>,
{
/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation
/// transactions, that are delivering outdated messages/confirmations. Without this validation,
/// even honest relayers may lose their funds if there are multiple relays running and
/// submitting the same messages/confirmations.
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
call.check_obsolete_call()
}
}

/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension.
///
/// ## Example
///
/// ```nocompile
/// generate_bridge_reject_obsolete_headers_and_messages!{
/// Call, AccountId
/// BridgeRialtoGrandpa, BridgeWestendGrandpa,
/// BridgeRialtoParachains
/// }
/// ```
///
/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged
/// headers and messages. Without that extension, even honest relayers may lose their funds if
/// there are multiple relays running and submitting the same information.
#[macro_export]
macro_rules! generate_bridge_reject_obsolete_headers_and_messages {
($call:ty, $account_id:ty, $($filter_call:ty),*) => {
#[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)]
pub struct BridgeRejectObsoleteHeadersAndMessages;
impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages {
const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages";
type AccountId = $account_id;
type Call = $call;
type AdditionalSigned = ();
type Pre = ();

fn additional_signed(&self) -> sp_std::result::Result<
(),
sp_runtime::transaction_validity::TransactionValidityError,
> {
Ok(())
}

fn validate(
&self,
_who: &Self::AccountId,
call: &Self::Call,
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
_len: usize,
) -> sp_runtime::transaction_validity::TransactionValidity {
let valid = sp_runtime::transaction_validity::ValidTransaction::default();
$(
let valid = valid
.combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?);
)*
Ok(valid)
}

fn pre_dispatch(
self,
who: &Self::AccountId,
call: &Self::Call,
info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
len: usize,
) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
self.validate(who, call, info, len).map(drop)
}
}
};
}

/// A mapping over `NetworkId`.
/// Since `NetworkId` doesn't include `Millau`, `Rialto` and `RialtoParachain`, we create some
/// synthetic associations between these chains and `NetworkId` chains.
pub enum CustomNetworkId {
/// The Millau network ID, associated with Kusama.
Millau,
/// The Rialto network ID, associated with Polkadot.
Rialto,
/// The RialtoParachain network ID, associated with Westend.
RialtoParachain,
}

impl TryFrom<bp_runtime::ChainId> for CustomNetworkId {
type Error = ();

fn try_from(chain: bp_runtime::ChainId) -> Result<Self, Self::Error> {
Ok(match chain {
bp_runtime::MILLAU_CHAIN_ID => Self::Millau,
bp_runtime::RIALTO_CHAIN_ID => Self::Rialto,
bp_runtime::RIALTO_PARACHAIN_CHAIN_ID => Self::RialtoParachain,
_ => return Err(()),
})
}
}

impl CustomNetworkId {
/// Converts self to XCM' network id.
pub const fn as_network_id(&self) -> NetworkId {
match *self {
CustomNetworkId::Millau => NetworkId::Kusama,
CustomNetworkId::Rialto => NetworkId::Polkadot,
CustomNetworkId::RialtoParachain => NetworkId::Westend,
}
}
}

#[cfg(test)]
mod tests {
use crate::BridgeRuntimeFilterCall;
use frame_support::{assert_err, assert_ok};
use sp_runtime::{
traits::SignedExtension,
transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
};

pub struct MockCall {
data: u32,
}

impl sp_runtime::traits::Dispatchable for MockCall {
type RuntimeOrigin = ();
type Config = ();
type Info = ();
type PostInfo = ();

fn dispatch(
self,
_origin: Self::RuntimeOrigin,
) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
unimplemented!()
}
}

struct FirstFilterCall;
impl BridgeRuntimeFilterCall<MockCall> for FirstFilterCall {
fn validate(call: &MockCall) -> TransactionValidity {
if call.data <= 1 {
return InvalidTransaction::Custom(1).into()
}

Ok(ValidTransaction { priority: 1, ..Default::default() })
}
}

struct SecondFilterCall;
impl BridgeRuntimeFilterCall<MockCall> for SecondFilterCall {
fn validate(call: &MockCall) -> TransactionValidity {
if call.data <= 2 {
return InvalidTransaction::Custom(2).into()
}

Ok(ValidTransaction { priority: 2, ..Default::default() })
}
}

#[test]
fn test() {
generate_bridge_reject_obsolete_headers_and_messages!(
MockCall,
(),
FirstFilterCall,
SecondFilterCall
);

assert_err!(
BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0),
InvalidTransaction::Custom(1)
);

assert_err!(
BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0),
InvalidTransaction::Custom(2)
);

assert_ok!(
BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0),
ValidTransaction { priority: 3, ..Default::default() }
)
}
}
779 changes: 779 additions & 0 deletions bridges/bin/runtime-common/src/messages.rs

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions bridges/bin/runtime-common/src/messages_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Helpers for implementing various message-related runtime API mthods.
use bp_messages::{
InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails,
};
use sp_std::vec::Vec;

/// Implementation of the `To*OutboundLaneApi::message_details`.
pub fn outbound_message_details<Runtime, MessagesPalletInstance>(
lane: LaneId,
begin: MessageNonce,
end: MessageNonce,
) -> Vec<OutboundMessageDetails>
where
Runtime: pallet_bridge_messages::Config<MessagesPalletInstance>,
MessagesPalletInstance: 'static,
{
(begin..=end)
.filter_map(|nonce| {
let message_data =
pallet_bridge_messages::Pallet::<Runtime, MessagesPalletInstance>::outbound_message_data(lane, nonce)?;
Some(OutboundMessageDetails {
nonce,
// dispatch message weight is always zero at the source chain, since we're paying for
// dispatch at the target chain
dispatch_weight: frame_support::weights::Weight::zero(),
size: message_data.len() as _,
})
})
.collect()
}

/// Implementation of the `To*InboundLaneApi::message_details`.
pub fn inbound_message_details<Runtime, MessagesPalletInstance>(
lane: LaneId,
messages: Vec<(MessagePayload, OutboundMessageDetails)>,
) -> Vec<InboundMessageDetails>
where
Runtime: pallet_bridge_messages::Config<MessagesPalletInstance>,
MessagesPalletInstance: 'static,
{
messages
.into_iter()
.map(|(payload, details)| {
pallet_bridge_messages::Pallet::<Runtime, MessagesPalletInstance>::inbound_message_data(
lane, payload, details,
)
})
.collect()
}
293 changes: 293 additions & 0 deletions bridges/bin/runtime-common/src/messages_benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Everything required to run benchmarks of messages module, based on
//! `bridge_runtime_common::messages` implementation.
#![cfg(feature = "runtime-benchmarks")]

use crate::{
messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain,
},
messages_generation::{
encode_all_messages, encode_lane_data, grow_trie_leaf_value, prepare_messages_storage_proof,
},
};

use bp_messages::storage_keys;
use bp_polkadot_core::parachains::ParaHash;
use bp_runtime::{
record_all_trie_keys, Chain, Parachain, RawStorageProof, StorageProofSize, UnderlyingChainOf,
};
use codec::Encode;
use frame_support::weights::Weight;
use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams};
use sp_runtime::traits::{Header, Zero};
use sp_std::prelude::*;
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
use xcm::v3::prelude::*;

/// Prepare inbound bridge message according to given message proof parameters.
fn prepare_inbound_message(
params: &MessageProofParams,
destination: InteriorMultiLocation,
) -> Vec<u8> {
// we only care about **this** message size when message proof needs to be `Minimal`
let expected_size = match params.size {
StorageProofSize::Minimal(size) => size as usize,
_ => 0,
};

// if we don't need a correct message, then we may just return some random blob
if !params.is_successful_dispatch_expected {
return vec![0u8; expected_size]
}

// else let's prepare successful message. For XCM bridge hubs, it is the message that
// will be pushed further to some XCM queue (XCMP/UMP)
let location = xcm::VersionedInteriorMultiLocation::V3(destination);
let location_encoded_size = location.encoded_size();

// we don't need to be super-precise with `expected_size` here
let xcm_size = expected_size.saturating_sub(location_encoded_size);
let xcm = xcm::VersionedXcm::<()>::V3(vec![Instruction::ClearOrigin; xcm_size].into());

// this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor
// or public fields, so just tuple
// (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed
// to the storage)
(location, xcm).encode().encode()
}

/// Prepare proof of messages for the `receive_messages_proof` call.
///
/// In addition to returning valid messages proof, environment is prepared to verify this message
/// proof.
///
/// This method is intended to be used when benchmarking pallet, linked to the chain that
/// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain`
/// function.
pub fn prepare_message_proof_from_grandpa_chain<R, FI, B>(
params: MessageProofParams,
message_destination: InteriorMultiLocation,
) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
where
R: pallet_bridge_grandpa::Config<FI, BridgedChain = UnderlyingChainOf<BridgedChain<B>>>,
FI: 'static,
B: MessageBridge,
{
// prepare storage proof
let (state_root, storage_proof) = prepare_messages_storage_proof::<B>(
params.lane,
params.message_nonces.clone(),
params.outbound_lane_data.clone(),
params.size,
prepare_inbound_message(&params, message_destination),
encode_all_messages,
encode_lane_data,
);

// update runtime storage
let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(state_root);

(
FromBridgedChainMessagesProof {
bridged_header_hash,
storage_proof,
lane: params.lane,
nonces_start: *params.message_nonces.start(),
nonces_end: *params.message_nonces.end(),
},
Weight::MAX / 1000,
)
}

/// Prepare proof of messages for the `receive_messages_proof` call.
///
/// In addition to returning valid messages proof, environment is prepared to verify this message
/// proof.
///
/// This method is intended to be used when benchmarking pallet, linked to the chain that
/// uses parachain finality. For GRANDPA chains, please use the
/// `prepare_message_proof_from_grandpa_chain` function.
pub fn prepare_message_proof_from_parachain<R, PI, B>(
params: MessageProofParams,
message_destination: InteriorMultiLocation,
) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
where
R: pallet_bridge_parachains::Config<PI>,
PI: 'static,
B: MessageBridge,
UnderlyingChainOf<BridgedChain<B>>: Chain<Hash = ParaHash> + Parachain,
{
// prepare storage proof
let (state_root, storage_proof) = prepare_messages_storage_proof::<B>(
params.lane,
params.message_nonces.clone(),
params.outbound_lane_data.clone(),
params.size,
prepare_inbound_message(&params, message_destination),
encode_all_messages,
encode_lane_data,
);

// update runtime storage
let (_, bridged_header_hash) =
insert_header_to_parachains_pallet::<R, PI, UnderlyingChainOf<BridgedChain<B>>>(state_root);

(
FromBridgedChainMessagesProof {
bridged_header_hash,
storage_proof,
lane: params.lane,
nonces_start: *params.message_nonces.start(),
nonces_end: *params.message_nonces.end(),
},
Weight::MAX / 1000,
)
}

/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call.
///
/// This method is intended to be used when benchmarking pallet, linked to the chain that
/// uses GRANDPA finality. For parachains, please use the
/// `prepare_message_delivery_proof_from_parachain` function.
pub fn prepare_message_delivery_proof_from_grandpa_chain<R, FI, B>(
params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>,
) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>
where
R: pallet_bridge_grandpa::Config<FI, BridgedChain = UnderlyingChainOf<BridgedChain<B>>>,
FI: 'static,
B: MessageBridge,
{
// prepare storage proof
let lane = params.lane;
let (state_root, storage_proof) = prepare_message_delivery_proof::<B>(params);

// update runtime storage
let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(state_root);

FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: bridged_header_hash.into(),
storage_proof,
lane,
}
}

/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call.
///
/// This method is intended to be used when benchmarking pallet, linked to the chain that
/// uses parachain finality. For GRANDPA chains, please use the
/// `prepare_message_delivery_proof_from_grandpa_chain` function.
pub fn prepare_message_delivery_proof_from_parachain<R, PI, B>(
params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>,
) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>
where
R: pallet_bridge_parachains::Config<PI>,
PI: 'static,
B: MessageBridge,
UnderlyingChainOf<BridgedChain<B>>: Chain<Hash = ParaHash> + Parachain,
{
// prepare storage proof
let lane = params.lane;
let (state_root, storage_proof) = prepare_message_delivery_proof::<B>(params);

// update runtime storage
let (_, bridged_header_hash) =
insert_header_to_parachains_pallet::<R, PI, UnderlyingChainOf<BridgedChain<B>>>(state_root);

FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: bridged_header_hash.into(),
storage_proof,
lane,
}
}

/// Prepare in-memory message delivery proof, without inserting anything to the runtime storage.
fn prepare_message_delivery_proof<B>(
params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>,
) -> (HashOf<BridgedChain<B>>, RawStorageProof)
where
B: MessageBridge,
{
// prepare Bridged chain storage with inbound lane state
let storage_key =
storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &params.lane).0;
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie =
TrieDBMutBuilderV1::<HasherOf<BridgedChain<B>>>::new(&mut mdb, &mut root).build();
let inbound_lane_data =
grow_trie_leaf_value(params.inbound_lane_data.encode(), params.size);
trie.insert(&storage_key, &inbound_lane_data)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
}

// generate storage proof to be delivered to This chain
let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain<B>>>, _>(&mdb, &root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");

(root, storage_proof)
}

/// Insert header to the bridge GRANDPA pallet.
pub(crate) fn insert_header_to_grandpa_pallet<R, GI>(
state_root: bp_runtime::HashOf<R::BridgedChain>,
) -> (bp_runtime::BlockNumberOf<R::BridgedChain>, bp_runtime::HashOf<R::BridgedChain>)
where
R: pallet_bridge_grandpa::Config<GI>,
GI: 'static,
R::BridgedChain: bp_runtime::Chain,
{
let bridged_block_number = Zero::zero();
let bridged_header = bp_runtime::HeaderOf::<R::BridgedChain>::new(
bridged_block_number,
Default::default(),
state_root,
Default::default(),
Default::default(),
);
let bridged_header_hash = bridged_header.hash();
pallet_bridge_grandpa::initialize_for_benchmarks::<R, GI>(bridged_header);
(bridged_block_number, bridged_header_hash)
}

/// Insert header to the bridge parachains pallet.
pub(crate) fn insert_header_to_parachains_pallet<R, PI, PC>(
state_root: bp_runtime::HashOf<PC>,
) -> (bp_runtime::BlockNumberOf<PC>, bp_runtime::HashOf<PC>)
where
R: pallet_bridge_parachains::Config<PI>,
PI: 'static,
PC: Chain<Hash = ParaHash> + Parachain,
{
let bridged_block_number = Zero::zero();
let bridged_header = bp_runtime::HeaderOf::<PC>::new(
bridged_block_number,
Default::default(),
state_root,
Default::default(),
Default::default(),
);
let bridged_header_hash = bridged_header.hash();
pallet_bridge_parachains::initialize_for_benchmarks::<R, PI, PC>(bridged_header);
(bridged_block_number, bridged_header_hash)
}
644 changes: 644 additions & 0 deletions bridges/bin/runtime-common/src/messages_call_ext.rs

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions bridges/bin/runtime-common/src/messages_generation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Helpers for generating message storage proofs, that are used by tests and by benchmarks.
#![cfg(any(feature = "runtime-benchmarks", test))]

use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge};

use bp_messages::{
storage_keys, LaneId, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
};
use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize};
use codec::Encode;
use sp_std::{ops::RangeInclusive, prelude::*};
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};

/// Simple and correct message data encode function.
pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option<Vec<u8>> {
Some(m.encode())
}

/// Simple and correct outbound lane data encode function.
pub(crate) fn encode_lane_data(d: &OutboundLaneData) -> Vec<u8> {
d.encode()
}

/// Prepare storage proof of given messages.
///
/// Returns state trie root and nodes with prepared messages.
pub(crate) fn prepare_messages_storage_proof<B>(
lane: LaneId,
message_nonces: RangeInclusive<MessageNonce>,
outbound_lane_data: Option<OutboundLaneData>,
size: StorageProofSize,
message_payload: MessagePayload,
encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
) -> (HashOf<BridgedChain<B>>, RawStorageProof)
where
B: MessageBridge,
HashOf<BridgedChain<B>>: Copy + Default,
{
// prepare Bridged chain storage with messages and (optionally) outbound lane state
let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1;
let mut storage_keys = Vec::with_capacity(message_count as usize + 1);
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie =
TrieDBMutBuilderV1::<HasherOf<BridgedChain<B>>>::new(&mut mdb, &mut root).build();

// insert messages
for (i, nonce) in message_nonces.into_iter().enumerate() {
let message_key = MessageKey { lane_id: lane, nonce };
let message_payload = match encode_message(nonce, &message_payload) {
Some(message_payload) =>
if i == 0 {
grow_trie_leaf_value(message_payload, size)
} else {
message_payload
},
None => continue,
};
let storage_key = storage_keys::message_key(
B::BRIDGED_MESSAGES_PALLET_NAME,
&message_key.lane_id,
message_key.nonce,
)
.0;
trie.insert(&storage_key, &message_payload)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
}

// insert outbound lane state
if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data)
{
let storage_key =
storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0;
trie.insert(&storage_key, &outbound_lane_data)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
}
}

// generate storage proof to be delivered to This chain
let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain<B>>>, _>(&mdb, &root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");
(root, storage_proof)
}

/// Add extra data to the trie leaf value so that it'll be of given size.
pub fn grow_trie_leaf_value(mut value: Vec<u8>, size: StorageProofSize) -> Vec<u8> {
match size {
StorageProofSize::Minimal(_) => (),
StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => {
value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len()));
},
StorageProofSize::HasLargeLeaf(_) => (),
}
value
}
152 changes: 152 additions & 0 deletions bridges/bin/runtime-common/src/messages_xcm_extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2023 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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 provides utilities for easier XCM handling, e.g:
//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue`
//! |
//! `Relayer`
//! |
//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue`
use bp_messages::{
source_chain::MessagesBridge,
target_chain::{DispatchMessage, MessageDispatch},
LaneId,
};
use bp_runtime::messages::MessageDispatchResult;
use codec::{Decode, Encode};
use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound};
use pallet_bridge_messages::WeightInfoExt as MessagesPalletWeights;
use scale_info::TypeInfo;
use sp_runtime::SaturatedConversion;
use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError};

/// Plain "XCM" payload, which we transfer through bridge
pub type XcmAsPlainPayload = sp_std::prelude::Vec<u8>;

/// Message dispatch result type for single message
#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)]
pub enum XcmBlobMessageDispatchResult {
InvalidPayload,
Dispatched,
NotDispatched(#[codec(skip)] Option<DispatchBlobError>),
}

/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages
pub struct XcmBlobMessageDispatch<DispatchBlob, Weights> {
_marker: sp_std::marker::PhantomData<(DispatchBlob, Weights)>,
}

impl<BlobDispatcher: DispatchBlob, Weights: MessagesPalletWeights> MessageDispatch
for XcmBlobMessageDispatch<BlobDispatcher, Weights>
{
type DispatchPayload = XcmAsPlainPayload;
type DispatchLevelResult = XcmBlobMessageDispatchResult;

fn dispatch_weight(message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
match message.data.payload {
Ok(ref payload) => {
let payload_size = payload.encoded_size().saturated_into();
Weights::message_dispatch_weight(payload_size)
},
Err(_) => Weight::zero(),
}
}

fn dispatch(
message: DispatchMessage<Self::DispatchPayload>,
) -> MessageDispatchResult<Self::DispatchLevelResult> {
let payload = match message.data.payload {
Ok(payload) => payload,
Err(e) => {
log::error!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}",
e,
message.key.nonce
);
return MessageDispatchResult {
unspent_weight: Weight::zero(),
dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
}
},
};
let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) {
Ok(_) => {
log::debug!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}",
message.key.nonce
);
XcmBlobMessageDispatchResult::Dispatched
},
Err(e) => {
log::error!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}",
e, message.key.nonce
);
XcmBlobMessageDispatchResult::NotDispatched(Some(e))
},
};
MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result }
}
}

/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from
/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`].
pub trait XcmBlobHauler {
/// Runtime message sender adapter.
type MessageSender: MessagesBridge<Self::MessageSenderOrigin, XcmAsPlainPayload>;

/// Runtime message sender origin, which is used by [`Self::MessageSender`].
type MessageSenderOrigin;
/// Our location within the Consensus Universe.
fn message_sender_origin() -> Self::MessageSenderOrigin;

/// Return message lane (as "point-to-point link") used to deliver XCM messages.
fn xcm_lane() -> LaneId;
}

/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`XcmBlobHauler::MessageSender`] and
/// makes sure that XCM blob is sent to the [`pallet_bridge_messages`] queue to be relayed.
pub struct XcmBlobHaulerAdapter<XcmBlobHauler>(sp_std::marker::PhantomData<XcmBlobHauler>);
impl<HaulerOrigin, H: XcmBlobHauler<MessageSenderOrigin = HaulerOrigin>> HaulBlob
for XcmBlobHaulerAdapter<H>
{
fn haul_blob(blob: sp_std::prelude::Vec<u8>) -> Result<(), HaulBlobError> {
let lane = H::xcm_lane();
H::MessageSender::send_message(H::message_sender_origin(), lane, blob)
.map(|artifacts| (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256))
.map(|result| {
log::info!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"haul_blob result - ok: {:?} on lane: {:?}",
result,
lane
)
})
.map_err(|error| {
log::error!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"haul_blob result - error: {:?} on lane: {:?}",
error,
lane
);
HaulBlobError::Transport("MessageSenderError")
})
}
}
424 changes: 424 additions & 0 deletions bridges/bin/runtime-common/src/mock.rs

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions bridges/bin/runtime-common/src/parachains_benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Everything required to run benchmarks of parachains finality module.
#![cfg(feature = "runtime-benchmarks")]

use crate::{
messages_benchmarking::insert_header_to_grandpa_pallet,
messages_generation::grow_trie_leaf_value,
};

use bp_parachains::parachain_head_storage_key_at_source;
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
use bp_runtime::{record_all_trie_keys, StorageProofSize};
use codec::Encode;
use frame_support::traits::Get;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use sp_std::prelude::*;
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};

/// Prepare proof of messages for the `receive_messages_proof` call.
///
/// In addition to returning valid messages proof, environment is prepared to verify this message
/// proof.
pub fn prepare_parachain_heads_proof<R, PI>(
parachains: &[ParaId],
parachain_head_size: u32,
size: StorageProofSize,
) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>)
where
R: pallet_bridge_parachains::Config<PI>
+ pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>,
PI: 'static,
<R as pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>>::BridgedChain:
bp_runtime::Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash>,
{
let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]);

// insert all heads to the trie
let mut parachain_heads = Vec::with_capacity(parachains.len());
let mut storage_keys = Vec::with_capacity(parachains.len());
let mut state_root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie =
TrieDBMutBuilderV1::<RelayBlockHasher>::new(&mut mdb, &mut state_root).build();

// insert parachain heads
for (i, parachain) in parachains.into_iter().enumerate() {
let storage_key =
parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain);
let leaf_data = if i == 0 {
grow_trie_leaf_value(parachain_head.encode(), size)
} else {
parachain_head.encode()
};
trie.insert(&storage_key.0, &leaf_data)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
parachain_heads.push((*parachain, parachain_head.hash()))
}
}

// generate heads storage proof
let proof = record_all_trie_keys::<LayoutV1<RelayBlockHasher>, _>(&mdb, &state_root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");

let (relay_block_number, relay_block_hash) =
insert_header_to_grandpa_pallet::<R, R::BridgesGrandpaPalletInstance>(state_root);

(relay_block_number, relay_block_hash, ParaHeadsProof(proof), parachain_heads)
}
201 changes: 201 additions & 0 deletions bridges/bin/runtime-common/src/priority_calculator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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/>.

//! Bridge transaction priority calculator.
//!
//! We want to prioritize message delivery transactions with more messages over
//! transactions with less messages. That's because we reject delivery transactions
//! if it contains already delivered message. And if some transaction delivers
//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will
//! be rejected. This can lower bridge throughput down to one message per block.
use bp_messages::MessageNonce;
use frame_support::traits::Get;
use sp_runtime::transaction_validity::TransactionPriority;

// reexport everything from `integrity_tests` module
pub use integrity_tests::*;

/// Compute priority boost for message delivery transaction that delivers
/// given number of messages.
pub fn compute_priority_boost<PriorityBoostPerMessage>(
messages: MessageNonce,
) -> TransactionPriority
where
PriorityBoostPerMessage: Get<TransactionPriority>,
{
// we don't want any boost for transaction with single message => minus one
PriorityBoostPerMessage::get().saturating_mul(messages - 1)
}

#[cfg(not(feature = "integrity-test"))]
mod integrity_tests {}

#[cfg(feature = "integrity-test")]
mod integrity_tests {
use super::compute_priority_boost;

use bp_messages::MessageNonce;
use bp_runtime::PreComputedSize;
use frame_support::{
dispatch::{DispatchClass, DispatchInfo, Dispatchable, Pays, PostDispatchInfo},
traits::Get,
};
use pallet_bridge_messages::WeightInfoExt;
use pallet_transaction_payment::OnChargeTransaction;
use sp_runtime::{
traits::{UniqueSaturatedInto, Zero},
transaction_validity::TransactionPriority,
FixedPointOperand, SaturatedConversion, Saturating,
};

type BalanceOf<T> =
<<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<
T,
>>::Balance;

/// Ensures that the value of `PriorityBoostPerMessage` matches the value of
/// `tip_boost_per_message`.
///
/// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have almost
/// the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want to be sure
/// that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the priority will be close
/// to `TX2` as well.
pub fn ensure_priority_boost_is_sane<Runtime, MessagesInstance, PriorityBoostPerMessage>(
tip_boost_per_message: BalanceOf<Runtime>,
) where
Runtime:
pallet_transaction_payment::Config + pallet_bridge_messages::Config<MessagesInstance>,
MessagesInstance: 'static,
PriorityBoostPerMessage: Get<TransactionPriority>,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
let priority_boost_per_message = PriorityBoostPerMessage::get();
let maximal_messages_in_delivery_transaction =
Runtime::MaxUnconfirmedMessagesAtInboundLane::get();
for messages in 1..=maximal_messages_in_delivery_transaction {
let base_priority = estimate_message_delivery_transaction_priority::<
Runtime,
MessagesInstance,
>(messages, Zero::zero());
let priority_boost = compute_priority_boost::<PriorityBoostPerMessage>(messages);
let priority_with_boost = base_priority + priority_boost;

let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into());
let priority_with_tip =
estimate_message_delivery_transaction_priority::<Runtime, MessagesInstance>(1, tip);

const ERROR_MARGIN: TransactionPriority = 5; // 5%
if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) /
priority_with_tip >
ERROR_MARGIN
{
panic!(
"The PriorityBoostPerMessage value ({}) must be fixed to: {}",
priority_boost_per_message,
compute_priority_boost_per_message::<Runtime, MessagesInstance>(
tip_boost_per_message
),
);
}
}
}

/// Compute priority boost that we give to message delivery transaction for additional message.
#[cfg(feature = "integrity-test")]
fn compute_priority_boost_per_message<Runtime, MessagesInstance>(
tip_boost_per_message: BalanceOf<Runtime>,
) -> TransactionPriority
where
Runtime:
pallet_transaction_payment::Config + pallet_bridge_messages::Config<MessagesInstance>,
MessagesInstance: 'static,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// esimate priority of transaction that delivers one message and has large tip
let maximal_messages_in_delivery_transaction =
Runtime::MaxUnconfirmedMessagesAtInboundLane::get();
let small_with_tip_priority =
estimate_message_delivery_transaction_priority::<Runtime, MessagesInstance>(
1,
tip_boost_per_message
.saturating_mul(maximal_messages_in_delivery_transaction.saturated_into()),
);
// estimate priority of transaction that delivers maximal number of messages, but has no tip
let large_without_tip_priority = estimate_message_delivery_transaction_priority::<
Runtime,
MessagesInstance,
>(maximal_messages_in_delivery_transaction, Zero::zero());

small_with_tip_priority
.saturating_sub(large_without_tip_priority)
.saturating_div(maximal_messages_in_delivery_transaction - 1)
}

/// Estimate message delivery transaction priority.
#[cfg(feature = "integrity-test")]
fn estimate_message_delivery_transaction_priority<Runtime, MessagesInstance>(
messages: MessageNonce,
tip: BalanceOf<Runtime>,
) -> TransactionPriority
where
Runtime:
pallet_transaction_payment::Config + pallet_bridge_messages::Config<MessagesInstance>,
MessagesInstance: 'static,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// just an estimation of extra transaction bytes that are added to every transaction
// (including signature, signed extensions extra and etc + in our case it includes
// all call arguments extept the proof itself)
let base_tx_size = 512;
// let's say we are relaying similar small messages and for every message we add more trie
// nodes to the proof (x0.5 because we expect some nodes to be reused)
let estimated_message_size = 512;
// let's say all our messages have the same dispatch weight
let estimated_message_dispatch_weight =
Runtime::WeightInfo::message_dispatch_weight(estimated_message_size);
// messages proof argument size is (for every message) messages size + some additional
// trie nodes. Some of them are reused by different messages, so let's take 2/3 of default
// "overhead" constant
let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size()
.saturating_mul(2)
.saturating_div(3)
.saturating_add(estimated_message_size)
.saturating_mul(messages as _);

// finally we are able to estimate transaction size and weight
let transaction_size = base_tx_size.saturating_add(messages_proof_size);
let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight(
&PreComputedSize(transaction_size as _),
messages as _,
estimated_message_dispatch_weight.saturating_mul(messages),
);

pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
&DispatchInfo {
weight: transaction_weight,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
},
transaction_size as _,
tip,
Zero::zero(),
)
}
}
1,730 changes: 1,730 additions & 0 deletions bridges/bin/runtime-common/src/refund_relayer_extension.rs

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions bridges/docs/complex-relay.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Complex Relay</title>
</head>
<body>
<h1>Complex Relay</h1>
<p>
Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required
finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA,
or Bridge Parachains finality pallets, or any combination of those.<br/>
</p>
<p>
There are 4-6 relayer subprocesses inside the Complex Relayer. They include two message relayers,
serving the lane in both directions and 2-4 Complex Relayers (depending on the finality type of Source
and Target Chains).<br/>
</p>
<p>
The following diagram shows the way the complex relayer serves the lane in single direction. Everything
below may be applied to the opposite direction if you'll swap the Source and Target Chains.
</p>
<div class="mermaid">
sequenceDiagram
participant Source Chain
participant Complex Relayer
participant Target Chain

Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42
Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42

Source Chain ->> Source Chain: someone Sends Message 43
Source Chain ->> Source Chain: Import and Finalize Block 481

Source Chain ->> Complex Relayer: notes new outbound message 43 at Source Chain Block 481
Note right of Complex Relayer: can't deliver message 43, Source Chain Block 481 is not relayed
Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 481

Source Chain ->> Complex Relayer: Read Finality Proof of Block 481
Complex Relayer ->> Target Chain: Submit Finality Proof of Block 481
Target Chain ->> Target Chain: Import and Finalize Block 61
Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42

Source Chain ->> Complex Relayer: Read Proof of Message 43 at Block 481
Complex Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481
Target Chain ->> Target Chain: Import and Finalize Block 62
Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] }

Target Chain ->> Complex Relayer: notes new unrewarded relayer at Target Chain Block 62
Note right of Complex Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed
Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Target Chain Block 62

Target Chain ->> Complex Relayer: Read Finality Proof of Block 62
Complex Relayer ->> Source Chain: Submit Finality Proof of Block 62
Source Chain ->> Source Chain: Import and Finalize Block 482
Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42

Target Chain ->> Complex Relayer: Read Proof of Message 43 Delivery at Block 62
Complex Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612
Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43]
Source Chain ->> Source Chain: prune delivered message 43 from runtime storage
Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43

Source Chain ->> Source Chain: someone Sends Message 44
Source Chain ->> Source Chain: Import and Finalize Block 483

Source Chain ->> Complex Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43
Note right of Complex Relayer: can't deliver message 44, Source Chain Block 483 is not relayed
Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 483

Source Chain ->> Complex Relayer: Read Finality Proof of Block 483
Complex Relayer ->> Target Chain: Submit Finality Proof of Block 483
Target Chain ->> Target Chain: Import and Finalize Block 63
Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] }

Source Chain ->> Complex Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483
Complex Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483
Target Chain ->> Target Chain: Import and Finalize Block 64
Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] }-->
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@8.8.4/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad: true})</script>
</body>
</html>
47 changes: 47 additions & 0 deletions bridges/docs/grandpa-finality-relay.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>GRANDPA Finality Relay</title>
</head>
<body>
<h1>GRANDPA Finality Relay</h1>
<p>
Source Chain is running GRANDPA Finality Gadget. Bridge GRANDPA finality pallet is deployed at
Target Chain runtime. Relayer is configured to relay Source Chain finality to Target Chain.
</p>
<div class="mermaid">
sequenceDiagram
participant Source Chain
participant Relayer
participant Target Chain
Note left of Source Chain: Best: 500, Finalized: 480, Authorities Set Index: 42
Note right of Target Chain: Uninitialized

Source Chain ->> Relayer: Read Initialization Data
Relayer ->> Target Chain: Initialize Bridge GRANDPA Finality Pallet
Note right of Target Chain: Finalized: 480, Authorities Set Index: 42

Source Chain ->> Source Chain: Import Block 501
Source Chain ->> Source Chain: Import Block 502
Source Chain ->> Source Chain: Finalize Block 495
Source Chain ->> Relayer: Read Finality Proof of Block 495
Relayer ->> Target Chain: Finality Proof of Block 495
Note right of Target Chain: Finalized: 495, Authorities Set Index: 42

Source Chain ->> Source Chain: Import Block 503 that changes Authorities Set to 43
Source Chain ->> Source Chain: Finalize Block 500
Note left of Relayer: Relayer Misses Finality Notification for Block 500

Source Chain ->> Source Chain: Import Block 504
Source Chain ->> Source Chain: Finalize Mandatory Block 503
Source Chain ->> Source Chain: Finalize Block 504
Source Chain ->> Relayer: Read Finality Proof of Mandatory Block 503
Relayer ->> Target Chain: Finality Proof of Block 503
Note right of Target Chain: Finalized: 503, Authorities Set Index: 43
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@8.8.4/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad: true})</script>
</body>
</html>
181 changes: 181 additions & 0 deletions bridges/docs/high-level-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# High-Level Bridge Documentation

This document gives a brief, abstract description of main components that may be found in this repository.
If you want to see how we're using them to build Rococo <> Wococo (Kusama <> Polkadot) bridge, please
refer to the [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md).

## Purpose

This repo contains all components required to build a trustless connection between standalone Substrate chains,
that are using GRANDPA finality, their parachains or any combination of those. On top of this connection, we
offer a messaging pallet that provides means to organize messages exchange.

On top of that layered infrastructure, anyone may build their own bridge applications - e.g. [XCM messaging](./polkadot-kusama-bridge-overview.md),
[encoded calls messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) and so on.

## Terminology

Even though we support (and require) two-way bridging, the documentation will generally talk about
a one-sided interaction. That's to say, we will only talk about syncing finality proofs 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.

The bridge has both on-chain (pallets) and offchain (relayers) components.

## On-chain components

On-chain bridge components are pallets that are deployed at the chain runtime. Finality pallets require
deployment at the target chain, while messages pallet needs to be deployed at both, source
and target chains.

### Bridge GRANDPA Finality Pallet

A GRANDPA light client of the source chain built into the target chain's runtime. It provides a "source of truth"
about the source chain headers which have been finalized. This is useful for higher level applications.

The pallet tracks current GRANDPA authorities set and only accepts finality proofs (GRANDPA justifications),
generated by the current authorities set. The GRANDPA protocol itself requires current authorities set to
generate explicit justification for the header that enacts next authorities set. Such headers and their finality
proofs are called mandatory in the pallet and relayer pays no fee for such headers submission.

The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers
he wants to submit (with the exception of mandatory headers).

More: [pallet level documentation and code](../modules/grandpa/).

### Bridge Parachains Finality Pallet

Parachains are not supposed to have their own finality, so we can't use bridge GRANDPA pallet to verify their
finality proofs. Instead, they rely on their relay chain finality. The parachain header is considered final,
when it is accepted by the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras)
at its relay chain. Obviously, the relay chain block, where it is accepted, must also be finalized by the relay
chain GRANDPA gadget.

That said, the bridge parachains pallet accepts storage proof of one or several parachain heads, inserted to the
[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642)
map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras).
To verify this storage proof, the pallet uses relay chain header, imported earlier by the bridge GRANDPA pallet.

The pallet may track multiple parachains at once and those parachains may use different primitives. So the
parachain header decoding never happens at the pallet level. For maintaining the headers order, the pallet
uses relay chain header number.

More: [pallet level documentation and code](../modules/parachains/).

### Bridge Messages Pallet

The pallet is responsible for queuing messages at the source chain and receiving the messages proofs at the
target chain. The messages are sent to the particular _lane_, where they are guaranteed to be received in the
same order they are sent. The pallet supports many lanes.

The lane has two ends. Outbound lane end is storing number of messages that have been sent and the number of
messages that have been received. Inbound lane end stores the number of messages that have been received and
also a map that maps messages to relayers that have delivered those messages to the target chain.

The pallet has three main entrypoints:
- the `send_message` may be used by the other runtime pallets to send the messages;
- the `receive_messages_proof` is responsible for parsing the messages proof and handing messages over to the
dispatch code;
- the `receive_messages_delivery_proof` is responsible for parsing the messages delivery proof and rewarding
relayers that have delivered the message.

Many things are abstracted by the pallet:
- the message itself may mean anything, the pallet doesn't care about its content;
- the message dispatch happens during delivery, but it is decoupled from the pallet code;
- the messages proof and messages delivery proof are verified outside of the pallet;
- the relayers incentivization scheme is defined outside of the pallet.

Outside of the messaging pallet, we have a set of adapters, where messages and delivery proofs are regular
storage proofs. The proofs are generated at the bridged chain and require bridged chain finality. So messages
pallet, in this case, depends on one of the finality pallets. The messages are XCM messages and we are using
XCM executor to dispatch them on receival. You may find more info in [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md)
document.

More: [pallet level documentation and code](../modules/messages/).

### Bridge Relayers Pallet

The pallet is quite simple. It just registers relayer rewards and has an entrypoint to collect them. When
the rewards are registered and the reward amount is configured outside of the pallet.

More: [pallet level documentation and code](../modules/relayers/).

## Offchain Components

Offchain bridge components are separate processes, called relayers. Relayers are connected both to the
source chain and target chain nodes. Relayers are reading state of the source chain, compare it to the
state of the target chain and, if state at target chain needs to be updated, submits target chain
transaction.

### GRANDPA Finality Relay

The task of relay is to submit source chain GRANDPA justifications and their corresponding headers to
the Bridge GRANDPA Finality Pallet, deployed at the target chain. For that, the relay subscribes to
the source chain GRANDPA justifications stream and submits every new justification it sees to the
target chain GRANDPA light client. In addition, relay is searching for mandatory headers and
submits their justifications - without that the pallet will be unable to move forward.

More: [GRANDPA Finality Relay Sequence Diagram](./grandpa-finality-relay.html), [pallet level documentation and code](../relays/finality/).

### Parachains Finality Relay

The relay connects to the source _relay_ chain and the target chain nodes. It doesn't need to connect to the
tracked parachain nodes. The relay looks at the [`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642)
map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras)
in source chain, and compares the value with the best parachain head, stored in the bridge parachains pallet at
the target chain. If new parachain head appears at the relay chain block `B`, the relay process **waits**
until header `B` or one of its ancestors appears at the target chain. Once it is available, the storage
proof of the map entry is generated and is submitted to the target chain.

As its on-chain component (which requires bridge GRANDPA pallet to be deployed nearby), the parachains
finality relay requires GRANDPA finality relay to be running in parallel. Without it, the header `B` or
any of its children's finality at source won't be relayed at target, and target chain
won't be able to verify generated storage proof.

More: [Parachains Finality Relay Sequence Diagram](./parachains-finality-relay.html), [code](../relays/parachains/).

### Messages Relay

Messages relay is actually two relays that are running in a single process: messages delivery relay and
delivery confirmation relay. Even though they are more complex and have many caveats, the overall algorithm
is the same as in other relays.

Message delivery relay connects to the source chain and looks at the outbound lane end, waiting until new
messages are queued there. Once they appear at the source block `B`, the relay start waiting for the block
`B` or its descendant appear at the target chain. Then the messages storage proof is generated and submitted
to the bridge messages pallet at the target chain. In addition, the transaction may include the storage proof
of the outbound lane state - that proves that relayer rewards have been paid and this data (map of relay
accounts to the delivered messages) may be pruned from the inbound lane state at the target chain.

Delivery confirmation relay connects to the target chain and starts watching the inbound lane end. When new
messages are delivered to the target chain, the corresponding _source chain account_ is inserted to the
map in the inbound lane data. Relay detects that, say, at the target chain block `B` and waits until that
block or its descendant appears at the source chain. Once that happens, the relay crafts a storage proof of
that data and sends it to the messages pallet, deployed at the source chain.

As you can see, the messages relay also requires finality relay to be operating in parallel. Since messages
relay submits transactions to both source and target chains, it requires both _source-to-target_ and
_target-to-source_ finality relays. They can be GRANDPA finality relays or GRANDPA+parachains finality relays,
depending on the type of connected chain.

More: [Messages Relay Sequence Diagram](./messages-relay.html), [pallet level documentation and code](../relays/messages/).

### Complex Relay

Every relay transaction has its cost. The only transaction, that is "free" to relayer is when the mandatory
GRANDPA header is submitted. The relay that feeds the bridge with every relay chain and/or parachain head it
sees, will have to pay a (quite large) cost. And if no messages are sent through the bridge, that is just
waste of money.

We have a special relay mode, called _complex relay_, where relay mostly sleeps and only submits transactions
that are required for the messages/confirmations delivery. This mode starts two message relays (in both
directions). All required finality relays are also started in a special _on-demand_ mode. In this mode they
do not submit any headers without special request. As always, the only exception is when GRANDPA finality
relay sees the mandatory header - it is submitted without such request.

The message relays are watching their lanes and when, at some block `B`, they see new messages/confirmations
to be delivered, they are asking on-demand relays to relay this block `B`. On-demand relays does that and
then message relay may perform its job. If on-demand relay is a parachain finality relay, it also runs its
own on-demand GRANDPA relay, which is used to relay required relay chain headers.

More: [Complex Relay Sequence Diagram](./complex-relay.html), [code](../relays/bin-substrate/src/cli/relay_headers_and_messages/).
Loading