Merge remote-tracking branch 'origin/master' into gav-xcm-v3

This commit is contained in:
Keith Yeung
2021-12-30 21:40:20 -08:00
198 changed files with 10258 additions and 6371 deletions
+156
View File
@@ -0,0 +1,156 @@
---
name: Release issue template
about: Tracking issue for new releases
title: Cumulus {{ env.VERSION }} Release checklist
---
# Release Checklist
### Runtime Releases
These checks should be performed on the codebase.
- [ ] Verify [`spec_version`](#spec-version) has been incremented since the
last release for any native runtimes from any existing use on public
(non-private/test) networks.
- [ ] Verify previously [completed migrations](#old-migrations-removed) are
removed for any public (non-private/test) networks.
- No migrations added in the last release that would need to be removed.
- [ ] Verify pallet and [extrinsic ordering](#extrinsic-ordering) as well as `SignedExtension`s have stayed
the same. Bump `transaction_version` if not.
- [ ] Verify new extrinsics have been correctly whitelisted/blacklisted for
[proxy filters](#proxy-filtering).
- No new extrinsics.
- [ ] Verify [benchmarks](#benchmarks) have been updated for any modified
runtime logic.
- [ ] Verify the weights are up-to-date.
- [ ] Verify that the various pieces of XCM config are sane.
The following checks can be performed after we have forked off to the release-
candidate branch or started an additional release candidate branch (rc-2, rc-3, etc)
- [ ] Verify [new migrations](#new-migrations) complete successfully, and the
runtime state is correctly updated for any public (non-private/test)
networks.
- [ ] Run integration tests.
- [ ] Teleport Relay -> Statemin* and back.
- [ ] Create asset (if applicable), mint and transfer
- [ ] Push runtime upgrade to Westmint and verify network stability.
### All Releases
- [ ] Check that the new polkadot-collator versions have [run on the network](#burn-in)
without issue.
- [ ] Check that a draft release has been created at
https://github.com/paritytech/cumulus/releases with relevant [release
notes](#release-notes).
- [ ] Check that [build artifacts](#build-artifacts) have been added to the
draft-release.
---
## Notes
### Burn In
Ensure that Parity DevOps has run the new release on Westmint and Statemine collators for 12h prior to publishing the release.
### Build Artifacts
Add any necessary assets to the release. They should include:
- Linux binary
- GPG signature of the Linux binary
- SHA256 of binary
- Source code
- Wasm binaries of any runtimes
### Release notes
The release notes should list:
- The priority of the release (i.e., how quickly users should upgrade) - this is
based on the max priority of any *client* changes.
- Which native runtimes and their versions are included
- The proposal hashes of the runtimes as built with
[srtool](https://github.com/paritytech/srtool)
- Any changes in this release that are still awaiting audit
The release notes may also list:
- Free text at the beginning of the notes mentioning anything important
regarding this release
- Notable changes separated into sections.
### Spec Version
A runtime upgrade must bump the spec number. This may follow a pattern with the
client release (e.g. runtime v12 corresponds to v0.8.12, even if the current
runtime is not v11).
### Runtime version bump between RCs
The clients need to be aware of runtime changes. However, we do not want to bump the
`spec_version` for every single release candidate. Instead, we can bump the `impl` field of the version
to signal the change to the client.
### Old Migrations Removed
Previous `on_runtime_upgrade` functions from old upgrades should be removed.
### New Migrations
Ensure that any migrations that are required due to storage or logic changes
are included in the `on_runtime_upgrade` function of the appropriate pallets.
### Extrinsic Ordering & Storage
Offline signing libraries depend on a consistent ordering of call indices and
functions. Compare the metadata of the current and new runtimes and ensure that
the `module index, call index` tuples map to the same set of functions. It also checks if there have been any changes in `storage`. In case of a breaking change, increase `transaction_version`.
To verify the order has not changed, manually start the following [Github Action](https://github.com/paritytech/cumulus/actions/workflows/extrinsic-ordering-check-from-bin.yml). It takes around a minute to run and will produce the report as artifact you need to manually check.
To run it, in the _Run Workflow_ dropdown:
1. **Use workflow from**: to ignore, leave `master` as default
2. **The WebSocket url of the reference node**:
- Statemine: `wss://kusama-statemine-rpc.paritytech.net`
- Westmint: `wss://westmint-rpc.polkadot.io`
3. **A url to a Linux binary for the node containing the runtime to test**: Paste the URL of the latest release-candidate binary from the draft-release on Github. The binary has to previously be uploaded to S3 (Github url link to the binary is constantly changing)
- https://releases.parity.io/cumulus/statemine-v6.0.0-rc1/polkadot-collator
4. **The name of the chain under test. Usually, you would pass a local chain**:
- Statemine: `statemine-local`
- Westmint: `westmint-local`
5. Click **Run workflow**
When the workflow is done, click on it and download the zip artifact, inside you'll find an `output.txt` file. The things to look for in the output are lines like:
- `[Identity] idx 28 -> 25 (calls 15)` - indicates the index for Identity has changed
- `[+] Society, Recovery` - indicates the new version includes 2 additional modules/pallets.
- If no indices have changed, every modules line should look something like `[Identity] idx 25 (calls 15)`
**Note**: Adding new functions to the runtime does not constitute a breaking change
as long as the indexes did not change.
**Note**: Extrinsic function signatures changes (adding/removing & ordering arguments) are not caught by the job, so those changes should be reviewed "manually"
### Proxy Filtering
The runtime contains proxy filters that map proxy types to allowable calls. If
the new runtime contains any new calls, verify that the proxy filters are up to
date to include them.
### Benchmarks
Until #631 is done, running the benchmarks is a manual process:
1. Connect to the bechmarking machine
2. Make sure no one else is using the machine with `htop check`
3. Pull in the branch of Cumulus that has the version of Statemine you want to release
4. Recompile `cargo build --release --features runtime-benchmarks`
5. From the root directory run `nohup ./scripts/benchmarks.sh &` (it will take quite a few hours)
6. Checkout in your local machine to the branch of cumulus that has the version of Statemine you want to release
7. `scp` from the host to your local machine the weights for Statemine, Westmint and Statemint you'll find in:
- `/polkadot-parachains/statemine/src/weights`
- `/polkadot-parachains/westmint/src/weights`
- `/polkadot-parachains/statemint/src/weights`
8. Commit the changes in your local and create a PR
+23
View File
@@ -0,0 +1,23 @@
name: Check labels
on:
pull_request:
types: [labeled, opened, synchronize, unlabeled]
jobs:
check-labels:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check labels
run: bash ${{ github.workspace }}/scripts/github/check_labels.sh
env:
GITHUB_PR: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
@@ -0,0 +1,63 @@
name: Release-candidate automation
on:
push:
branches:
- release-**v[0-9]+.[0-9]+.[0-9]+
workflow_dispatch:
jobs:
tag_rc:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
with:
fetch-depth: 0
- id: compute_tag
name: Compute next rc tag
shell: bash
run: |
# Get last rc tag if exists, else set it to {version}-rc1
version=${GITHUB_REF#refs/heads/release-}
echo "$version"
echo "::set-output name=version::$version"
git tag -l
last_rc=$(git tag -l "$version-rc*" | sort -V | tail -n 1)
if [ -n "$last_rc" ]; then
suffix=$(echo "$last_rc" | grep -Eo '[0-9]+$')
echo $suffix
((suffix++))
echo $suffix
echo "::set-output name=new_tag::$version-rc$suffix"
echo "::set-output name=first_rc::false"
else
echo "::set-output name=new_tag::$version-rc1"
echo "::set-output name=first_rc::true"
fi
- name: Apply new tag
uses: tvdias/github-tagger@v0.0.2
with:
# We can't use the normal GITHUB_TOKEN for the following reason:
# https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token
# RELEASE_BRANCH_TOKEN requires public_repo OAuth scope
repo-token: "${{ secrets.RELEASE_BRANCH_TOKEN }}"
tag: ${{ steps.compute_tag.outputs.new_tag }}
- id: create-issue
uses: JasonEtco/create-an-issue@v2
# Only create the issue if it's the first release candidate
if: steps.compute_tag.outputs.first_rc == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.compute_tag.outputs.version }}
with:
filename: .github/ISSUE_TEMPLATE/release.md
- name: Send Matrix message
uses: s3krit/matrix-message-action@v0.0.3
if: steps.create-issue.outputs.url != ''
with:
room_id: ${{ secrets.INTERNAL_CUMULUS_MATRIX_ROOM_ID }}
access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }}
server: "matrix.parity.io"
message: |
Release process for Cumulus ${{ steps.compute_tag.outputs.version }} has been started.
Tracking issue: ${{ steps.create-issue.outputs.url }}"
@@ -0,0 +1,221 @@
name: Release - Create draft
on:
workflow_dispatch:
inputs:
ref1:
description: The 'from' tag to use for the diff
default: statemine-v5.0.0
required: true
ref2:
description: The 'to' tag to use for the diff
default: release-statemine-v6
required: true
pre_release:
description: For pre-releases
default: "true"
required: true
jobs:
get-rust-versions:
runs-on: ubuntu-latest
container:
image: paritytech/ci-linux:production
outputs:
rustc-stable: ${{ steps.get-rust-versions.outputs.stable }}
rustc-nightly: ${{ steps.get-rust-versions.outputs.nightly }}
steps:
- id: get-rust-versions
run: |
echo "::set-output name=stable::$(rustc +stable --version)"
echo "::set-output name=nightly::$(rustc +nightly --version)"
build-runtimes:
runs-on: ubuntu-latest
strategy:
matrix:
runtime: ["shell", "statemine", "statemint", "westmint", "rococo-parachain"]
steps:
- name: Checkout sources
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref2 }}
- name: Cache target dir
uses: actions/cache@v2
with:
path: "${{ github.workspace }}/runtime/${{ matrix.runtime }}/target"
key: srtool-target-${{ matrix.runtime }}-${{ github.sha }}
restore-keys: |
srtool-target-${{ matrix.runtime }}-
srtool-target-
- name: Build ${{ matrix.runtime }} runtime
id: srtool_build
uses: chevdor/srtool-actions@v0.3.0
with:
image: paritytech/srtool
chain: ${{ matrix.runtime }}
runtime_dir: polkadot-parachains/${{ matrix.runtime }}
- name: Store srtool digest to disk
run: |
echo '${{ steps.srtool_build.outputs.json }}' | \
jq > ${{ matrix.runtime }}_srtool_output.json
- name: Upload ${{ matrix.runtime }} srtool json
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.runtime }}-srtool-json
path: ${{ matrix.runtime }}_srtool_output.json
- name: Upload ${{ matrix.runtime }} runtime
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.runtime }}-runtime
path: |
${{ steps.srtool_build.outputs.wasm_compressed }}
publish-draft-release:
runs-on: ubuntu-latest
needs: ["get-rust-versions", "build-runtimes"]
outputs:
release_url: ${{ steps.create-release.outputs.html_url }}
asset_upload_url: ${{ steps.create-release.outputs.upload_url }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
with:
fetch-depth: 0
path: cumulus
ref: ${{ github.event.inputs.ref2 }}
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0.0
- name: Download srtool json output
uses: actions/download-artifact@v2
- name: Prepare tooling
run: |
cd cumulus/scripts/changelog
gem install bundler changelogerator
bundle install
changelogerator --help
URL=https://github.com/chevdor/tera-cli/releases/download/v0.2.1/tera-cli_linux_amd64.deb
wget $URL -O tera.deb
sudo dpkg -i tera.deb
tera --version
- name: Generate release notes
env:
RUSTC_STABLE: ${{ needs.get-rust-versions.outputs.rustc-stable }}
RUSTC_NIGHTLY: ${{ needs.get-rust-versions.outputs.rustc-nightly }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NO_CACHE: 1
DEBUG: 1
SHELL_DIGEST: ${{ github.workspace}}/shell-srtool-json/shell_srtool_output.json
WESTMINT_DIGEST: ${{ github.workspace}}/westmint-srtool-json/westmint_srtool_output.json
STATEMINE_DIGEST: ${{ github.workspace}}/statemine-srtool-json/statemine_srtool_output.json
STATEMINT_DIGEST: ${{ github.workspace}}/statemint-srtool-json/statemint_srtool_output.json
ROCOCO_DIGEST: ${{ github.workspace}}/rococo-parachain-srtool-json/rococo-parachain_srtool_output.json
REF1: ${{ github.event.inputs.ref1 }}
REF2: ${{ github.event.inputs.ref2 }}
PRE_RELEASE: ${{ github.event.inputs.pre_release }}
HIDE_SRTOOL_ROCOCO: false
HIDE_SRTOOL_SHELL: false
run: |
find ${{env.GITHUB_WORKSPACE}} -type f -name "*_srtool_output.json"
ls -al $SHELL_DIGEST
ls -al $WESTMINT_DIGEST
ls -al $STATEMINE_DIGEST
ls -al $STATEMINT_DIGEST
ls -al $ROCOCO_DIGEST
echo "The diff will be computed from $REF1 to $REF2"
cd cumulus/scripts/changelog
./bin/changelog $REF1 $REF2 release-notes.md
ls -al release-notes.md
ls -al context.json
- name: Archive artifact context.json
uses: actions/upload-artifact@v2
with:
name: release-notes-context
path: |
context.json
**/*_srtool_output.json
- name: Create draft release
id: create-release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: parachains-${{ github.ref }}
release_name: Parachains ${{ github.ref }}
body_path: ./cumulus/scripts/changelog/release-notes.md
draft: true
publish-runtimes:
runs-on: ubuntu-latest
needs: ["publish-draft-release"]
env:
RUNTIME_DIR: polkadot-parachains
strategy:
matrix:
runtime: ["shell", "statemine", "statemint", "westmint", "rococo-parachain"]
steps:
- name: Checkout sources
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref2 }}
- name: Download artifacts
uses: actions/download-artifact@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0.0
- name: Get runtime version for ${{ matrix.runtime }}
id: get-runtime-ver
run: |
echo "require './scripts/github/runtime-version.rb'" > script.rb
echo "puts get_runtime(runtime: \"${{ matrix.runtime }}\", runtime_dir: \"$RUNTIME_DIR\")" >> script.rb
echo "Current folder: $PWD"
ls "$RUNTIME_DIR/${{ matrix.runtime }}"
runtime_ver=$(ruby script.rb)
echo "Found version: >$runtime_ver<"
echo "::set-output name=runtime_ver::$runtime_ver"
- name: Upload compressed ${{ matrix.runtime }} wasm
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.publish-draft-release.outputs.asset_upload_url }}
asset_path: "${{ matrix.runtime }}-runtime/${{ matrix.runtime }}_runtime.compact.compressed.wasm"
asset_name: ${{ matrix.runtime }}_runtime-v${{ steps.get-runtime-ver.outputs.runtime_ver }}.compact.compressed.wasm
asset_content_type: application/wasm
post_to_matrix:
runs-on: ubuntu-latest
needs: publish-draft-release
steps:
- name: Internal polkadot channel
uses: s3krit/matrix-message-action@v0.0.3
with:
room_id: ${{ secrets.INTERNAL_CUMULUS_MATRIX_ROOM_ID }}
access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }}
message: |
**New draft for ${{ github.repository }}**: ${{ github.ref }}<br/>
Draft release created: [draft](${{ needs.publish-draft-release.outputs.release_url }})
NOTE: The link above will no longer be valid if the draft is edited. You can then use the following link:
[${{ github.server_url }}/${{ github.repository }}/releases](${{ github.server_url }}/${{ github.repository }}/releases)
server: "matrix.parity.io"
+121
View File
@@ -0,0 +1,121 @@
name: Release - Docker
# This workflow listens to pubished releases.
# It includes releases and pre-releases.
# It fetches the binaries, checks sha256 and GPG
# signatures, then builds an injected docker
# image and publishes it.
on:
release:
types:
- published
jobs:
docker_build_publish:
env:
BINARY: polkadot-collator
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
with:
ref: ${{ github.event.release.tag_name }}
- name: Fetch files from release
run: |
echo Repo: ${{ github.event.repository.full_name }}
echo Name: ${{ github.event.release.name }}
echo Tag: ${{ github.event.release.tag_name }}
echo Draft: ${{ github.event.release.draft }}
echo Prerelease: ${{ github.event.release.prerelease }}
echo Assets: ${{ github.event.release.assets }}
for f in $BINARY $BINARY.asc $BINARY.sha256; do
URL="https://github.com/${{ github.event.repository.full_name }}/releases/download/${{ github.event.release.tag_name }}/$f"
echo " - Fetching $f from $URL"
wget $URL -O $f
done
chmod a+x $BINARY
ls -al
- name: Check files
run: |
ls -al *collator*
shasum -a 256 -c $BINARY.sha256
sha_result=$?
KEY_PARITY_SEC=9D4B2B6EB8F97156D19669A9FF0812D491B96798
KEY_CHEVDOR=2835EAF92072BC01D188AF2C4A092B93E97CE1E2
gpg --receive-keys $KEY_PARITY_SEC
if [[ ${{ github.event.release.prerelease }} == "true" ]]; then
gpg --receive-keys $KEY_CHEVDOR
fi
gpg --verify $BINARY.asc
gpg_result=$?
echo sha_result: $sha_result
echo gpg_result: $gpg_result
# If it fails, it would fail earlier but a second check
# does not hurt in case of refactoring...
if [[ $sha_result -ne 0 || $gpg_result -ne 0 ]]; then
echo "Check failed, exiting with error"
exit 1
else
echo "Checks passed"
fi
- name: Build injected image
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
run: |
export OWNER=$DOCKERHUB_USERNAME
mkdir -p target/release
cp -f $BINARY* target/release/
./docker/scripts/build-injected-image.sh
- name: Login to Dockerhub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Tag and Publish
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
run: |
docker run --pull never --rm $DOCKERHUB_USERNAME/$BINARY --version
VERSION=$(docker run --pull never --rm $DOCKERHUB_USERNAME/$BINARY --version | awk '{ print $2 }' )
SEMVER=$( echo $VERSION | cut -f1 -d- )
GITREF=$( echo $VERSION | cut -f2 -d- )
PRE=${{ github.event.release.prerelease }}
PRE_STR=""
echo "SEMVER=$SEMVER"
echo "GITREF=$GITREF"
echo "PRE=$PRE"
# Build a tag such as:
# 1.2.3-8a1201273 or
# 1.2.3-pre-8a1201273 for pre-releases
[[ $PRE == "true" ]] && PRE_STR="-pre"
TAG=${SEMVER}${PRE_STR}-${GITREF}
echo "PRE_STR=$PRE_STR"
echo "TAG=$TAG"
docker tag $DOCKERHUB_USERNAME/$BINARY $DOCKERHUB_USERNAME/$BINARY:$TAG
docker push $DOCKERHUB_USERNAME/$BINARY:$TAG
if [[ $PRE != "true" ]]; then
docker tag $DOCKERHUB_USERNAME/$BINARY $DOCKERHUB_USERNAME/$BINARY:latest
docker tag $DOCKERHUB_USERNAME/$BINARY $DOCKERHUB_USERNAME/$BINARY:$SEMVER
docker push $DOCKERHUB_USERNAME/$BINARY:latest
docker push $DOCKERHUB_USERNAME/$BINARY:$SEMVER
fi
docker images | grep $DOCKERHUB_USERNAME/$BINARY
@@ -1,4 +1,4 @@
name: Pushes release notes to a Matrix room
name: Release - Pushes release notes to a Matrix room
on:
release:
types:
+6 -2
View File
@@ -1,7 +1,7 @@
name: Srtool build
env:
SUBWASM_VERSION: 0.14.1
SUBWASM_VERSION: 0.15.0
on:
push:
@@ -14,6 +14,10 @@ on:
- "scripts"
- "test"
branches:
- "release*"
- "master"
schedule:
- cron: "00 02 * * 1" # 2AM weekly on monday
@@ -24,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
chain: ["statemine", "westmint", "statemint", "rococo", "shell"]
chain: ["statemine", "westmint", "statemint", "rococo-parachain", "shell"]
steps:
- uses: actions/checkout@v2
with:
+23
View File
@@ -107,6 +107,29 @@ test-linux-stable:
fi
- sccache -s
check-runtime-benchmarks:
stage: test
<<: *docker-env
script:
# Check that the node will compile with `runtime-benchmarks` feature flag.
- time cargo check --all --features runtime-benchmarks
- sccache -s
cargo-check-try-runtime:
stage: test
<<: *docker-env
script:
# Check that the node will compile with `try-runtime` feature flag.
- time cargo check --all --features try-runtime
- sccache -s
cargo-check-benches:
stage: test
<<: *docker-env
script:
- time cargo check --all --benches
- sccache -s
#### stage: publish
publish-s3:
+1
View File
@@ -1,4 +1,5 @@
# Basic
edition = "2021"
hard_tabs = true
max_width = 100
use_small_heuristics = "Max"
+27
View File
@@ -0,0 +1,27 @@
# Lists some code owners.
#
# A codeowner just oversees some part of the codebase. If an owned file is changed then the
# corresponding codeowner receives a review request. An approval of the codeowner might be
# required for merging a PR (depends on repository settings).
#
# For details about syntax, see:
# https://help.github.com/en/articles/about-code-owners
# But here are some important notes:
#
# - Glob syntax is git-like, e.g. `/core` means the core directory in the root, unlike `core`
# which can be everywhere.
# - Multiple owners are supported.
# - Either handle (e.g, @github_user or @github/team) or email can be used. Keep in mind,
# that handles might work better because they are more recognizable on GitHub,
# eyou can use them for mentioning unlike an email.
# - The latest matching rule, if multiple, takes precedence.
# CI
/.github/ @paritytech/ci @chevdor
/.gitlab-ci.yml @paritytech/ci
/scripts/ci/ @paritytech/ci
/scripts/github/ @paritytech/ci @chevdor
/scripts/extrinsic-ordering-filter.sh @paritytech/ci @chevdor
# CHANGELOG
/scripts/changelog/ @chevdor
Generated
+1927 -1738
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -7,7 +7,7 @@ members = [
"client/network",
"client/pov-recovery",
"client/service",
"pallets/asset-tx-payment",
"client/relay-chain-interface",
"pallets/aura-ext",
"pallets/collator-selection",
"pallets/dmp-queue",
@@ -25,7 +25,7 @@ members = [
"polkadot-parachains/",
"polkadot-parachains/pallets/parachain-info",
"polkadot-parachains/pallets/ping",
"polkadot-parachains/rococo",
"polkadot-parachains/rococo-parachain",
"polkadot-parachains/shell",
"polkadot-parachains/parachains-common",
"polkadot-parachains/statemint",
@@ -35,7 +35,6 @@ members = [
"test/relay-sproof-builder",
"test/relay-validation-worker-provider",
"test/runtime",
"test/runtime-upgrade",
"test/service",
]
+20 -22
View File
@@ -41,13 +41,13 @@ Statemint is a common good parachain providing an asset store for the Polkadot e
To run a Statemine or Westmint node (Statemint is not deployed, yet) you will need to compile the
`polkadot-collator` binary:
```sh
```bash
cargo build --release --locked -p polkadot-collator
```
Once the executable is built, launch the parachain node via:
```sh
```bash
CHAIN=westmint # or statemine
./target/release/polkadot-collator --chain $CHAIN
```
@@ -56,15 +56,10 @@ Refer to the [setup instructions below](#local-setup) to run a local network for
# Rococo :crown:
[Rococo](https://polkadot.js.org/apps/?rpc=wss://rococo-rpc.polkadot.io) is the testnet for
parachains. It currently runs the parachains
[Tick](https://polkadot.js.org/apps/?rpc=wss://tick-rpc.polkadot.io),
[Trick](https://polkadot.js.org/apps/?rpc=wss://trick-rpc.polkadot.io) and
[Track](https://polkadot.js.org/apps/?rpc=wss://track-rpc.polkadot.io).
[Rococo](https://polkadot.js.org/apps/?rpc=wss://rococo-rpc.polkadot.io) is becoming a [Community Parachain Testbed](https://polkadot.network/blog/rococo-revamp-becoming-a-community-parachain-testbed/) for parachain teams in the Polkadot ecosystem. It supports multiple parachains with the differentiation of long-term connections and recurring short-term connections, to see which parachains are currently connected and how long they will be connected for [see here](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-rpc.polkadot.io#/parachains).
Rococo is an elaborate style of design and the name describes the painstaking effort that has gone
into this project. Tick, Trick and Track are the German names for the cartoon ducks known to English
speakers as Huey, Dewey and Louie.
into this project.
## Build & Launch Rococo Collators
@@ -73,7 +68,7 @@ eventually be included by the relay chain for a parachain.
To run a Rococo collator you will need to compile the following binary:
```
```bash
cargo build --release --locked -p polkadot-collator
```
@@ -93,18 +88,13 @@ If you want to reproduce other steps of CI process you can use the following
Once the executable is built, launch collators for each parachain (repeat once each for chain
`tick`, `trick`, `track`):
```
```bash
./target/release/polkadot-collator --chain $CHAIN --validator
```
## Parachains
The parachains of Rococo all use the same runtime code. The only difference between them is the
parachain ID used for registration with the relay chain:
- Tick: 100
- Trick: 110
- Track: 120
- [Canvas - WASM Smart Contract](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-canvas-rpc.polkadot.io#/explorer)
The network uses horizontal message passing (HRMP) to enable communication between parachains and
the relay chain and, in turn, between parachains. This means that every message is sent to the relay
@@ -154,19 +144,27 @@ cargo build --release
# Parachain Full Node 1
./target/release/polkadot-collator --tmp --parachain-id <parachain_id_u32_type_range> --port 40337 --ws-port 9948 -- --execution wasm --chain ../polkadot/rococo-local-cfde.json --port 30337
```
### Register the parachain
![image](https://user-images.githubusercontent.com/2915325/99548884-1be13580-2987-11eb-9a8b-20be658d34f9.png)
## Build the docker image
## Containerize
After building `polkadot-collator` with cargo or with Parity docker image as documented in [this chapter](#build--launch-rococo-collators), the following will allow producting a new docker image where the compiled binary is injected:
After building `polkadot-collator` with cargo or with Parity CI image as documented in [this chapter](#build--launch-rococo-collators),
the following will allow producing a new docker image where the compiled binary is injected:
```
```bash
./docker/scripts/build-injected-image.sh
```
You may then start a new contaier:
Alternatively, you can build an image with a builder pattern:
```
```bash
docker build --tag $OWNER/$IMAGE_NAME --file ./docker/polkadot-collator_builder.Containerfile .
You may then run your new container:
```bash
docker run --rm -it $OWNER/$IMAGE_NAME --collator --tmp --parachain-id 1000 --execution wasm --chain /specs/westmint.json
```
+1 -1
View File
@@ -2,7 +2,7 @@
name = "cumulus-client-cli"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
structopt = "0.3.3"
+5 -10
View File
@@ -30,7 +30,7 @@ use std::{
};
use structopt::StructOpt;
/// The `purge-chain` command used to remove the whole chain: the parachain and the relaychain.
/// The `purge-chain` command used to remove the whole chain: the parachain and the relay chain.
#[derive(Debug, StructOpt)]
pub struct PurgeChainCmd {
/// The base struct of the purge-chain command.
@@ -125,10 +125,6 @@ pub struct RunCmd {
#[structopt(flatten)]
pub base: sc_cli::RunCmd,
/// Id of the parachain this collator collates for.
#[structopt(long)]
pub parachain_id: Option<u32>,
/// Run node as collator.
///
/// Note that this is the same as running with `--validator`.
@@ -137,13 +133,11 @@ pub struct RunCmd {
}
/// A non-redundant version of the `RunCmd` that sets the `validator` field when the
/// original `RunCmd` had the `colaltor` field.
/// original `RunCmd` had the `collator` field.
/// This is how we make `--collator` imply `--validator`.
pub struct NormalizedRunCmd {
/// The cumulus RunCmd inherents from sc_cli's
pub base: sc_cli::RunCmd,
/// Id of the parachain this collator collates for.
pub parachain_id: Option<u32>,
}
impl RunCmd {
@@ -153,7 +147,7 @@ impl RunCmd {
new_base.validator = self.base.validator || self.collator;
NormalizedRunCmd { base: new_base, parachain_id: self.parachain_id }
NormalizedRunCmd { base: new_base }
}
}
@@ -204,8 +198,9 @@ impl sc_cli::CliConfiguration for NormalizedRunCmd {
fn prometheus_config(
&self,
default_listen_port: u16,
chain_spec: &Box<dyn sc_cli::ChainSpec>,
) -> sc_cli::Result<Option<PrometheusConfig>> {
self.base.prometheus_config(default_listen_port)
self.base.prometheus_config(default_listen_port, chain_spec)
}
fn disable_grandpa(&self) -> sc_cli::Result<bool> {
+2 -1
View File
@@ -2,7 +2,7 @@
name = "cumulus-client-collator"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Substrate dependencies
@@ -22,6 +22,7 @@ polkadot-node-subsystem = { git = "https://github.com/paritytech/polkadot", bran
cumulus-client-network = { path = "../network" }
cumulus-client-consensus-common = { path = "../consensus/common" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-relay-chain-interface = { path = "../relay-chain-interface" }
# Other dependencies
codec = { package = "parity-scale-codec", version = "2.3.0", features = [ "derive" ] }
+7 -5
View File
@@ -17,7 +17,9 @@
//! Cumulus Collator implementation for Substrate.
use cumulus_client_network::WaitToAnnounce;
use cumulus_primitives_core::{CollectCollationInfo, ParachainBlockData, PersistedValidationData};
use cumulus_primitives_core::{
relay_chain::Hash as PHash, CollectCollationInfo, ParachainBlockData, PersistedValidationData,
};
use sc_client_api::BlockBackend;
use sp_api::ProvideRuntimeApi;
@@ -34,7 +36,7 @@ use polkadot_node_primitives::{
};
use polkadot_node_subsystem::messages::{CollationGenerationMessage, CollatorProtocolMessage};
use polkadot_overseer::Handle as OverseerHandle;
use polkadot_primitives::v1::{CollatorPair, Hash as PHash, HeadData, Id as ParaId};
use polkadot_primitives::v1::{CollatorPair, HeadData, Id as ParaId};
use codec::{Decode, Encode};
use futures::{channel::oneshot, FutureExt};
@@ -390,7 +392,7 @@ mod tests {
.build()
.expect("Builds overseer");
spawner.spawn("overseer", overseer.run().then(|_| async { () }).boxed());
spawner.spawn("overseer", None, overseer.run().then(|_| async { () }).boxed());
let collator_start = start_collator(StartCollatorParams {
runtime_api: client.clone(),
@@ -428,8 +430,8 @@ mod tests {
assert_eq!(1, *block.header().number());
// Ensure that we did not include `:code` in the proof.
let db = block
.storage_proof()
let proof = block.storage_proof();
let db = proof
.to_storage_proof::<BlakeTwo256>(Some(header.state_root()))
.unwrap()
.0
+1 -4
View File
@@ -3,7 +3,7 @@ name = "cumulus-client-consensus-aura"
description = "AURA consensus algorithm for parachains"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Substrate dependencies
@@ -24,9 +24,6 @@ sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "mast
sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot dependencies
polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# Cumulus dependencies
cumulus-client-consensus-common = { path = "../common" }
cumulus-primitives-core = { path = "../../../primitives/core" }
+3 -5
View File
@@ -24,12 +24,12 @@ use sc_consensus_slots::InherentDataProviderExt;
use sc_telemetry::TelemetryHandle;
use sp_api::{ApiExt, ProvideRuntimeApi};
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_blockchain::{HeaderBackend, ProvideCache};
use sp_blockchain::HeaderBackend;
use sp_consensus::{CanAuthorWith, Error as ConsensusError};
use sp_consensus_aura::{digests::CompatibleDigestItem, AuraApi};
use sp_consensus_aura::AuraApi;
use sp_core::crypto::Pair;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::traits::{Block as BlockT, DigestItemFor};
use sp_runtime::traits::Block as BlockT;
use std::{fmt::Debug, hash::Hash, sync::Arc};
use substrate_prometheus_endpoint::Registry;
@@ -69,7 +69,6 @@ where
C: 'static
+ ProvideRuntimeApi<Block>
+ BlockOf
+ ProvideCache<Block>
+ Send
+ Sync
+ AuxStore
@@ -79,7 +78,6 @@ where
+ Send
+ Sync
+ 'static,
DigestItemFor<Block>: CompatibleDigestItem<P::Signature>,
P: Pair + Send + Sync + 'static,
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Codec,
P::Signature: Codec,
+34 -304
View File
@@ -26,19 +26,16 @@ use codec::{Decode, Encode};
use cumulus_client_consensus_common::{
ParachainBlockImport, ParachainCandidate, ParachainConsensus,
};
use cumulus_primitives_core::{
relay_chain::v1::{Block as PBlock, Hash as PHash, ParachainHost},
PersistedValidationData,
};
use cumulus_primitives_core::{relay_chain::v1::Hash as PHash, PersistedValidationData};
use futures::lock::Mutex;
use polkadot_client::ClientHandle;
use sc_client_api::{backend::AuxStore, Backend, BlockOf};
use sc_client_api::{backend::AuxStore, BlockOf};
use sc_consensus::BlockImport;
use sc_consensus_slots::{BackoffAuthoringBlocksStrategy, SlotInfo};
use sc_telemetry::TelemetryHandle;
use sp_api::ProvideRuntimeApi;
use sp_application_crypto::AppPublic;
use sp_blockchain::{HeaderBackend, ProvideCache};
use sp_blockchain::HeaderBackend;
use sp_consensus::{
EnableProofRecording, Environment, ProofRecording, Proposer, SlotData, SyncOracle,
};
@@ -46,8 +43,8 @@ use sp_consensus_aura::AuraApi;
use sp_core::crypto::Pair;
use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
use sp_keystore::SyncCryptoStorePtr;
use sp_runtime::traits::{Block as BlockT, HashFor, Header as HeaderT, Member, NumberFor};
use std::{convert::TryFrom, hash::Hash, marker::PhantomData, sync::Arc};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, NumberFor};
use std::{convert::TryFrom, hash::Hash, sync::Arc};
mod import_queue;
@@ -60,10 +57,8 @@ pub use sc_consensus_slots::InherentDataProviderExt;
const LOG_TARGET: &str = "aura::cumulus";
/// The implementation of the AURA consensus for parachains.
pub struct AuraConsensus<B, RClient, RBackend, CIDP> {
pub struct AuraConsensus<B, CIDP> {
create_inherent_data_providers: Arc<CIDP>,
relay_chain_client: Arc<RClient>,
relay_chain_backend: Arc<RBackend>,
aura_worker: Arc<
Mutex<
dyn sc_consensus_slots::SlotWorker<B, <EnableProofRecording as ProofRecording>::Proof>
@@ -74,53 +69,42 @@ pub struct AuraConsensus<B, RClient, RBackend, CIDP> {
slot_duration: SlotDuration,
}
impl<B, RClient, RBackend, CIDP> Clone for AuraConsensus<B, RClient, RBackend, CIDP> {
impl<B, CIDP> Clone for AuraConsensus<B, CIDP> {
fn clone(&self) -> Self {
Self {
create_inherent_data_providers: self.create_inherent_data_providers.clone(),
relay_chain_backend: self.relay_chain_backend.clone(),
relay_chain_client: self.relay_chain_client.clone(),
aura_worker: self.aura_worker.clone(),
slot_duration: self.slot_duration,
}
}
}
impl<B, RClient, RBackend, CIDP> AuraConsensus<B, RClient, RBackend, CIDP>
impl<B, CIDP> AuraConsensus<B, CIDP>
where
B: BlockT,
RClient: ProvideRuntimeApi<PBlock>,
RClient::Api: ParachainHost<PBlock>,
RBackend: Backend<PBlock>,
CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)>,
CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)> + 'static,
CIDP::InherentDataProviders: InherentDataProviderExt,
{
/// Create a new instance of AURA consensus.
pub fn new<P, Client, BI, SO, PF, BS, Error>(
para_client: Arc<Client>,
block_import: BI,
sync_oracle: SO,
proposer_factory: PF,
force_authoring: bool,
backoff_authoring_blocks: Option<BS>,
keystore: SyncCryptoStorePtr,
create_inherent_data_providers: CIDP,
polkadot_client: Arc<RClient>,
polkadot_backend: Arc<RBackend>,
slot_duration: SlotDuration,
telemetry: Option<TelemetryHandle>,
block_proposal_slot_portion: SlotProportion,
max_block_proposal_slot_portion: Option<SlotProportion>,
) -> Self
/// Create a new boxed instance of AURA consensus.
pub fn build<P, Client, BI, SO, PF, BS, Error>(
BuildAuraConsensusParams {
proposer_factory,
create_inherent_data_providers,
block_import,
para_client,
backoff_authoring_blocks,
sync_oracle,
keystore,
force_authoring,
slot_duration,
telemetry,
block_proposal_slot_portion,
max_block_proposal_slot_portion,
}: BuildAuraConsensusParams<PF, BI, CIDP, Client, BS, SO>,
) -> Box<dyn ParachainConsensus<B>>
where
Client: ProvideRuntimeApi<B>
+ BlockOf
+ ProvideCache<B>
+ AuxStore
+ HeaderBackend<B>
+ Send
+ Sync
+ 'static,
Client:
ProvideRuntimeApi<B> + BlockOf + AuxStore + HeaderBackend<B> + Send + Sync + 'static,
Client::Api: AuraApi<B, P::Public>,
BI: BlockImport<B, Transaction = sp_api::TransactionFor<Client, B>> + Send + Sync + 'static,
SO: SyncOracle + Send + Sync + Clone + 'static,
@@ -154,13 +138,11 @@ where
},
);
Self {
Box::new(Self {
create_inherent_data_providers: Arc::new(create_inherent_data_providers),
relay_chain_backend: polkadot_backend,
relay_chain_client: polkadot_client,
aura_worker: Arc::new(Mutex::new(worker)),
slot_duration,
}
})
}
/// Create the inherent data.
@@ -200,13 +182,10 @@ where
}
#[async_trait::async_trait]
impl<B, RClient, RBackend, CIDP> ParachainConsensus<B> for AuraConsensus<B, RClient, RBackend, CIDP>
impl<B, CIDP> ParachainConsensus<B> for AuraConsensus<B, CIDP>
where
B: BlockT,
RClient: ProvideRuntimeApi<PBlock> + Send + Sync,
RClient::Api: ParachainHost<PBlock>,
RBackend: Backend<PBlock>,
CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)> + Send + Sync,
CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)> + Send + Sync + 'static,
CIDP::InherentDataProviders: InherentDataProviderExt + Send,
{
async fn produce_candidate(
@@ -238,12 +217,10 @@ where
}
/// Paramaters of [`build_aura_consensus`].
pub struct BuildAuraConsensusParams<PF, BI, RBackend, CIDP, Client, BS, SO> {
pub struct BuildAuraConsensusParams<PF, BI, CIDP, Client, BS, SO> {
pub proposer_factory: PF,
pub create_inherent_data_providers: CIDP,
pub block_import: BI,
pub relay_chain_client: polkadot_client::Client,
pub relay_chain_backend: Arc<RBackend>,
pub para_client: Arc<Client>,
pub backoff_authoring_blocks: Option<BS>,
pub sync_oracle: SO,
@@ -254,250 +231,3 @@ pub struct BuildAuraConsensusParams<PF, BI, RBackend, CIDP, Client, BS, SO> {
pub block_proposal_slot_portion: SlotProportion,
pub max_block_proposal_slot_portion: Option<SlotProportion>,
}
/// Build the [`AuraConsensus`].
///
/// Returns a boxed [`ParachainConsensus`].
pub fn build_aura_consensus<P, Block, PF, BI, RBackend, CIDP, Client, SO, BS, Error>(
BuildAuraConsensusParams {
proposer_factory,
create_inherent_data_providers,
block_import,
relay_chain_client,
relay_chain_backend,
para_client,
backoff_authoring_blocks,
sync_oracle,
keystore,
force_authoring,
slot_duration,
telemetry,
block_proposal_slot_portion,
max_block_proposal_slot_portion,
}: BuildAuraConsensusParams<PF, BI, RBackend, CIDP, Client, BS, SO>,
) -> Box<dyn ParachainConsensus<Block>>
where
Block: BlockT,
RBackend: Backend<PBlock> + 'static,
CIDP: CreateInherentDataProviders<Block, (PHash, PersistedValidationData)>
+ Send
+ Sync
+ 'static,
CIDP::InherentDataProviders: InherentDataProviderExt + Send,
Client: ProvideRuntimeApi<Block>
+ BlockOf
+ ProvideCache<Block>
+ AuxStore
+ HeaderBackend<Block>
+ Send
+ Sync
+ 'static,
Client::Api: AuraApi<Block, P::Public>,
BI: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>>
+ Send
+ Sync
+ 'static,
SO: SyncOracle + Send + Sync + Clone + 'static,
BS: BackoffAuthoringBlocksStrategy<NumberFor<Block>> + Send + Sync + 'static,
PF: Environment<Block, Error = Error> + Send + Sync + 'static,
PF::Proposer: Proposer<
Block,
Error = Error,
Transaction = sp_api::TransactionFor<Client, Block>,
ProofRecording = EnableProofRecording,
Proof = <EnableProofRecording as ProofRecording>::Proof,
>,
Error: std::error::Error + Send + From<sp_consensus::Error> + 'static,
P: Pair + Send + Sync,
P::Public: AppPublic + Hash + Member + Encode + Decode,
P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
{
AuraConsensusBuilder::<P, _, _, _, _, _, _, _, _, _>::new(
proposer_factory,
block_import,
create_inherent_data_providers,
relay_chain_client,
relay_chain_backend,
para_client,
backoff_authoring_blocks,
sync_oracle,
force_authoring,
keystore,
slot_duration,
telemetry,
block_proposal_slot_portion,
max_block_proposal_slot_portion,
)
.build()
}
/// Aura consensus builder.
///
/// Builds a [`AuraConsensus`] for a parachain. As this requires
/// a concrete relay chain client instance, the builder takes a [`polkadot_client::Client`]
/// that wraps this concrete instance. By using [`polkadot_client::ExecuteWithClient`]
/// the builder gets access to this concrete instance.
struct AuraConsensusBuilder<P, Block, PF, BI, RBackend, CIDP, Client, SO, BS, Error> {
_phantom: PhantomData<(Block, Error, P)>,
proposer_factory: PF,
create_inherent_data_providers: CIDP,
block_import: BI,
relay_chain_backend: Arc<RBackend>,
relay_chain_client: polkadot_client::Client,
para_client: Arc<Client>,
backoff_authoring_blocks: Option<BS>,
sync_oracle: SO,
force_authoring: bool,
keystore: SyncCryptoStorePtr,
slot_duration: SlotDuration,
telemetry: Option<TelemetryHandle>,
block_proposal_slot_portion: SlotProportion,
max_block_proposal_slot_portion: Option<SlotProportion>,
}
impl<Block, PF, BI, RBackend, CIDP, Client, SO, BS, P, Error>
AuraConsensusBuilder<P, Block, PF, BI, RBackend, CIDP, Client, SO, BS, Error>
where
Block: BlockT,
RBackend: Backend<PBlock> + 'static,
CIDP: CreateInherentDataProviders<Block, (PHash, PersistedValidationData)>
+ Send
+ Sync
+ 'static,
CIDP::InherentDataProviders: InherentDataProviderExt + Send,
Client: ProvideRuntimeApi<Block>
+ BlockOf
+ ProvideCache<Block>
+ AuxStore
+ HeaderBackend<Block>
+ Send
+ Sync
+ 'static,
Client::Api: AuraApi<Block, P::Public>,
BI: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>>
+ Send
+ Sync
+ 'static,
SO: SyncOracle + Send + Sync + Clone + 'static,
BS: BackoffAuthoringBlocksStrategy<NumberFor<Block>> + Send + Sync + 'static,
PF: Environment<Block, Error = Error> + Send + Sync + 'static,
PF::Proposer: Proposer<
Block,
Error = Error,
Transaction = sp_api::TransactionFor<Client, Block>,
ProofRecording = EnableProofRecording,
Proof = <EnableProofRecording as ProofRecording>::Proof,
>,
Error: std::error::Error + Send + From<sp_consensus::Error> + 'static,
P: Pair + Send + Sync,
P::Public: AppPublic + Hash + Member + Encode + Decode,
P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
{
/// Create a new instance of the builder.
fn new(
proposer_factory: PF,
block_import: BI,
create_inherent_data_providers: CIDP,
relay_chain_client: polkadot_client::Client,
relay_chain_backend: Arc<RBackend>,
para_client: Arc<Client>,
backoff_authoring_blocks: Option<BS>,
sync_oracle: SO,
force_authoring: bool,
keystore: SyncCryptoStorePtr,
slot_duration: SlotDuration,
telemetry: Option<TelemetryHandle>,
block_proposal_slot_portion: SlotProportion,
max_block_proposal_slot_portion: Option<SlotProportion>,
) -> Self {
Self {
_phantom: PhantomData,
proposer_factory,
block_import,
create_inherent_data_providers,
relay_chain_backend,
relay_chain_client,
para_client,
backoff_authoring_blocks,
sync_oracle,
force_authoring,
keystore,
slot_duration,
telemetry,
block_proposal_slot_portion,
max_block_proposal_slot_portion,
}
}
/// Build the relay chain consensus.
fn build(self) -> Box<dyn ParachainConsensus<Block>> {
self.relay_chain_client.clone().execute_with(self)
}
}
impl<Block, PF, BI, RBackend, CIDP, Client, SO, BS, P, Error> polkadot_client::ExecuteWithClient
for AuraConsensusBuilder<P, Block, PF, BI, RBackend, CIDP, Client, SO, BS, Error>
where
Block: BlockT,
RBackend: Backend<PBlock> + 'static,
CIDP: CreateInherentDataProviders<Block, (PHash, PersistedValidationData)>
+ Send
+ Sync
+ 'static,
CIDP::InherentDataProviders: InherentDataProviderExt + Send,
Client: ProvideRuntimeApi<Block>
+ BlockOf
+ ProvideCache<Block>
+ AuxStore
+ HeaderBackend<Block>
+ Send
+ Sync
+ 'static,
Client::Api: AuraApi<Block, P::Public>,
BI: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>>
+ Send
+ Sync
+ 'static,
SO: SyncOracle + Send + Sync + Clone + 'static,
BS: BackoffAuthoringBlocksStrategy<NumberFor<Block>> + Send + Sync + 'static,
PF: Environment<Block, Error = Error> + Send + Sync + 'static,
PF::Proposer: Proposer<
Block,
Error = Error,
Transaction = sp_api::TransactionFor<Client, Block>,
ProofRecording = EnableProofRecording,
Proof = <EnableProofRecording as ProofRecording>::Proof,
>,
Error: std::error::Error + Send + From<sp_consensus::Error> + 'static,
P: Pair + Send + Sync,
P::Public: AppPublic + Hash + Member + Encode + Decode,
P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
{
type Output = Box<dyn ParachainConsensus<Block>>;
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
<Api as sp_api::ApiExt<PBlock>>::StateBackend: sp_api::StateBackend<HashFor<PBlock>>,
PBackend: Backend<PBlock>,
PBackend::State: sp_api::StateBackend<sp_runtime::traits::BlakeTwo256>,
Api: polkadot_client::RuntimeApiCollection<StateBackend = PBackend::State>,
PClient: polkadot_client::AbstractClient<PBlock, PBackend, Api = Api> + 'static,
{
Box::new(AuraConsensus::new::<P, _, _, _, _, _, _>(
self.para_client,
self.block_import,
self.sync_oracle,
self.proposer_factory,
self.force_authoring,
self.backoff_authoring_blocks,
self.keystore,
self.create_inherent_data_providers,
client.clone(),
self.relay_chain_backend,
self.slot_duration,
self.telemetry,
self.block_proposal_slot_portion,
self.max_block_proposal_slot_portion,
))
}
}
+4 -1
View File
@@ -3,7 +3,7 @@ name = "cumulus-client-consensus-common"
description = "Cumulus specific common consensus implementations"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Substrate deps
@@ -18,6 +18,9 @@ sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot deps
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# Cumulus deps
cumulus-relay-chain-interface = { path = "../../relay-chain-interface" }
# Other deps
futures = { version = "0.3.8", features = ["compat"] }
codec = { package = "parity-scale-codec", version = "2.3.0", features = [ "derive" ] }
@@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
use cumulus_relay_chain_interface::RelayChainInterface;
use sc_client_api::{
Backend, BlockBackend, BlockImportNotification, BlockchainEvents, Finalizer, UsageProvider,
};
use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::{Error as ClientError, Result as ClientResult};
use sp_consensus::{BlockOrigin, BlockStatus};
use sp_runtime::{
@@ -26,9 +26,7 @@ use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT},
};
use polkadot_primitives::v1::{
Block as PBlock, Id as ParaId, OccupiedCoreAssumption, ParachainHost,
};
use polkadot_primitives::v1::{Block as PBlock, Id as ParaId, OccupiedCoreAssumption};
use codec::Decode;
use futures::{future, select, FutureExt, Stream, StreamExt};
@@ -370,10 +368,9 @@ where
}
}
impl<T> RelaychainClient for Arc<T>
impl<RCInterface> RelaychainClient for RCInterface
where
T: sc_client_api::BlockchainEvents<PBlock> + ProvideRuntimeApi<PBlock> + 'static + Send + Sync,
<T as ProvideRuntimeApi<PBlock>>::Api: ParachainHost<PBlock>,
RCInterface: RelayChainInterface + Clone + 'static,
{
type Error = ClientError;
@@ -410,8 +407,7 @@ where
at: &BlockId<PBlock>,
para_id: ParaId,
) -> ClientResult<Option<Vec<u8>>> {
self.runtime_api()
.persisted_validation_data(at, para_id, OccupiedCoreAssumption::TimedOut)
self.persisted_validation_data(at, para_id, OccupiedCoreAssumption::TimedOut)
.map(|s| s.map(|s| s.parent_head.0))
.map_err(Into::into)
}
+2 -2
View File
@@ -3,7 +3,7 @@ name = "cumulus-client-consensus-relay-chain"
description = "The relay-chain provided consensus algorithm"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Substrate deps
@@ -19,11 +19,11 @@ sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "mast
substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot dependencies
polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# Cumulus dependencies
cumulus-client-consensus-common = { path = "../common" }
cumulus-primitives-core = { path = "../../../primitives/core" }
cumulus-relay-chain-interface = { path = "../../relay-chain-interface" }
# Other deps
futures = { version = "0.3.8", features = ["compat"] }
+27 -134
View File
@@ -36,20 +36,16 @@
use cumulus_client_consensus_common::{
ParachainBlockImport, ParachainCandidate, ParachainConsensus,
};
use cumulus_primitives_core::{
relay_chain::v1::{Block as PBlock, Hash as PHash, ParachainHost},
ParaId, PersistedValidationData,
};
use cumulus_primitives_core::{relay_chain::v1::Hash as PHash, ParaId, PersistedValidationData};
use cumulus_relay_chain_interface::RelayChainInterface;
use parking_lot::Mutex;
use polkadot_client::ClientHandle;
use sc_client_api::Backend;
use sc_consensus::{BlockImport, BlockImportParams};
use sp_api::ProvideRuntimeApi;
use sp_consensus::{
BlockOrigin, EnableProofRecording, Environment, ProofRecording, Proposal, Proposer,
};
use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
use sp_runtime::traits::{Block as BlockT, HashFor, Header as HeaderT};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use std::{marker::PhantomData, sync::Arc, time::Duration};
mod import_queue;
@@ -58,18 +54,18 @@ pub use import_queue::{import_queue, Verifier};
const LOG_TARGET: &str = "cumulus-consensus-relay-chain";
/// The implementation of the relay-chain provided consensus for parachains.
pub struct RelayChainConsensus<B, PF, BI, RClient, RBackend, CIDP> {
pub struct RelayChainConsensus<B, PF, BI, RCInterface, CIDP> {
para_id: ParaId,
_phantom: PhantomData<B>,
proposer_factory: Arc<Mutex<PF>>,
create_inherent_data_providers: Arc<CIDP>,
block_import: Arc<futures::lock::Mutex<ParachainBlockImport<BI>>>,
relay_chain_client: Arc<RClient>,
relay_chain_backend: Arc<RBackend>,
relay_chain_interface: RCInterface,
}
impl<B, PF, BI, RClient, RBackend, CIDP> Clone
for RelayChainConsensus<B, PF, BI, RClient, RBackend, CIDP>
impl<B, PF, BI, RCInterface, CIDP> Clone for RelayChainConsensus<B, PF, BI, RCInterface, CIDP>
where
RCInterface: Clone,
{
fn clone(&self) -> Self {
Self {
@@ -78,18 +74,15 @@ impl<B, PF, BI, RClient, RBackend, CIDP> Clone
proposer_factory: self.proposer_factory.clone(),
create_inherent_data_providers: self.create_inherent_data_providers.clone(),
block_import: self.block_import.clone(),
relay_chain_backend: self.relay_chain_backend.clone(),
relay_chain_client: self.relay_chain_client.clone(),
relay_chain_interface: self.relay_chain_interface.clone(),
}
}
}
impl<B, PF, BI, RClient, RBackend, CIDP> RelayChainConsensus<B, PF, BI, RClient, RBackend, CIDP>
impl<B, PF, BI, RCInterface, CIDP> RelayChainConsensus<B, PF, BI, RCInterface, CIDP>
where
B: BlockT,
RClient: ProvideRuntimeApi<PBlock>,
RClient::Api: ParachainHost<PBlock>,
RBackend: Backend<PBlock>,
RCInterface: RelayChainInterface,
CIDP: CreateInherentDataProviders<B, (PHash, PersistedValidationData)>,
{
/// Create a new instance of relay-chain provided consensus.
@@ -98,8 +91,7 @@ where
proposer_factory: PF,
create_inherent_data_providers: CIDP,
block_import: BI,
polkadot_client: Arc<RClient>,
polkadot_backend: Arc<RBackend>,
relay_chain_interface: RCInterface,
) -> Self {
Self {
para_id,
@@ -108,8 +100,7 @@ where
block_import: Arc::new(futures::lock::Mutex::new(ParachainBlockImport::new(
block_import,
))),
relay_chain_backend: polkadot_backend,
relay_chain_client: polkadot_client,
relay_chain_interface,
_phantom: PhantomData,
}
}
@@ -148,13 +139,11 @@ where
}
#[async_trait::async_trait]
impl<B, PF, BI, RClient, RBackend, CIDP> ParachainConsensus<B>
for RelayChainConsensus<B, PF, BI, RClient, RBackend, CIDP>
impl<B, PF, BI, RCInterface, CIDP> ParachainConsensus<B>
for RelayChainConsensus<B, PF, BI, RCInterface, CIDP>
where
B: BlockT,
RClient: ProvideRuntimeApi<PBlock> + Send + Sync,
RClient::Api: ParachainHost<PBlock>,
RBackend: Backend<PBlock>,
RCInterface: RelayChainInterface + Clone,
BI: BlockImport<B> + Send + Sync,
PF: Environment<B> + Send + Sync,
PF::Proposer: Proposer<
@@ -229,27 +218,25 @@ where
}
/// Paramaters of [`build_relay_chain_consensus`].
pub struct BuildRelayChainConsensusParams<PF, BI, RBackend, CIDP> {
pub struct BuildRelayChainConsensusParams<PF, BI, CIDP, RCInterface> {
pub para_id: ParaId,
pub proposer_factory: PF,
pub create_inherent_data_providers: CIDP,
pub block_import: BI,
pub relay_chain_client: polkadot_client::Client,
pub relay_chain_backend: Arc<RBackend>,
pub relay_chain_interface: RCInterface,
}
/// Build the [`RelayChainConsensus`].
///
/// Returns a boxed [`ParachainConsensus`].
pub fn build_relay_chain_consensus<Block, PF, BI, RBackend, CIDP>(
pub fn build_relay_chain_consensus<Block, PF, BI, CIDP, RCInterface>(
BuildRelayChainConsensusParams {
para_id,
proposer_factory,
create_inherent_data_providers,
block_import,
relay_chain_client,
relay_chain_backend,
}: BuildRelayChainConsensusParams<PF, BI, RBackend, CIDP>,
relay_chain_interface,
}: BuildRelayChainConsensusParams<PF, BI, CIDP, RCInterface>,
) -> Box<dyn ParachainConsensus<Block>>
where
Block: BlockT,
@@ -261,108 +248,14 @@ where
Proof = <EnableProofRecording as ProofRecording>::Proof,
>,
BI: BlockImport<Block> + Send + Sync + 'static,
RBackend: Backend<PBlock> + 'static,
CIDP: CreateInherentDataProviders<Block, (PHash, PersistedValidationData)> + 'static,
RCInterface: RelayChainInterface + Clone + 'static,
{
RelayChainConsensusBuilder::new(
Box::new(RelayChainConsensus::new(
para_id,
proposer_factory,
block_import,
create_inherent_data_providers,
relay_chain_client,
relay_chain_backend,
)
.build()
}
/// Relay chain consensus builder.
///
/// Builds a [`RelayChainConsensus`] for a parachain. As this requires
/// a concrete relay chain client instance, the builder takes a [`polkadot_client::Client`]
/// that wraps this concrete instanace. By using [`polkadot_client::ExecuteWithClient`]
/// the builder gets access to this concrete instance.
struct RelayChainConsensusBuilder<Block, PF, BI, RBackend, CIDP> {
para_id: ParaId,
_phantom: PhantomData<Block>,
proposer_factory: PF,
create_inherent_data_providers: CIDP,
block_import: BI,
relay_chain_backend: Arc<RBackend>,
relay_chain_client: polkadot_client::Client,
}
impl<Block, PF, BI, RBackend, CIDP> RelayChainConsensusBuilder<Block, PF, BI, RBackend, CIDP>
where
Block: BlockT,
PF: Environment<Block> + Send + Sync + 'static,
PF::Proposer: Proposer<
Block,
Transaction = BI::Transaction,
ProofRecording = EnableProofRecording,
Proof = <EnableProofRecording as ProofRecording>::Proof,
>,
BI: BlockImport<Block> + Send + Sync + 'static,
RBackend: Backend<PBlock> + 'static,
CIDP: CreateInherentDataProviders<Block, (PHash, PersistedValidationData)> + 'static,
{
/// Create a new instance of the builder.
fn new(
para_id: ParaId,
proposer_factory: PF,
block_import: BI,
create_inherent_data_providers: CIDP,
relay_chain_client: polkadot_client::Client,
relay_chain_backend: Arc<RBackend>,
) -> Self {
Self {
para_id,
_phantom: PhantomData,
proposer_factory,
block_import,
create_inherent_data_providers,
relay_chain_backend,
relay_chain_client,
}
}
/// Build the relay chain consensus.
fn build(self) -> Box<dyn ParachainConsensus<Block>> {
self.relay_chain_client.clone().execute_with(self)
}
}
impl<Block, PF, BI, RBackend, CIDP> polkadot_client::ExecuteWithClient
for RelayChainConsensusBuilder<Block, PF, BI, RBackend, CIDP>
where
Block: BlockT,
PF: Environment<Block> + Send + Sync + 'static,
PF::Proposer: Proposer<
Block,
Transaction = BI::Transaction,
ProofRecording = EnableProofRecording,
Proof = <EnableProofRecording as ProofRecording>::Proof,
>,
BI: BlockImport<Block> + Send + Sync + 'static,
RBackend: Backend<PBlock> + 'static,
CIDP: CreateInherentDataProviders<Block, (PHash, PersistedValidationData)> + 'static,
{
type Output = Box<dyn ParachainConsensus<Block>>;
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
<Api as sp_api::ApiExt<PBlock>>::StateBackend: sp_api::StateBackend<HashFor<PBlock>>,
PBackend: Backend<PBlock>,
PBackend::State: sp_api::StateBackend<sp_runtime::traits::BlakeTwo256>,
Api: polkadot_client::RuntimeApiCollection<StateBackend = PBackend::State>,
PClient: polkadot_client::AbstractClient<PBlock, PBackend, Api = Api> + 'static,
{
Box::new(RelayChainConsensus::new(
self.para_id,
self.proposer_factory,
self.create_inherent_data_providers,
self.block_import,
client.clone(),
self.relay_chain_backend,
))
}
block_import,
relay_chain_interface,
))
}
+9 -3
View File
@@ -3,7 +3,7 @@ name = "cumulus-client-network"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Cumulus-specific networking protocol"
edition = "2018"
edition = "2021"
[dependencies]
# Substrate deps
@@ -13,20 +13,23 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot deps
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-node-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" }
cumulus-relay-chain-interface = { path = "../relay-chain-interface" }
# other deps
codec = { package = "parity-scale-codec", version = "2.3.0", features = [ "derive" ] }
futures = { version = "0.3.1", features = ["compat"] }
futures-timer = "3.0.2"
tracing = "0.1.22"
parking_lot = "0.10.2"
parking_lot = "0.11.1"
derive_more = "0.99.2"
async-trait = "0.1.52"
[dev-dependencies]
tokio = { version = "1.10", features = ["macros"] }
@@ -34,9 +37,12 @@ tokio = { version = "1.10", features = ["macros"] }
# Cumulus deps
cumulus-test-service = { path = "../../test/service" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-relay-chain-local = { path = "../relay-chain-local" }
# Polkadot deps
polkadot-test-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# substrate deps
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
+38 -153
View File
@@ -20,12 +20,8 @@
//! that use the relay chain provided consensus. See [`BlockAnnounceValidator`]
//! and [`WaitToAnnounce`] for more information about this implementation.
use sc_client_api::{Backend, BlockchainEvents};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_consensus::{
block_validation::{BlockAnnounceValidator as BlockAnnounceValidatorT, Validation},
SyncOracle,
use sp_consensus::block_validation::{
BlockAnnounceValidator as BlockAnnounceValidatorT, Validation,
};
use sp_core::traits::SpawnNamed;
use sp_runtime::{
@@ -33,12 +29,12 @@ use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT},
};
use polkadot_client::ClientHandle;
use cumulus_relay_chain_interface::RelayChainInterface;
use polkadot_node_primitives::{CollationSecondedSignal, Statement};
use polkadot_parachain::primitives::HeadData;
use polkadot_primitives::v1::{
Block as PBlock, CandidateReceipt, CompactStatement, Hash as PHash, Id as ParaId,
OccupiedCoreAssumption, ParachainHost, SigningContext, UncheckedSigned,
OccupiedCoreAssumption, SigningContext, UncheckedSigned,
};
use codec::{Decode, DecodeAll, Encode};
@@ -50,11 +46,8 @@ use futures::{
use std::{convert::TryFrom, fmt, marker::PhantomData, pin::Pin, sync::Arc};
use wait_on_relay_chain_block::WaitOnRelayChainBlock;
#[cfg(test)]
mod tests;
mod wait_on_relay_chain_block;
const LOG_TARGET: &str = "sync::cumulus";
@@ -135,19 +128,18 @@ impl BlockAnnounceData {
/// Check the signature of the statement.
///
/// Returns an `Err(_)` if it failed.
fn check_signature<P>(
fn check_signature<RCInterface>(
self,
relay_chain_client: &Arc<P>,
relay_chain_client: &RCInterface,
) -> Result<Validation, BlockAnnounceError>
where
P: ProvideRuntimeApi<PBlock> + Send + Sync + 'static,
P::Api: ParachainHost<PBlock>,
RCInterface: RelayChainInterface + 'static,
{
let runtime_api = relay_chain_client.runtime_api();
let validator_index = self.statement.unchecked_validator_index();
let runtime_api_block_id = BlockId::Hash(self.relay_parent);
let session_index = match runtime_api.session_index_for_child(&runtime_api_block_id) {
let session_index = match relay_chain_client.session_index_for_child(&runtime_api_block_id)
{
Ok(r) => r,
Err(e) => return Err(BlockAnnounceError(format!("{:?}", e))),
};
@@ -155,7 +147,7 @@ impl BlockAnnounceData {
let signing_context = SigningContext { parent_hash: self.relay_parent, session_index };
// Check that the signer is a legit validator.
let authorities = match runtime_api.validators(&runtime_api_block_id) {
let authorities = match relay_chain_client.validators(&runtime_api_block_id) {
Ok(r) => r,
Err(e) => return Err(BlockAnnounceError(format!("{:?}", e))),
};
@@ -230,52 +222,37 @@ impl TryFrom<&'_ CollationSecondedSignal> for BlockAnnounceData {
/// chain. If it is at the tip, it is required to provide a justification or otherwise we reject
/// it. However, if the announcement is for a block below the tip the announcement is accepted
/// as it probably comes from a node that is currently syncing the chain.
pub struct BlockAnnounceValidator<Block, R, B, BCE> {
pub struct BlockAnnounceValidator<Block, RCInterface> {
phantom: PhantomData<Block>,
relay_chain_client: Arc<R>,
relay_chain_backend: Arc<B>,
relay_chain_interface: RCInterface,
para_id: ParaId,
relay_chain_sync_oracle: Box<dyn SyncOracle + Send>,
wait_on_relay_chain_block: WaitOnRelayChainBlock<B, BCE>,
}
impl<Block, R, B, BCE> BlockAnnounceValidator<Block, R, B, BCE> {
impl<Block, RCInterface> BlockAnnounceValidator<Block, RCInterface>
where
RCInterface: Clone,
{
/// Create a new [`BlockAnnounceValidator`].
pub fn new(
relay_chain_client: Arc<R>,
para_id: ParaId,
relay_chain_sync_oracle: Box<dyn SyncOracle + Send>,
relay_chain_backend: Arc<B>,
relay_chain_blockchain_events: Arc<BCE>,
) -> Self {
pub fn new(relay_chain_interface: RCInterface, para_id: ParaId) -> Self {
Self {
phantom: Default::default(),
relay_chain_client,
relay_chain_interface: relay_chain_interface.clone(),
para_id,
relay_chain_sync_oracle,
relay_chain_backend: relay_chain_backend.clone(),
wait_on_relay_chain_block: WaitOnRelayChainBlock::new(
relay_chain_backend,
relay_chain_blockchain_events,
),
}
}
}
impl<Block: BlockT, R, B, BCE> BlockAnnounceValidator<Block, R, B, BCE>
impl<Block: BlockT, RCInterface> BlockAnnounceValidator<Block, RCInterface>
where
R: ProvideRuntimeApi<PBlock> + Send + Sync + 'static,
R::Api: ParachainHost<PBlock>,
B: Backend<PBlock> + 'static,
RCInterface: RelayChainInterface + Clone,
{
/// Get the included block of the given parachain in the relay chain.
fn included_block(
relay_chain_client: &R,
relay_chain_interface: &RCInterface,
block_id: &BlockId<PBlock>,
para_id: ParaId,
) -> Result<Block::Header, BoxedError> {
let validation_data = relay_chain_client
.runtime_api()
let validation_data = relay_chain_interface
.persisted_validation_data(block_id, para_id, OccupiedCoreAssumption::TimedOut)
.map_err(|e| Box::new(BlockAnnounceError(format!("{:?}", e))) as Box<_>)?
.ok_or_else(|| {
@@ -293,12 +270,11 @@ where
/// Get the backed block hash of the given parachain in the relay chain.
fn backed_block_hash(
relay_chain_client: &R,
relay_chain_interface: &RCInterface,
block_id: &BlockId<PBlock>,
para_id: ParaId,
) -> Result<Option<PHash>, BoxedError> {
let candidate_receipt = relay_chain_client
.runtime_api()
let candidate_receipt = relay_chain_interface
.candidate_pending_availability(block_id, para_id)
.map_err(|e| Box::new(BlockAnnounceError(format!("{:?}", e))) as Box<_>)?;
@@ -310,21 +286,20 @@ where
&self,
header: Block::Header,
) -> impl Future<Output = Result<Validation, BoxedError>> {
let relay_chain_client = self.relay_chain_client.clone();
let relay_chain_backend = self.relay_chain_backend.clone();
let relay_chain_interface = self.relay_chain_interface.clone();
let para_id = self.para_id;
async move {
// Check if block is equal or higher than best (this requires a justification)
let relay_chain_info = relay_chain_backend.blockchain().info();
let runtime_api_block_id = BlockId::Hash(relay_chain_info.best_hash);
let relay_chain_best_hash = relay_chain_interface.best_block_hash();
let runtime_api_block_id = BlockId::Hash(relay_chain_best_hash);
let block_number = header.number();
let best_head =
Self::included_block(&*relay_chain_client, &runtime_api_block_id, para_id)?;
Self::included_block(&relay_chain_interface, &runtime_api_block_id, para_id)?;
let known_best_number = best_head.number();
let backed_block =
|| Self::backed_block_hash(&*relay_chain_client, &runtime_api_block_id, para_id);
|| Self::backed_block_hash(&relay_chain_interface, &runtime_api_block_id, para_id);
if best_head == header {
tracing::debug!(target: LOG_TARGET, "Announced block matches best block.",);
@@ -348,20 +323,17 @@ where
}
}
impl<Block: BlockT, P, B, BCE> BlockAnnounceValidatorT<Block>
for BlockAnnounceValidator<Block, P, B, BCE>
impl<Block: BlockT, RCInterface> BlockAnnounceValidatorT<Block>
for BlockAnnounceValidator<Block, RCInterface>
where
P: ProvideRuntimeApi<PBlock> + Send + Sync + 'static,
P::Api: ParachainHost<PBlock>,
B: Backend<PBlock> + 'static,
BCE: BlockchainEvents<PBlock> + 'static + Send + Sync,
RCInterface: RelayChainInterface + Clone + 'static,
{
fn validate(
&mut self,
header: &Block::Header,
mut data: &[u8],
) -> Pin<Box<dyn Future<Output = Result<Validation, BoxedError>> + Send>> {
if self.relay_chain_sync_oracle.is_major_syncing() {
if self.relay_chain_interface.is_major_syncing() {
return ready(Ok(Validation::Success { is_new_best: false })).boxed()
}
@@ -381,9 +353,8 @@ where
.boxed(),
};
let relay_chain_client = self.relay_chain_client.clone();
let relay_chain_interface = self.relay_chain_interface.clone();
let header_encoded = header.encode();
let wait_on_relay_chain_block = self.wait_on_relay_chain_block.clone();
async move {
if let Err(e) = block_announce_data.validate(header_encoded) {
@@ -392,106 +363,19 @@ where
let relay_parent = block_announce_data.receipt.descriptor.relay_parent;
wait_on_relay_chain_block
.wait_on_relay_chain_block(relay_parent)
relay_chain_interface
.wait_for_block(relay_parent)
.await
.map_err(|e| Box::new(BlockAnnounceError(e.to_string())) as Box<_>)?;
block_announce_data
.check_signature(&relay_chain_client)
.check_signature(&relay_chain_interface)
.map_err(|e| Box::new(e) as Box<_>)
}
.boxed()
}
}
/// Build a block announce validator instance.
///
/// Returns a boxed [`BlockAnnounceValidator`].
pub fn build_block_announce_validator<Block: BlockT, B>(
relay_chain_client: polkadot_client::Client,
para_id: ParaId,
relay_chain_sync_oracle: Box<dyn SyncOracle + Send>,
relay_chain_backend: Arc<B>,
) -> Box<dyn BlockAnnounceValidatorT<Block> + Send>
where
B: Backend<PBlock> + Send + 'static,
{
BlockAnnounceValidatorBuilder::new(
relay_chain_client,
para_id,
relay_chain_sync_oracle,
relay_chain_backend,
)
.build()
}
/// Block announce validator builder.
///
/// Builds a [`BlockAnnounceValidator`] for a parachain. As this requires
/// a concrete relay chain client instance, the builder takes a [`polkadot_client::Client`]
/// that wraps this concrete instanace. By using [`polkadot_client::ExecuteWithClient`]
/// the builder gets access to this concrete instance.
struct BlockAnnounceValidatorBuilder<Block, B> {
phantom: PhantomData<Block>,
relay_chain_client: polkadot_client::Client,
para_id: ParaId,
relay_chain_sync_oracle: Box<dyn SyncOracle + Send>,
relay_chain_backend: Arc<B>,
}
impl<Block: BlockT, B> BlockAnnounceValidatorBuilder<Block, B>
where
B: Backend<PBlock> + Send + 'static,
{
/// Create a new instance of the builder.
fn new(
relay_chain_client: polkadot_client::Client,
para_id: ParaId,
relay_chain_sync_oracle: Box<dyn SyncOracle + Send>,
relay_chain_backend: Arc<B>,
) -> Self {
Self {
relay_chain_client,
para_id,
relay_chain_sync_oracle,
relay_chain_backend,
phantom: PhantomData,
}
}
/// Build the block announce validator.
fn build(self) -> Box<dyn BlockAnnounceValidatorT<Block> + Send> {
self.relay_chain_client.clone().execute_with(self)
}
}
impl<Block: BlockT, B> polkadot_client::ExecuteWithClient
for BlockAnnounceValidatorBuilder<Block, B>
where
B: Backend<PBlock> + Send + 'static,
{
type Output = Box<dyn BlockAnnounceValidatorT<Block> + Send>;
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
<Api as sp_api::ApiExt<PBlock>>::StateBackend:
sp_api::StateBackend<sp_runtime::traits::BlakeTwo256>,
PBackend: Backend<PBlock>,
PBackend::State: sp_api::StateBackend<sp_runtime::traits::BlakeTwo256>,
Api: polkadot_client::RuntimeApiCollection<StateBackend = PBackend::State>,
PClient: polkadot_client::AbstractClient<PBlock, PBackend, Api = Api> + 'static,
{
Box::new(BlockAnnounceValidator::new(
client.clone(),
self.para_id,
self.relay_chain_sync_oracle,
self.relay_chain_backend,
client,
))
}
}
/// Wait before announcing a block that a candidate message has been received for this block, then
/// add this message as justification for the block announcement.
///
@@ -522,6 +406,7 @@ impl<Block: BlockT> WaitToAnnounce<Block> {
self.spawner.spawn(
"cumulus-wait-to-announce",
None,
async move {
tracing::debug!(
target: "cumulus-network",
+215 -156
View File
@@ -15,29 +15,33 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use async_trait::async_trait;
use cumulus_relay_chain_interface::WaitError;
use cumulus_relay_chain_local::{check_block_in_chain, BlockCheckStatus};
use cumulus_test_service::runtime::{Block, Hash, Header};
use futures::{executor::block_on, poll, task::Poll};
use futures::{executor::block_on, poll, task::Poll, FutureExt, StreamExt};
use parking_lot::Mutex;
use polkadot_node_primitives::{SignedFullStatement, Statement};
use polkadot_primitives::v1::{
Block as PBlock, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateEvent,
CommittedCandidateReceipt, CoreState, GroupRotationInfo, Hash as PHash, HeadData, Id as ParaId,
InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, ParachainHost,
PersistedValidationData, ScrapedOnChainVotes, SessionIndex, SessionInfo, SigningContext,
ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
Block as PBlock, CandidateCommitments, CandidateDescriptor, CollatorPair,
CommittedCandidateReceipt, Hash as PHash, HeadData, Id as ParaId, InboundDownwardMessage,
InboundHrmpMessage, OccupiedCoreAssumption, PersistedValidationData, SessionIndex,
SigningContext, ValidationCodeHash, ValidatorId,
};
use polkadot_service::Handle;
use polkadot_test_client::{
Client as PClient, ClientBlockImportExt, DefaultTestClientBuilderExt, FullBackend as PBackend,
InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt,
};
use sp_api::{ApiRef, ProvideRuntimeApi};
use sc_client_api::{Backend, BlockchainEvents};
use sp_blockchain::HeaderBackend;
use sp_consensus::BlockOrigin;
use sp_core::H256;
use sp_core::{Pair, H256};
use sp_keyring::Sr25519Keyring;
use sp_keystore::{testing::KeyStore, SyncCryptoStore, SyncCryptoStorePtr};
use sp_runtime::RuntimeAppPublic;
use std::collections::BTreeMap;
use sp_state_machine::StorageValue;
use std::{collections::BTreeMap, time::Duration};
fn check_error(error: crate::BoxedError, check_error: impl Fn(&BlockAnnounceError) -> bool) {
let error = *error
@@ -49,31 +53,190 @@ fn check_error(error: crate::BoxedError, check_error: impl Fn(&BlockAnnounceErro
}
#[derive(Clone)]
struct DummyCollatorNetwork;
struct DummyRelayChainInterface {
data: Arc<Mutex<ApiData>>,
relay_client: Arc<PClient>,
relay_backend: Arc<PBackend>,
}
impl SyncOracle for DummyCollatorNetwork {
fn is_major_syncing(&mut self) -> bool {
impl DummyRelayChainInterface {
fn new() -> Self {
let builder = TestClientBuilder::new();
let relay_backend = builder.backend();
Self {
data: Arc::new(Mutex::new(ApiData {
validators: vec![Sr25519Keyring::Alice.public().into()],
has_pending_availability: false,
})),
relay_client: Arc::new(builder.build()),
relay_backend,
}
}
}
#[async_trait]
impl RelayChainInterface for DummyRelayChainInterface {
fn validators(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
) -> Result<Vec<ValidatorId>, sp_api::ApiError> {
Ok(self.data.lock().validators.clone())
}
fn block_status(
&self,
block_id: cumulus_primitives_core::relay_chain::BlockId,
) -> Result<sp_blockchain::BlockStatus, sp_blockchain::Error> {
self.relay_backend.blockchain().status(block_id)
}
fn best_block_hash(&self) -> PHash {
self.relay_backend.blockchain().info().best_hash
}
fn retrieve_dmq_contents(&self, _: ParaId, _: PHash) -> Option<Vec<InboundDownwardMessage>> {
unimplemented!("Not needed for test")
}
fn retrieve_all_inbound_hrmp_channel_contents(
&self,
_: ParaId,
_: PHash,
) -> Option<BTreeMap<ParaId, Vec<InboundHrmpMessage>>> {
Some(BTreeMap::new())
}
fn persisted_validation_data(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
_: ParaId,
_: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData>, sp_api::ApiError> {
Ok(Some(PersistedValidationData {
parent_head: HeadData(default_header().encode()),
..Default::default()
}))
}
fn candidate_pending_availability(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
_: ParaId,
) -> Result<Option<CommittedCandidateReceipt>, sp_api::ApiError> {
if self.data.lock().has_pending_availability {
Ok(Some(CommittedCandidateReceipt {
descriptor: CandidateDescriptor {
para_head: polkadot_parachain::primitives::HeadData(default_header().encode())
.hash(),
para_id: 0u32.into(),
relay_parent: PHash::random(),
collator: CollatorPair::generate().0.public(),
persisted_validation_data_hash: PHash::random().into(),
pov_hash: PHash::random(),
erasure_root: PHash::random(),
signature: sp_core::sr25519::Signature([0u8; 64]).into(),
validation_code_hash: ValidationCodeHash::from(PHash::random()),
},
commitments: CandidateCommitments {
upward_messages: Vec::new(),
horizontal_messages: Vec::new(),
new_validation_code: None,
head_data: HeadData(Vec::new()),
processed_downward_messages: 0,
hrmp_watermark: 0,
},
}))
} else {
Ok(None)
}
}
fn session_index_for_child(
&self,
_: &cumulus_primitives_core::relay_chain::BlockId,
) -> Result<SessionIndex, sp_api::ApiError> {
Ok(0)
}
fn import_notification_stream(&self) -> sc_client_api::ImportNotifications<PBlock> {
self.relay_client.import_notification_stream()
}
fn finality_notification_stream(&self) -> sc_client_api::FinalityNotifications<PBlock> {
self.relay_client.finality_notification_stream()
}
fn storage_changes_notification_stream(
&self,
filter_keys: Option<&[sc_client_api::StorageKey]>,
child_filter_keys: Option<
&[(sc_client_api::StorageKey, Option<Vec<sc_client_api::StorageKey>>)],
>,
) -> sc_client_api::blockchain::Result<sc_client_api::StorageEventStream<PHash>> {
self.relay_client
.storage_changes_notification_stream(filter_keys, child_filter_keys)
}
fn is_major_syncing(&self) -> bool {
false
}
fn is_offline(&mut self) -> bool {
unimplemented!("Not required in tests")
fn overseer_handle(&self) -> Option<Handle> {
unimplemented!("Not needed for test")
}
fn get_storage_by_key(
&self,
_: &polkadot_service::BlockId,
_: &[u8],
) -> Result<Option<StorageValue>, sp_blockchain::Error> {
unimplemented!("Not needed for test")
}
fn prove_read(
&self,
_: &polkadot_service::BlockId,
_: &Vec<Vec<u8>>,
) -> Result<Option<sc_client_api::StorageProof>, Box<dyn sp_state_machine::Error>> {
unimplemented!("Not needed for test")
}
async fn wait_for_block(
&self,
hash: PHash,
) -> Result<(), cumulus_relay_chain_interface::WaitError> {
let mut listener = match check_block_in_chain(
self.relay_backend.clone(),
self.relay_client.clone(),
hash,
)? {
BlockCheckStatus::InChain => return Ok(()),
BlockCheckStatus::Unknown(listener) => listener,
};
let mut timeout = futures_timer::Delay::new(Duration::from_secs(10)).fuse();
loop {
futures::select! {
_ = timeout => return Err(WaitError::Timeout(hash)),
evt = listener.next() => match evt {
Some(evt) if evt.hash == hash => return Ok(()),
// Not the event we waited on.
Some(_) => continue,
None => return Err(WaitError::ImportListenerClosed(hash)),
}
}
}
}
}
fn make_validator_and_api(
) -> (BlockAnnounceValidator<Block, TestApi, PBackend, PClient>, Arc<TestApi>) {
let api = Arc::new(TestApi::new());
) -> (BlockAnnounceValidator<Block, Arc<DummyRelayChainInterface>>, Arc<DummyRelayChainInterface>) {
let relay_chain_interface = Arc::new(DummyRelayChainInterface::new());
(
BlockAnnounceValidator::new(
api.clone(),
ParaId::from(56),
Box::new(DummyCollatorNetwork),
api.relay_backend.clone(),
api.relay_client.clone(),
),
api,
BlockAnnounceValidator::new(relay_chain_interface.clone(), ParaId::from(56)),
relay_chain_interface,
)
}
@@ -89,7 +252,7 @@ fn default_header() -> Header {
/// Same as [`make_gossip_message_and_header`], but using the genesis header as relay parent.
async fn make_gossip_message_and_header_using_genesis(
api: Arc<TestApi>,
api: Arc<DummyRelayChainInterface>,
validator_index: u32,
) -> (CollationSecondedSignal, Header) {
let relay_parent = api.relay_client.hash(0).ok().flatten().expect("Genesis hash exists");
@@ -98,7 +261,7 @@ async fn make_gossip_message_and_header_using_genesis(
}
async fn make_gossip_message_and_header(
api: Arc<TestApi>,
relay_chain_interface: Arc<DummyRelayChainInterface>,
relay_parent: H256,
validator_index: u32,
) -> (CollationSecondedSignal, Header) {
@@ -109,8 +272,9 @@ async fn make_gossip_message_and_header(
Some(&Sr25519Keyring::Alice.to_seed()),
)
.unwrap();
let session_index =
api.runtime_api().session_index_for_child(&BlockId::Hash(relay_parent)).unwrap();
let session_index = relay_chain_interface
.session_index_for_child(&BlockId::Hash(relay_parent))
.unwrap();
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let header = default_header();
@@ -120,9 +284,15 @@ async fn make_gossip_message_and_header(
..Default::default()
},
descriptor: CandidateDescriptor {
para_id: 0u32.into(),
relay_parent,
collator: CollatorPair::generate().0.public(),
persisted_validation_data_hash: PHash::random().into(),
pov_hash: PHash::random(),
erasure_root: PHash::random(),
signature: sp_core::sr25519::Signature([0u8; 64]).into(),
para_head: polkadot_parachain::primitives::HeadData(header.encode()).hash(),
..Default::default()
validation_code_hash: ValidationCodeHash::from(PHash::random()),
},
};
let statement = Statement::Seconded(candidate_receipt);
@@ -285,8 +455,7 @@ fn check_statement_seconded() {
Some(&Sr25519Keyring::Alice.to_seed()),
)
.unwrap();
let session_index =
api.runtime_api().session_index_for_child(&BlockId::Hash(relay_parent)).unwrap();
let session_index = api.session_index_for_child(&BlockId::Hash(relay_parent)).unwrap();
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let statement = Statement::Valid(Default::default());
@@ -303,7 +472,20 @@ fn check_statement_seconded() {
.expect("Signs statement");
let data = BlockAnnounceData {
receipt: Default::default(),
receipt: CandidateReceipt {
commitments_hash: PHash::random(),
descriptor: CandidateDescriptor {
para_head: HeadData(Vec::new()).hash(),
para_id: 0u32.into(),
relay_parent: PHash::random(),
collator: CollatorPair::generate().0.public(),
persisted_validation_data_hash: PHash::random().into(),
pov_hash: PHash::random(),
erasure_root: PHash::random(),
signature: sp_core::sr25519::Signature([0u8; 64]).into(),
validation_code_hash: ValidationCodeHash::from(PHash::random()),
},
},
statement: signed_statement.convert_payload().into(),
relay_parent,
}
@@ -377,126 +559,3 @@ struct ApiData {
validators: Vec<ValidatorId>,
has_pending_availability: bool,
}
struct TestApi {
data: Arc<Mutex<ApiData>>,
relay_client: Arc<PClient>,
relay_backend: Arc<PBackend>,
}
impl TestApi {
fn new() -> Self {
let builder = TestClientBuilder::new();
let relay_backend = builder.backend();
Self {
data: Arc::new(Mutex::new(ApiData {
validators: vec![Sr25519Keyring::Alice.public().into()],
has_pending_availability: false,
})),
relay_client: Arc::new(builder.build()),
relay_backend,
}
}
}
#[derive(Default)]
struct RuntimeApi {
data: Arc<Mutex<ApiData>>,
}
impl ProvideRuntimeApi<PBlock> for TestApi {
type Api = RuntimeApi;
fn runtime_api<'a>(&'a self) -> ApiRef<'a, Self::Api> {
RuntimeApi { data: self.data.clone() }.into()
}
}
sp_api::mock_impl_runtime_apis! {
impl ParachainHost<PBlock> for RuntimeApi {
fn validators(&self) -> Vec<ValidatorId> {
self.data.lock().validators.clone()
}
fn validator_groups(&self) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumber>) {
(Vec::new(), GroupRotationInfo { session_start_block: 0, group_rotation_frequency: 0, now: 0 })
}
fn availability_cores(&self) -> Vec<CoreState<PHash>> {
Vec::new()
}
fn persisted_validation_data(
&self,
_: ParaId,
_: OccupiedCoreAssumption,
) -> Option<PersistedValidationData<PHash, BlockNumber>> {
Some(PersistedValidationData {
parent_head: HeadData(default_header().encode()),
..Default::default()
})
}
fn session_index_for_child(&self) -> SessionIndex {
0
}
fn validation_code(&self, _: ParaId, _: OccupiedCoreAssumption) -> Option<ValidationCode> {
None
}
fn candidate_pending_availability(&self, _: ParaId) -> Option<CommittedCandidateReceipt<PHash>> {
if self.data.lock().has_pending_availability {
Some(CommittedCandidateReceipt {
descriptor: CandidateDescriptor {
para_head: polkadot_parachain::primitives::HeadData(
default_header().encode(),
).hash(),
..Default::default()
},
..Default::default()
})
} else {
None
}
}
fn candidate_events(&self) -> Vec<CandidateEvent<PHash>> {
Vec::new()
}
fn session_info(_: SessionIndex) -> Option<SessionInfo> {
None
}
fn check_validation_outputs(_: ParaId, _: CandidateCommitments) -> bool {
false
}
fn dmq_contents(_: ParaId) -> Vec<InboundDownwardMessage<BlockNumber>> {
Vec::new()
}
fn inbound_hrmp_channels_contents(
_: ParaId,
) -> BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumber>>> {
BTreeMap::new()
}
fn assumed_validation_data(
_: ParaId,
_: Hash,
) -> Option<(PersistedValidationData<Hash, BlockNumber>, ValidationCodeHash)> {
None
}
fn validation_code_by_hash(_: ValidationCodeHash) -> Option<ValidationCode> {
None
}
fn on_chain_votes() -> Option<ScrapedOnChainVotes<Hash>> {
None
}
}
}
@@ -1,235 +0,0 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot 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.
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Provides the [`WaitOnRelayChainBlock`] type.
use futures::{future::ready, Future, FutureExt, StreamExt};
use polkadot_primitives::v1::{Block as PBlock, Hash as PHash};
use sc_client_api::{
blockchain::{self, BlockStatus, HeaderBackend},
Backend, BlockchainEvents,
};
use sp_runtime::generic::BlockId;
use std::{sync::Arc, time::Duration};
/// The timeout in seconds after that the waiting for a block should be aborted.
const TIMEOUT_IN_SECONDS: u64 = 6;
/// Custom error type used by [`WaitOnRelayChainBlock`].
#[derive(Debug, derive_more::Display)]
pub enum Error {
#[display(fmt = "Timeout while waiting for relay-chain block `{}` to be imported.", _0)]
Timeout(PHash),
#[display(
fmt = "Import listener closed while waiting for relay-chain block `{}` to be imported.",
_0
)]
ImportListenerClosed(PHash),
#[display(
fmt = "Blockchain returned an error while waiting for relay-chain block `{}` to be imported: {:?}",
_0,
_1
)]
BlockchainError(PHash, blockchain::Error),
}
/// A helper to wait for a given relay chain block in an async way.
///
/// The caller needs to pass the hash of a block it waits for and the function will return when the
/// block is available or an error occurred.
///
/// The waiting for the block is implemented as follows:
///
/// 1. Get a read lock on the import lock from the backend.
///
/// 2. Check if the block is already imported. If yes, return from the function.
///
/// 3. If the block isn't imported yet, add an import notification listener.
///
/// 4. Poll the import notification listener until the block is imported or the timeout is fired.
///
/// The timeout is set to 6 seconds. This should be enough time to import the block in the current
/// round and if not, the new round of the relay chain already started anyway.
pub struct WaitOnRelayChainBlock<B, BCE> {
block_chain_events: Arc<BCE>,
backend: Arc<B>,
}
impl<B, BCE> Clone for WaitOnRelayChainBlock<B, BCE> {
fn clone(&self) -> Self {
Self { backend: self.backend.clone(), block_chain_events: self.block_chain_events.clone() }
}
}
impl<B, BCE> WaitOnRelayChainBlock<B, BCE> {
/// Creates a new instance of `Self`.
pub fn new(backend: Arc<B>, block_chain_events: Arc<BCE>) -> Self {
Self { backend, block_chain_events }
}
}
impl<B, BCE> WaitOnRelayChainBlock<B, BCE>
where
B: Backend<PBlock>,
BCE: BlockchainEvents<PBlock>,
{
pub fn wait_on_relay_chain_block(
&self,
hash: PHash,
) -> impl Future<Output = Result<(), Error>> {
let _lock = self.backend.get_import_lock().read();
match self.backend.blockchain().status(BlockId::Hash(hash)) {
Ok(BlockStatus::InChain) => return ready(Ok(())).boxed(),
Err(err) => return ready(Err(Error::BlockchainError(hash, err))).boxed(),
_ => {},
}
let mut listener = self.block_chain_events.import_notification_stream();
// Now it is safe to drop the lock, even when the block is now imported, it should show
// up in our registered listener.
drop(_lock);
let mut timeout = futures_timer::Delay::new(Duration::from_secs(TIMEOUT_IN_SECONDS)).fuse();
async move {
loop {
futures::select! {
_ = timeout => return Err(Error::Timeout(hash)),
evt = listener.next() => match evt {
Some(evt) if evt.hash == hash => return Ok(()),
// Not the event we waited on.
Some(_) => continue,
None => return Err(Error::ImportListenerClosed(hash)),
}
}
}
}
.boxed()
}
}
#[cfg(test)]
mod tests {
use super::*;
use polkadot_test_client::{
construct_transfer_extrinsic, BlockBuilderExt, Client, ClientBlockImportExt,
DefaultTestClientBuilderExt, ExecutionStrategy, FullBackend, InitPolkadotBlockBuilder,
TestClientBuilder, TestClientBuilderExt,
};
use sp_consensus::BlockOrigin;
use sp_runtime::traits::Block as BlockT;
use futures::{executor::block_on, poll, task::Poll};
fn build_client_backend_and_block() -> (Arc<Client>, Arc<FullBackend>, PBlock) {
let builder =
TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::NativeWhenPossible);
let backend = builder.backend();
let client = Arc::new(builder.build());
let block_builder = client.init_polkadot_block_builder();
let block = block_builder.build().expect("Finalizes the block").block;
(client, backend, block)
}
#[test]
fn returns_directly_for_available_block() {
let (mut client, backend, block) = build_client_backend_and_block();
let hash = block.hash();
block_on(client.import(BlockOrigin::Own, block)).expect("Imports the block");
let wait = WaitOnRelayChainBlock::new(backend, client);
block_on(async move {
// Should be ready on the first poll
assert!(matches!(poll!(wait.wait_on_relay_chain_block(hash)), Poll::Ready(Ok(()))));
});
}
#[test]
fn resolve_after_block_import_notification_was_received() {
let (mut client, backend, block) = build_client_backend_and_block();
let hash = block.hash();
let wait = WaitOnRelayChainBlock::new(backend, client.clone());
block_on(async move {
let mut future = wait.wait_on_relay_chain_block(hash);
// As the block is not yet imported, the first poll should return `Pending`
assert!(poll!(&mut future).is_pending());
// Import the block that should fire the notification
client.import(BlockOrigin::Own, block).await.expect("Imports the block");
// Now it should have received the notification and report that the block was imported
assert!(matches!(poll!(future), Poll::Ready(Ok(()))));
});
}
#[test]
fn wait_for_block_time_out_when_block_is_not_imported() {
let (client, backend, block) = build_client_backend_and_block();
let hash = block.hash();
let wait = WaitOnRelayChainBlock::new(backend, client.clone());
assert!(matches!(block_on(wait.wait_on_relay_chain_block(hash)), Err(Error::Timeout(_))));
}
#[test]
fn do_not_resolve_after_different_block_import_notification_was_received() {
let (mut client, backend, block) = build_client_backend_and_block();
let hash = block.hash();
let ext = construct_transfer_extrinsic(
&*client,
sp_keyring::Sr25519Keyring::Alice,
sp_keyring::Sr25519Keyring::Bob,
1000,
);
let mut block_builder = client.init_polkadot_block_builder();
// Push an extrinsic to get a different block hash.
block_builder.push_polkadot_extrinsic(ext).expect("Push extrinsic");
let block2 = block_builder.build().expect("Build second block").block;
let hash2 = block2.hash();
let wait = WaitOnRelayChainBlock::new(backend, client.clone());
block_on(async move {
let mut future = wait.wait_on_relay_chain_block(hash);
let mut future2 = wait.wait_on_relay_chain_block(hash2);
// As the block is not yet imported, the first poll should return `Pending`
assert!(poll!(&mut future).is_pending());
assert!(poll!(&mut future2).is_pending());
// Import the block that should fire the notification
client.import(BlockOrigin::Own, block2).await.expect("Imports the second block");
// The import notification of the second block should not make this one finish
assert!(poll!(&mut future).is_pending());
// Now it should have received the notification and report that the block was imported
assert!(matches!(poll!(future2), Poll::Ready(Ok(()))));
client.import(BlockOrigin::Own, block).await.expect("Imports the first block");
// Now it should be ready
assert!(matches!(poll!(future), Poll::Ready(Ok(()))));
});
}
}
+1 -10
View File
@@ -16,7 +16,6 @@
use cumulus_primitives_core::ParaId;
use cumulus_test_service::{initial_head_data, run_relay_chain_validator_node, Keyring::*};
use futures::join;
#[substrate_test_utils::test]
#[ignore]
@@ -29,7 +28,7 @@ async fn sync_blocks_from_tip_without_being_connected_to_a_collator() {
let tokio_handle = tokio::runtime::Handle::current();
// start alice
let alice = run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, vec![]);
let alice = run_relay_chain_validator_node(tokio_handle.clone(), Alice, || {}, Vec::new());
// start bob
let bob =
@@ -71,12 +70,4 @@ async fn sync_blocks_from_tip_without_being_connected_to_a_collator() {
.await;
eve.wait_for_blocks(7).await;
join!(
alice.task_manager.clean_shutdown(),
bob.task_manager.clean_shutdown(),
charlie.task_manager.clean_shutdown(),
dave.task_manager.clean_shutdown(),
eve.task_manager.clean_shutdown(),
);
}
+2 -1
View File
@@ -3,7 +3,7 @@ name = "cumulus-client-pov-recovery"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Cumulus-specific networking protocol"
edition = "2018"
edition = "2021"
[dependencies]
# Substrate deps
@@ -22,6 +22,7 @@ polkadot-node-subsystem = { git = "https://github.com/paritytech/polkadot", bran
# Cumulus deps
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-relay-chain-interface = {path = "../relay-chain-interface"}
# other deps
codec = { package = "parity-scale-codec", version = "2.3.0", features = [ "derive" ] }
+13 -20
View File
@@ -44,7 +44,6 @@
use sc_client_api::{BlockBackend, BlockchainEvents, UsageProvider};
use sc_consensus::import_queue::{ImportQueue, IncomingBlock};
use sp_api::ProvideRuntimeApi;
use sp_consensus::{BlockOrigin, BlockStatus};
use sp_runtime::{
generic::BlockId,
@@ -54,11 +53,11 @@ use sp_runtime::{
use polkadot_node_primitives::{AvailableData, POV_BOMB_LIMIT};
use polkadot_overseer::Handle as OverseerHandle;
use polkadot_primitives::v1::{
Block as PBlock, CandidateReceipt, CommittedCandidateReceipt, Id as ParaId, ParachainHost,
SessionIndex,
CandidateReceipt, CommittedCandidateReceipt, Id as ParaId, SessionIndex,
};
use cumulus_primitives_core::ParachainBlockData;
use cumulus_relay_chain_interface::RelayChainInterface;
use codec::Decode;
use futures::{select, stream::FuturesUnordered, Future, FutureExt, Stream, StreamExt};
@@ -102,15 +101,14 @@ pub struct PoVRecovery<Block: BlockT, PC, IQ, RC> {
relay_chain_slot_duration: Duration,
parachain_client: Arc<PC>,
parachain_import_queue: IQ,
relay_chain_client: Arc<RC>,
relay_chain_interface: RC,
para_id: ParaId,
}
impl<Block: BlockT, PC, IQ, RC> PoVRecovery<Block, PC, IQ, RC>
impl<Block: BlockT, PC, IQ, RCInterface> PoVRecovery<Block, PC, IQ, RCInterface>
where
PC: BlockBackend<Block> + BlockchainEvents<Block> + UsageProvider<Block>,
RC: ProvideRuntimeApi<PBlock> + BlockchainEvents<PBlock>,
RC::Api: ParachainHost<PBlock>,
RCInterface: RelayChainInterface + Clone,
IQ: ImportQueue<Block>,
{
/// Create a new instance.
@@ -119,7 +117,7 @@ where
relay_chain_slot_duration: Duration,
parachain_client: Arc<PC>,
parachain_import_queue: IQ,
relay_chain_client: Arc<RC>,
relay_chain_interface: RCInterface,
para_id: ParaId,
) -> Self {
Self {
@@ -130,7 +128,7 @@ where
waiting_for_parent: HashMap::new(),
parachain_client,
parachain_import_queue,
relay_chain_client,
relay_chain_interface,
para_id,
}
}
@@ -365,7 +363,7 @@ where
let mut imported_blocks = self.parachain_client.import_notification_stream().fuse();
let mut finalized_blocks = self.parachain_client.finality_notification_stream().fuse();
let pending_candidates =
pending_candidates(self.relay_chain_client.clone(), self.para_id).fuse();
pending_candidates(self.relay_chain_interface.clone(), self.para_id).fuse();
futures::pin_mut!(pending_candidates);
loop {
@@ -419,20 +417,15 @@ where
}
/// Returns a stream over pending candidates for the parachain corresponding to `para_id`.
fn pending_candidates<RC>(
relay_chain_client: Arc<RC>,
fn pending_candidates(
relay_chain_client: impl RelayChainInterface,
para_id: ParaId,
) -> impl Stream<Item = (CommittedCandidateReceipt, SessionIndex)>
where
RC: ProvideRuntimeApi<PBlock> + BlockchainEvents<PBlock>,
RC::Api: ParachainHost<PBlock>,
{
) -> impl Stream<Item = (CommittedCandidateReceipt, SessionIndex)> {
relay_chain_client.import_notification_stream().filter_map(move |n| {
let runtime_api = relay_chain_client.runtime_api();
let res = runtime_api
let res = relay_chain_client
.candidate_pending_availability(&BlockId::hash(n.hash), para_id)
.and_then(|pa| {
runtime_api
relay_chain_client
.session_index_for_child(&BlockId::hash(n.hash))
.map(|v| pa.map(|pa| (pa, v)))
})
+1 -9
View File
@@ -16,7 +16,6 @@
use cumulus_primitives_core::ParaId;
use cumulus_test_service::{initial_head_data, Keyring::*};
use futures::join;
use std::sync::Arc;
/// Tests the PoV recovery.
@@ -39,7 +38,7 @@ async fn pov_recovery() {
tokio_handle.clone(),
Alice,
|| {},
vec![],
Vec::new(),
);
// Start bob
@@ -86,11 +85,4 @@ async fn pov_recovery() {
.await;
dave.wait_for_blocks(7).await;
join!(
alice.task_manager.clean_shutdown(),
bob.task_manager.clean_shutdown(),
charlie.task_manager.clean_shutdown(),
dave.task_manager.clean_shutdown(),
);
}
+21
View File
@@ -0,0 +1,21 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
name = "cumulus-relay-chain-interface"
version = "0.1.0"
edition = "2021"
[dependencies]
polkadot-overseer = { git = "https://github.com/paritytech/polkadot", branch = "master" }
cumulus-primitives-core = { path = "../../primitives/core" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
parking_lot = "0.11.1"
derive_more = "0.99.2"
async-trait = "0.1.52"
+248
View File
@@ -0,0 +1,248 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus 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.
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
use std::{collections::BTreeMap, sync::Arc};
use cumulus_primitives_core::{
relay_chain::{
v1::{CommittedCandidateReceipt, OccupiedCoreAssumption, SessionIndex, ValidatorId},
Block as PBlock, BlockId, Hash as PHash, InboundHrmpMessage,
},
InboundDownwardMessage, ParaId, PersistedValidationData,
};
use polkadot_overseer::Handle as OverseerHandle;
use sc_client_api::{blockchain::BlockStatus, StorageProof};
use sp_api::ApiError;
use sp_state_machine::StorageValue;
use async_trait::async_trait;
#[derive(Debug, derive_more::Display)]
pub enum WaitError {
#[display(fmt = "Timeout while waiting for relay-chain block `{}` to be imported.", _0)]
Timeout(PHash),
#[display(
fmt = "Import listener closed while waiting for relay-chain block `{}` to be imported.",
_0
)]
ImportListenerClosed(PHash),
#[display(
fmt = "Blockchain returned an error while waiting for relay-chain block `{}` to be imported: {:?}",
_0,
_1
)]
BlockchainError(PHash, sp_blockchain::Error),
}
/// Trait that provides all necessary methods for interaction between collator and relay chain.
#[async_trait]
pub trait RelayChainInterface: Send + Sync {
/// Fetch a storage item by key.
fn get_storage_by_key(
&self,
block_id: &BlockId,
key: &[u8],
) -> Result<Option<StorageValue>, sp_blockchain::Error>;
/// Fetch a vector of current validators.
fn validators(&self, block_id: &BlockId) -> Result<Vec<ValidatorId>, ApiError>;
/// Get the status of a given block.
fn block_status(&self, block_id: BlockId) -> Result<BlockStatus, sp_blockchain::Error>;
/// Get the hash of the current best block.
fn best_block_hash(&self) -> PHash;
/// Returns the whole contents of the downward message queue for the parachain we are collating
/// for.
///
/// Returns `None` in case of an error.
fn retrieve_dmq_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> Option<Vec<InboundDownwardMessage>>;
/// Returns channels contents for each inbound HRMP channel addressed to the parachain we are
/// collating for.
///
/// Empty channels are also included.
fn retrieve_all_inbound_hrmp_channel_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> Option<BTreeMap<ParaId, Vec<InboundHrmpMessage>>>;
/// Yields the persisted validation data for the given `ParaId` along with an assumption that
/// should be used if the para currently occupies a core.
///
/// Returns `None` if either the para is not registered or the assumption is `Freed`
/// and the para already occupies a core.
fn persisted_validation_data(
&self,
block_id: &BlockId,
para_id: ParaId,
_: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData>, ApiError>;
/// Get the receipt of a candidate pending availability. This returns `Some` for any paras
/// assigned to occupied cores in `availability_cores` and `None` otherwise.
fn candidate_pending_availability(
&self,
block_id: &BlockId,
para_id: ParaId,
) -> Result<Option<CommittedCandidateReceipt>, ApiError>;
/// Returns the session index expected at a child of the block.
fn session_index_for_child(&self, block_id: &BlockId) -> Result<SessionIndex, ApiError>;
/// Get a stream of import block notifications.
fn import_notification_stream(&self) -> sc_client_api::ImportNotifications<PBlock>;
/// Wait for a block with a given hash in the relay chain.
///
/// This method returns immediately on error or if the block is already
/// reported to be in chain. Otherwise, it waits for the block to arrive.
async fn wait_for_block(&self, hash: PHash) -> Result<(), WaitError>;
/// Get a stream of finality notifications.
fn finality_notification_stream(&self) -> sc_client_api::FinalityNotifications<PBlock>;
/// Get a stream of storage change notifications.
fn storage_changes_notification_stream(
&self,
filter_keys: Option<&[sc_client_api::StorageKey]>,
child_filter_keys: Option<
&[(sc_client_api::StorageKey, Option<Vec<sc_client_api::StorageKey>>)],
>,
) -> sc_client_api::blockchain::Result<sc_client_api::StorageEventStream<PHash>>;
/// Whether the synchronization service is undergoing major sync.
/// Returns true if so.
fn is_major_syncing(&self) -> bool;
/// Get a handle to the overseer.
fn overseer_handle(&self) -> Option<OverseerHandle>;
/// Generate a storage read proof.
fn prove_read(
&self,
block_id: &BlockId,
relevant_keys: &Vec<Vec<u8>>,
) -> Result<Option<StorageProof>, Box<dyn sp_state_machine::Error>>;
}
#[async_trait]
impl<T> RelayChainInterface for Arc<T>
where
T: RelayChainInterface + ?Sized,
{
fn retrieve_dmq_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> Option<Vec<InboundDownwardMessage>> {
(**self).retrieve_dmq_contents(para_id, relay_parent)
}
fn retrieve_all_inbound_hrmp_channel_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> Option<BTreeMap<ParaId, Vec<InboundHrmpMessage>>> {
(**self).retrieve_all_inbound_hrmp_channel_contents(para_id, relay_parent)
}
fn persisted_validation_data(
&self,
block_id: &BlockId,
para_id: ParaId,
occupied_core_assumption: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData>, ApiError> {
(**self).persisted_validation_data(block_id, para_id, occupied_core_assumption)
}
fn candidate_pending_availability(
&self,
block_id: &BlockId,
para_id: ParaId,
) -> Result<Option<CommittedCandidateReceipt>, ApiError> {
(**self).candidate_pending_availability(block_id, para_id)
}
fn session_index_for_child(&self, block_id: &BlockId) -> Result<SessionIndex, ApiError> {
(**self).session_index_for_child(block_id)
}
fn validators(&self, block_id: &BlockId) -> Result<Vec<ValidatorId>, ApiError> {
(**self).validators(block_id)
}
fn import_notification_stream(&self) -> sc_client_api::ImportNotifications<PBlock> {
(**self).import_notification_stream()
}
fn finality_notification_stream(&self) -> sc_client_api::FinalityNotifications<PBlock> {
(**self).finality_notification_stream()
}
fn storage_changes_notification_stream(
&self,
filter_keys: Option<&[sc_client_api::StorageKey]>,
child_filter_keys: Option<
&[(sc_client_api::StorageKey, Option<Vec<sc_client_api::StorageKey>>)],
>,
) -> sc_client_api::blockchain::Result<sc_client_api::StorageEventStream<PHash>> {
(**self).storage_changes_notification_stream(filter_keys, child_filter_keys)
}
fn best_block_hash(&self) -> PHash {
(**self).best_block_hash()
}
fn block_status(&self, block_id: BlockId) -> Result<BlockStatus, sp_blockchain::Error> {
(**self).block_status(block_id)
}
fn is_major_syncing(&self) -> bool {
(**self).is_major_syncing()
}
fn overseer_handle(&self) -> Option<OverseerHandle> {
(**self).overseer_handle()
}
fn get_storage_by_key(
&self,
block_id: &BlockId,
key: &[u8],
) -> Result<Option<StorageValue>, sp_blockchain::Error> {
(**self).get_storage_by_key(block_id, key)
}
fn prove_read(
&self,
block_id: &BlockId,
relevant_keys: &Vec<Vec<u8>>,
) -> Result<Option<StorageProof>, Box<dyn sp_state_machine::Error>> {
(**self).prove_read(block_id, relevant_keys)
}
async fn wait_for_block(&self, hash: PHash) -> Result<(), WaitError> {
(**self).wait_for_block(hash).await
}
}
+43
View File
@@ -0,0 +1,43 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
name = "cumulus-relay-chain-local"
version = "0.1.0"
edition = "2021"
[dependencies]
polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-relay-chain-interface = { path = "../relay-chain-interface" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" }
parking_lot = "0.11.1"
tracing = "0.1.25"
async-trait = "0.1.52"
futures = { version = "0.3.1", features = ["compat"] }
futures-timer = "3.0.2"
[dev-dependencies]
# Cumulus deps
cumulus-test-service = { path = "../../test/service" }
# Polkadot deps
polkadot-test-client = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# substrate deps
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
+540
View File
@@ -0,0 +1,540 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus 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.
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
use std::{sync::Arc, time::Duration};
use async_trait::async_trait;
use cumulus_primitives_core::{
relay_chain::{
v1::{CommittedCandidateReceipt, OccupiedCoreAssumption, SessionIndex, ValidatorId},
v2::ParachainHost,
Block as PBlock, BlockId, Hash as PHash, InboundHrmpMessage,
},
InboundDownwardMessage, ParaId, PersistedValidationData,
};
use cumulus_relay_chain_interface::{RelayChainInterface, WaitError};
use futures::{FutureExt, StreamExt};
use parking_lot::Mutex;
use polkadot_client::{ClientHandle, ExecuteWithClient, FullBackend};
use polkadot_service::{
AuxStore, BabeApi, CollatorPair, Configuration, Handle, NewFull, Role, TaskManager,
};
use sc_client_api::{
blockchain::BlockStatus, Backend, BlockchainEvents, HeaderBackend, ImportNotifications,
StorageProof, UsageProvider,
};
use sc_telemetry::TelemetryWorkerHandle;
use sp_api::{ApiError, ProvideRuntimeApi};
use sp_consensus::SyncOracle;
use sp_core::{sp_std::collections::btree_map::BTreeMap, Pair};
use sp_state_machine::{Backend as StateBackend, StorageValue};
const LOG_TARGET: &str = "relay-chain-local";
/// The timeout in seconds after that the waiting for a block should be aborted.
const TIMEOUT_IN_SECONDS: u64 = 6;
/// Provides an implementation of the [`RelayChainInterface`] using a local in-process relay chain node.
pub struct RelayChainLocal<Client> {
full_client: Arc<Client>,
backend: Arc<FullBackend>,
sync_oracle: Arc<Mutex<Box<dyn SyncOracle + Send + Sync>>>,
overseer_handle: Option<Handle>,
}
impl<Client> RelayChainLocal<Client> {
/// Create a new instance of [`RelayChainLocal`]
pub fn new(
full_client: Arc<Client>,
backend: Arc<FullBackend>,
sync_oracle: Arc<Mutex<Box<dyn SyncOracle + Send + Sync>>>,
overseer_handle: Option<Handle>,
) -> Self {
Self { full_client, backend, sync_oracle, overseer_handle }
}
}
impl<T> Clone for RelayChainLocal<T> {
fn clone(&self) -> Self {
Self {
full_client: self.full_client.clone(),
backend: self.backend.clone(),
sync_oracle: self.sync_oracle.clone(),
overseer_handle: self.overseer_handle.clone(),
}
}
}
#[async_trait]
impl<Client> RelayChainInterface for RelayChainLocal<Client>
where
Client: ProvideRuntimeApi<PBlock>
+ BlockchainEvents<PBlock>
+ AuxStore
+ UsageProvider<PBlock>
+ Sync
+ Send,
Client::Api: ParachainHost<PBlock> + BabeApi<PBlock>,
{
fn retrieve_dmq_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> Option<Vec<InboundDownwardMessage>> {
self.full_client
.runtime_api()
.dmq_contents_with_context(
&BlockId::hash(relay_parent),
sp_core::ExecutionContext::Importing,
para_id,
)
.map_err(|e| {
tracing::error!(
target: LOG_TARGET,
relay_parent = ?relay_parent,
error = ?e,
"An error occured during requesting the downward messages.",
);
})
.ok()
}
fn retrieve_all_inbound_hrmp_channel_contents(
&self,
para_id: ParaId,
relay_parent: PHash,
) -> Option<BTreeMap<ParaId, Vec<InboundHrmpMessage>>> {
self.full_client
.runtime_api()
.inbound_hrmp_channels_contents_with_context(
&BlockId::hash(relay_parent),
sp_core::ExecutionContext::Importing,
para_id,
)
.map_err(|e| {
tracing::error!(
target: LOG_TARGET,
relay_parent = ?relay_parent,
error = ?e,
"An error occured during requesting the inbound HRMP messages.",
);
})
.ok()
}
fn persisted_validation_data(
&self,
block_id: &BlockId,
para_id: ParaId,
occupied_core_assumption: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData>, ApiError> {
self.full_client.runtime_api().persisted_validation_data(
block_id,
para_id,
occupied_core_assumption,
)
}
fn candidate_pending_availability(
&self,
block_id: &BlockId,
para_id: ParaId,
) -> Result<Option<CommittedCandidateReceipt>, ApiError> {
self.full_client.runtime_api().candidate_pending_availability(block_id, para_id)
}
fn session_index_for_child(&self, block_id: &BlockId) -> Result<SessionIndex, ApiError> {
self.full_client.runtime_api().session_index_for_child(block_id)
}
fn validators(&self, block_id: &BlockId) -> Result<Vec<ValidatorId>, ApiError> {
self.full_client.runtime_api().validators(block_id)
}
fn import_notification_stream(&self) -> sc_client_api::ImportNotifications<PBlock> {
self.full_client.import_notification_stream()
}
fn finality_notification_stream(&self) -> sc_client_api::FinalityNotifications<PBlock> {
self.full_client.finality_notification_stream()
}
fn storage_changes_notification_stream(
&self,
filter_keys: Option<&[sc_client_api::StorageKey]>,
child_filter_keys: Option<
&[(sc_client_api::StorageKey, Option<Vec<sc_client_api::StorageKey>>)],
>,
) -> sc_client_api::blockchain::Result<sc_client_api::StorageEventStream<PHash>> {
self.full_client
.storage_changes_notification_stream(filter_keys, child_filter_keys)
}
fn best_block_hash(&self) -> PHash {
self.backend.blockchain().info().best_hash
}
fn block_status(&self, block_id: BlockId) -> Result<BlockStatus, sp_blockchain::Error> {
self.backend.blockchain().status(block_id)
}
fn is_major_syncing(&self) -> bool {
let mut network = self.sync_oracle.lock();
network.is_major_syncing()
}
fn overseer_handle(&self) -> Option<Handle> {
self.overseer_handle.clone()
}
fn get_storage_by_key(
&self,
block_id: &BlockId,
key: &[u8],
) -> Result<Option<StorageValue>, sp_blockchain::Error> {
let state = self.backend.state_at(*block_id)?;
state.storage(key).map_err(sp_blockchain::Error::Storage)
}
fn prove_read(
&self,
block_id: &BlockId,
relevant_keys: &Vec<Vec<u8>>,
) -> Result<Option<StorageProof>, Box<dyn sp_state_machine::Error>> {
let state_backend = self
.backend
.state_at(*block_id)
.map_err(|e| {
tracing::error!(
target: LOG_TARGET,
relay_parent = ?block_id,
error = ?e,
"Cannot obtain the state of the relay chain.",
);
})
.ok();
match state_backend {
Some(state) => sp_state_machine::prove_read(state, relevant_keys)
.map_err(|e| {
tracing::error!(
target: LOG_TARGET,
relay_parent = ?block_id,
error = ?e,
"Failed to collect required relay chain state storage proof.",
);
e
})
.map(Some),
None => Ok(None),
}
}
/// Wait for a given relay chain block in an async way.
///
/// The caller needs to pass the hash of a block it waits for and the function will return when the
/// block is available or an error occurred.
///
/// The waiting for the block is implemented as follows:
///
/// 1. Get a read lock on the import lock from the backend.
///
/// 2. Check if the block is already imported. If yes, return from the function.
///
/// 3. If the block isn't imported yet, add an import notification listener.
///
/// 4. Poll the import notification listener until the block is imported or the timeout is fired.
///
/// The timeout is set to 6 seconds. This should be enough time to import the block in the current
/// round and if not, the new round of the relay chain already started anyway.
async fn wait_for_block(&self, hash: PHash) -> Result<(), WaitError> {
let mut listener =
match check_block_in_chain(self.backend.clone(), self.full_client.clone(), hash)? {
BlockCheckStatus::InChain => return Ok(()),
BlockCheckStatus::Unknown(listener) => listener,
};
let mut timeout = futures_timer::Delay::new(Duration::from_secs(TIMEOUT_IN_SECONDS)).fuse();
loop {
futures::select! {
_ = timeout => return Err(WaitError::Timeout(hash)),
evt = listener.next() => match evt {
Some(evt) if evt.hash == hash => return Ok(()),
// Not the event we waited on.
Some(_) => continue,
None => return Err(WaitError::ImportListenerClosed(hash)),
}
}
}
}
}
pub enum BlockCheckStatus {
/// Block is in chain
InChain,
/// Block status is unknown, listener can be used to wait for notification
Unknown(ImportNotifications<PBlock>),
}
// Helper function to check if a block is in chain.
pub fn check_block_in_chain<Client>(
backend: Arc<FullBackend>,
client: Arc<Client>,
hash: PHash,
) -> Result<BlockCheckStatus, WaitError>
where
Client: BlockchainEvents<PBlock>,
{
let _lock = backend.get_import_lock().read();
let block_id = BlockId::Hash(hash);
match backend.blockchain().status(block_id) {
Ok(BlockStatus::InChain) => return Ok(BlockCheckStatus::InChain),
Err(err) => return Err(WaitError::BlockchainError(hash, err)),
_ => {},
}
let listener = client.import_notification_stream();
Ok(BlockCheckStatus::Unknown(listener))
}
/// Builder for a concrete relay chain interface, created from a full node. Builds
/// a [`RelayChainLocal`] to access relay chain data necessary for parachain operation.
///
/// The builder takes a [`polkadot_client::Client`]
/// that wraps a concrete instance. By using [`polkadot_client::ExecuteWithClient`]
/// the builder gets access to this concrete instance and instantiates a [`RelayChainLocal`] with it.
struct RelayChainLocalBuilder {
polkadot_client: polkadot_client::Client,
backend: Arc<FullBackend>,
sync_oracle: Arc<Mutex<Box<dyn SyncOracle + Send + Sync>>>,
overseer_handle: Option<Handle>,
}
impl RelayChainLocalBuilder {
pub fn build(self) -> Arc<dyn RelayChainInterface> {
self.polkadot_client.clone().execute_with(self)
}
}
impl ExecuteWithClient for RelayChainLocalBuilder {
type Output = Arc<dyn RelayChainInterface>;
fn execute_with_client<Client, Api, Backend>(self, client: Arc<Client>) -> Self::Output
where
Client: ProvideRuntimeApi<PBlock>
+ BlockchainEvents<PBlock>
+ AuxStore
+ UsageProvider<PBlock>
+ 'static
+ Sync
+ Send,
Client::Api: ParachainHost<PBlock> + BabeApi<PBlock>,
{
Arc::new(RelayChainLocal::new(client, self.backend, self.sync_oracle, self.overseer_handle))
}
}
/// Build the Polkadot full node using the given `config`.
#[sc_tracing::logging::prefix_logs_with("Relaychain")]
fn build_polkadot_full_node(
config: Configuration,
telemetry_worker_handle: Option<TelemetryWorkerHandle>,
) -> Result<(NewFull<polkadot_client::Client>, CollatorPair), polkadot_service::Error> {
let is_light = matches!(config.role, Role::Light);
if is_light {
Err(polkadot_service::Error::Sub("Light client not supported.".into()))
} else {
let collator_key = CollatorPair::generate().0;
let relay_chain_full_node = polkadot_service::build_full(
config,
polkadot_service::IsCollator::Yes(collator_key.clone()),
None,
true,
None,
telemetry_worker_handle,
polkadot_service::RealOverseerGen,
)?;
Ok((relay_chain_full_node, collator_key))
}
}
/// Builds a relay chain interface by constructing a full relay chain node
pub fn build_relay_chain_interface(
polkadot_config: Configuration,
telemetry_worker_handle: Option<TelemetryWorkerHandle>,
task_manager: &mut TaskManager,
) -> Result<(Arc<(dyn RelayChainInterface + 'static)>, CollatorPair), polkadot_service::Error> {
let (full_node, collator_key) =
build_polkadot_full_node(polkadot_config, telemetry_worker_handle).map_err(
|e| match e {
polkadot_service::Error::Sub(x) => x,
s => format!("{}", s).into(),
},
)?;
let sync_oracle: Box<dyn SyncOracle + Send + Sync> = Box::new(full_node.network.clone());
let sync_oracle = Arc::new(Mutex::new(sync_oracle));
let relay_chain_interface_builder = RelayChainLocalBuilder {
polkadot_client: full_node.client.clone(),
backend: full_node.backend.clone(),
sync_oracle,
overseer_handle: full_node.overseer_handle.clone(),
};
task_manager.add_child(full_node.task_manager);
Ok((relay_chain_interface_builder.build(), collator_key))
}
#[cfg(test)]
mod tests {
use parking_lot::Mutex;
use super::*;
use polkadot_primitives::v1::Block as PBlock;
use polkadot_test_client::{
construct_transfer_extrinsic, BlockBuilderExt, Client, ClientBlockImportExt,
DefaultTestClientBuilderExt, ExecutionStrategy, InitPolkadotBlockBuilder,
TestClientBuilder, TestClientBuilderExt,
};
use sc_service::Arc;
use sp_consensus::{BlockOrigin, SyncOracle};
use sp_runtime::traits::Block as BlockT;
use futures::{executor::block_on, poll, task::Poll};
struct DummyNetwork {}
impl SyncOracle for DummyNetwork {
fn is_major_syncing(&mut self) -> bool {
unimplemented!("Not needed for test")
}
fn is_offline(&mut self) -> bool {
unimplemented!("Not needed for test")
}
}
fn build_client_backend_and_block() -> (Arc<Client>, PBlock, RelayChainLocal<Client>) {
let builder =
TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::NativeWhenPossible);
let backend = builder.backend();
let client = Arc::new(builder.build());
let block_builder = client.init_polkadot_block_builder();
let block = block_builder.build().expect("Finalizes the block").block;
let dummy_network: Box<dyn SyncOracle + Sync + Send> = Box::new(DummyNetwork {});
(
client.clone(),
block,
RelayChainLocal::new(
client,
backend.clone(),
Arc::new(Mutex::new(dummy_network)),
None,
),
)
}
#[test]
fn returns_directly_for_available_block() {
let (mut client, block, relay_chain_interface) = build_client_backend_and_block();
let hash = block.hash();
block_on(client.import(BlockOrigin::Own, block)).expect("Imports the block");
block_on(async move {
// Should be ready on the first poll
assert!(matches!(
poll!(relay_chain_interface.wait_for_block(hash)),
Poll::Ready(Ok(()))
));
});
}
#[test]
fn resolve_after_block_import_notification_was_received() {
let (mut client, block, relay_chain_interface) = build_client_backend_and_block();
let hash = block.hash();
block_on(async move {
let mut future = relay_chain_interface.wait_for_block(hash);
// As the block is not yet imported, the first poll should return `Pending`
assert!(poll!(&mut future).is_pending());
// Import the block that should fire the notification
client.import(BlockOrigin::Own, block).await.expect("Imports the block");
// Now it should have received the notification and report that the block was imported
assert!(matches!(poll!(future), Poll::Ready(Ok(()))));
});
}
#[test]
fn wait_for_block_time_out_when_block_is_not_imported() {
let (_, block, relay_chain_interface) = build_client_backend_and_block();
let hash = block.hash();
assert!(matches!(
block_on(relay_chain_interface.wait_for_block(hash)),
Err(WaitError::Timeout(_))
));
}
#[test]
fn do_not_resolve_after_different_block_import_notification_was_received() {
let (mut client, block, relay_chain_interface) = build_client_backend_and_block();
let hash = block.hash();
let ext = construct_transfer_extrinsic(
&*client,
sp_keyring::Sr25519Keyring::Alice,
sp_keyring::Sr25519Keyring::Bob,
1000,
);
let mut block_builder = client.init_polkadot_block_builder();
// Push an extrinsic to get a different block hash.
block_builder.push_polkadot_extrinsic(ext).expect("Push extrinsic");
let block2 = block_builder.build().expect("Build second block").block;
let hash2 = block2.hash();
block_on(async move {
let mut future = relay_chain_interface.wait_for_block(hash);
let mut future2 = relay_chain_interface.wait_for_block(hash2);
// As the block is not yet imported, the first poll should return `Pending`
assert!(poll!(&mut future).is_pending());
assert!(poll!(&mut future2).is_pending());
// Import the block that should fire the notification
client.import(BlockOrigin::Own, block2).await.expect("Imports the second block");
// The import notification of the second block should not make this one finish
assert!(poll!(&mut future).is_pending());
// Now it should have received the notification and report that the block was imported
assert!(matches!(poll!(future2), Poll::Ready(Ok(()))));
client.import(BlockOrigin::Own, block).await.expect("Imports the first block");
// Now it should be ready
assert!(matches!(poll!(future), Poll::Ready(Ok(()))));
});
}
}
+2 -2
View File
@@ -2,13 +2,14 @@
name = "cumulus-client-service"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Cumulus dependencies
cumulus-client-consensus-common = { path = "../consensus/common" }
cumulus-client-collator = { path = "../collator" }
cumulus-client-pov-recovery = { path = "../pov-recovery" }
cumulus-relay-chain-interface = { path = "../relay-chain-interface" }
cumulus-primitives-core = { path = "../../primitives/core" }
# Substrate dependencies
@@ -27,7 +28,6 @@ sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "mas
# Polkadot dependencies
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-overseer = { git = "https://github.com/paritytech/polkadot", branch = "master" }
# Other deps
+7 -2
View File
@@ -21,21 +21,26 @@ use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero
/// Generate the genesis block from a given ChainSpec.
pub fn generate_genesis_block<Block: BlockT>(
chain_spec: &Box<dyn ChainSpec>,
genesis_state_version: sp_runtime::StateVersion,
) -> Result<Block, String> {
let storage = chain_spec.build_storage()?;
let child_roots = storage.children_default.iter().map(|(sk, child_content)| {
let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
child_content.data.clone().into_iter().collect(),
genesis_state_version,
);
(sk.clone(), state_root.encode())
});
let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
storage.top.clone().into_iter().chain(child_roots).collect(),
genesis_state_version,
);
let extrinsics_root =
<<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(Vec::new());
let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
Vec::new(),
sp_runtime::StateVersion::V0,
);
Ok(Block::new(
<<Block as BlockT>::Header as HeaderT>::new(
+55 -182
View File
@@ -20,9 +20,8 @@
use cumulus_client_consensus_common::ParachainConsensus;
use cumulus_primitives_core::{CollectCollationInfo, ParaId};
use polkadot_overseer::Handle as OverseerHandle;
use polkadot_primitives::v1::{Block as PBlock, CollatorPair};
use polkadot_service::{AbstractClient, Client as PClient, ClientHandle, RuntimeApiCollection};
use cumulus_relay_chain_interface::RelayChainInterface;
use polkadot_primitives::v1::CollatorPair;
use sc_client_api::{
Backend as BackendT, BlockBackend, BlockchainEvents, Finalizer, UsageProvider,
};
@@ -30,47 +29,32 @@ use sc_consensus::{
import_queue::{ImportQueue, IncomingBlock, Link, Origin},
BlockImport,
};
use sc_service::{Configuration, Role, TaskManager};
use sc_telemetry::TelemetryWorkerHandle;
use sc_service::{Configuration, TaskManager};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_consensus::BlockOrigin;
use sp_core::{traits::SpawnNamed, Pair};
use sp_core::traits::SpawnNamed;
use sp_runtime::{
traits::{BlakeTwo256, Block as BlockT, NumberFor},
traits::{Block as BlockT, NumberFor},
Justifications,
};
use std::{marker::PhantomData, ops::Deref, sync::Arc};
use std::{sync::Arc, time::Duration};
pub mod genesis;
/// The relay chain full node handle.
pub struct RFullNode<C> {
/// The relay chain full node handles.
pub relay_chain_full_node: polkadot_service::NewFull<C>,
/// The collator key used by the node.
pub collator_key: CollatorPair,
}
impl<C> Deref for RFullNode<C> {
type Target = polkadot_service::NewFull<C>;
fn deref(&self) -> &Self::Target {
&self.relay_chain_full_node
}
}
/// Parameters given to [`start_collator`].
pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Spawner, RClient, IQ> {
pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, RCInterface, Spawner, IQ> {
pub block_status: Arc<BS>,
pub client: Arc<Client>,
pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
pub spawner: Spawner,
pub para_id: ParaId,
pub relay_chain_full_node: RFullNode<RClient>,
pub relay_chain_interface: RCInterface,
pub task_manager: &'a mut TaskManager,
pub parachain_consensus: Box<dyn ParachainConsensus<Block>>,
pub import_queue: IQ,
pub collator_key: CollatorPair,
pub slot_duration: Duration,
}
/// Start a collator node for a parachain.
@@ -78,7 +62,7 @@ pub struct StartCollatorParams<'a, Block: BlockT, BS, Client, Spawner, RClient,
/// A collator is similar to a validator in a normal blockchain.
/// It is responsible for producing blocks and sending the blocks to a
/// parachain validator for validation and inclusion into the relay chain.
pub async fn start_collator<'a, Block, BS, Client, Backend, Spawner, RClient, IQ>(
pub async fn start_collator<'a, Block, BS, Client, Backend, RCInterface, Spawner, IQ>(
StartCollatorParams {
block_status,
client,
@@ -86,10 +70,12 @@ pub async fn start_collator<'a, Block, BS, Client, Backend, Spawner, RClient, IQ
spawner,
para_id,
task_manager,
relay_chain_full_node,
relay_chain_interface,
parachain_consensus,
import_queue,
}: StartCollatorParams<'a, Block, BS, Client, Spawner, RClient, IQ>,
collator_key,
slot_duration,
}: StartCollatorParams<'a, Block, BS, Client, RCInterface, Spawner, IQ>,
) -> sc_service::error::Result<()>
where
Block: BlockT,
@@ -106,55 +92,58 @@ where
Client::Api: CollectCollationInfo<Block>,
for<'b> &'b Client: BlockImport<Block>,
Spawner: SpawnNamed + Clone + Send + Sync + 'static,
RClient: ClientHandle,
RCInterface: RelayChainInterface + Clone + 'static,
Backend: BackendT<Block> + 'static,
IQ: ImportQueue<Block> + 'static,
{
relay_chain_full_node.client.execute_with(StartConsensus {
let consensus = cumulus_client_consensus_common::run_parachain_consensus(
para_id,
announce_block: announce_block.clone(),
client: client.clone(),
task_manager,
_phantom: PhantomData,
});
client.clone(),
relay_chain_interface.clone(),
announce_block.clone(),
);
relay_chain_full_node.client.execute_with(StartPoVRecovery {
para_id,
client: client.clone(),
import_queue,
task_manager,
overseer_handle: relay_chain_full_node
.overseer_handle
.clone()
task_manager
.spawn_essential_handle()
.spawn("cumulus-consensus", None, consensus);
let pov_recovery = cumulus_client_pov_recovery::PoVRecovery::new(
relay_chain_interface
.overseer_handle()
.ok_or_else(|| "Polkadot full node did not provide an `OverseerHandle`!")?,
_phantom: PhantomData,
})?;
slot_duration,
client.clone(),
import_queue,
relay_chain_interface.clone(),
para_id,
);
task_manager
.spawn_essential_handle()
.spawn("cumulus-pov-recovery", None, pov_recovery.run());
cumulus_client_collator::start_collator(cumulus_client_collator::StartCollatorParams {
runtime_api: client.clone(),
block_status,
announce_block,
overseer_handle: relay_chain_full_node
.overseer_handle
.clone()
overseer_handle: relay_chain_interface
.overseer_handle()
.ok_or_else(|| "Polkadot full node did not provide an `OverseerHandle`!")?,
spawner,
para_id,
key: relay_chain_full_node.collator_key.clone(),
key: collator_key,
parachain_consensus,
})
.await;
task_manager.add_child(relay_chain_full_node.relay_chain_full_node.task_manager);
Ok(())
}
/// Parameters given to [`start_full_node`].
pub struct StartFullNodeParams<'a, Block: BlockT, Client, PClient> {
pub struct StartFullNodeParams<'a, Block: BlockT, Client, RCInterface> {
pub para_id: ParaId,
pub client: Arc<Client>,
pub relay_chain_full_node: RFullNode<PClient>,
pub relay_chain_interface: RCInterface,
pub task_manager: &'a mut TaskManager,
pub announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
}
@@ -163,14 +152,14 @@ pub struct StartFullNodeParams<'a, Block: BlockT, Client, PClient> {
///
/// A full node will only sync the given parachain and will follow the
/// tip of the chain.
pub fn start_full_node<Block, Client, Backend, PClient>(
pub fn start_full_node<Block, Client, Backend, RCInterface>(
StartFullNodeParams {
client,
announce_block,
task_manager,
relay_chain_full_node,
relay_chain_interface,
para_id,
}: StartFullNodeParams<Block, Client, PClient>,
}: StartFullNodeParams<Block, Client, RCInterface>,
) -> sc_service::error::Result<()>
where
Block: BlockT,
@@ -183,112 +172,22 @@ where
+ 'static,
for<'a> &'a Client: BlockImport<Block>,
Backend: BackendT<Block> + 'static,
PClient: ClientHandle,
RCInterface: RelayChainInterface + Clone + 'static,
{
relay_chain_full_node.client.execute_with(StartConsensus {
announce_block,
let consensus = cumulus_client_consensus_common::run_parachain_consensus(
para_id,
client,
task_manager,
_phantom: PhantomData,
});
client.clone(),
relay_chain_interface.clone(),
announce_block,
);
task_manager.add_child(relay_chain_full_node.relay_chain_full_node.task_manager);
task_manager
.spawn_essential_handle()
.spawn("cumulus-consensus", None, consensus);
Ok(())
}
struct StartConsensus<'a, Block: BlockT, Client, Backend> {
para_id: ParaId,
announce_block: Arc<dyn Fn(Block::Hash, Option<Vec<u8>>) + Send + Sync>,
client: Arc<Client>,
task_manager: &'a mut TaskManager,
_phantom: PhantomData<Backend>,
}
impl<'a, Block, Client, Backend> polkadot_service::ExecuteWithClient
for StartConsensus<'a, Block, Client, Backend>
where
Block: BlockT,
Client: Finalizer<Block, Backend>
+ UsageProvider<Block>
+ Send
+ Sync
+ BlockBackend<Block>
+ BlockchainEvents<Block>
+ 'static,
for<'b> &'b Client: BlockImport<Block>,
Backend: BackendT<Block> + 'static,
{
type Output = ();
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
<Api as sp_api::ApiExt<PBlock>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
PBackend: sc_client_api::Backend<PBlock>,
PBackend::State: sp_api::StateBackend<BlakeTwo256>,
Api: RuntimeApiCollection<StateBackend = PBackend::State>,
PClient: AbstractClient<PBlock, PBackend, Api = Api> + 'static,
{
let consensus = cumulus_client_consensus_common::run_parachain_consensus(
self.para_id,
self.client.clone(),
client.clone(),
self.announce_block,
);
self.task_manager.spawn_essential_handle().spawn("cumulus-consensus", consensus);
}
}
struct StartPoVRecovery<'a, Block: BlockT, Client, IQ> {
para_id: ParaId,
client: Arc<Client>,
task_manager: &'a mut TaskManager,
overseer_handle: OverseerHandle,
import_queue: IQ,
_phantom: PhantomData<Block>,
}
impl<'a, Block, Client, IQ> polkadot_service::ExecuteWithClient
for StartPoVRecovery<'a, Block, Client, IQ>
where
Block: BlockT,
Client: UsageProvider<Block>
+ Send
+ Sync
+ BlockBackend<Block>
+ BlockchainEvents<Block>
+ 'static,
IQ: ImportQueue<Block> + 'static,
{
type Output = sc_service::error::Result<()>;
fn execute_with_client<PClient, Api, PBackend>(self, client: Arc<PClient>) -> Self::Output
where
<Api as sp_api::ApiExt<PBlock>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
PBackend: sc_client_api::Backend<PBlock>,
PBackend::State: sp_api::StateBackend<BlakeTwo256>,
Api: RuntimeApiCollection<StateBackend = PBackend::State>,
PClient: AbstractClient<PBlock, PBackend, Api = Api> + 'static,
{
let pov_recovery = cumulus_client_pov_recovery::PoVRecovery::new(
self.overseer_handle,
sc_consensus_babe::Config::get_or_compute(&*client)?.slot_duration(),
self.client,
self.import_queue,
client,
self.para_id,
);
self.task_manager
.spawn_essential_handle()
.spawn("cumulus-pov-recovery", pov_recovery.run());
Ok(())
}
}
/// Prepare the parachain's node condifugration
///
/// This function will disable the default announcement of Substrate for the parachain in favor
@@ -299,32 +198,6 @@ pub fn prepare_node_config(mut parachain_config: Configuration) -> Configuration
parachain_config
}
/// Build the Polkadot full node using the given `config`.
#[sc_tracing::logging::prefix_logs_with("Relaychain")]
pub fn build_polkadot_full_node(
config: Configuration,
telemetry_worker_handle: Option<TelemetryWorkerHandle>,
) -> Result<RFullNode<PClient>, polkadot_service::Error> {
let is_light = matches!(config.role, Role::Light);
if is_light {
Err(polkadot_service::Error::Sub("Light client not supported.".into()))
} else {
let collator_key = CollatorPair::generate().0;
let relay_chain_full_node = polkadot_service::build_full(
config,
polkadot_service::IsCollator::Yes(collator_key.clone()),
None,
true,
None,
telemetry_worker_handle,
polkadot_service::RealOverseerGen,
)?;
Ok(RFullNode { relay_chain_full_node, collator_key })
}
}
/// A shared import queue
///
/// This is basically a hack until the Substrate side is implemented properly.
+2
View File
@@ -36,6 +36,8 @@ RUN apt-get update && \
# add polkadot-collator binary to the docker image
COPY ./target/release/polkadot-collator /usr/local/bin
COPY ./target/release/polkadot-collator.asc /usr/local/bin
COPY ./target/release/polkadot-collator.sha256 /usr/local/bin
COPY ./polkadot-parachains/res/*.json /specs/
USER polkadot
@@ -0,0 +1,36 @@
# This file is sourced from https://github.com/paritytech/polkadot/blob/master/scripts/dockerfiles/polkadot/polkadot_builder.Dockerfile
# This is the build stage for Polkadot-collator. Here we create the binary in a temporary image.
FROM docker.io/paritytech/ci-linux:production as builder
WORKDIR /cumulus
COPY . /cumulus
RUN cargo build --release --locked -p polkadot-collator
# This is the 2nd stage: a very small image where we copy the Polkadot binary."
FROM docker.io/library/ubuntu:20.04
LABEL io.parity.image.type="builder" \
io.parity.image.authors="devops-team@parity.io" \
io.parity.image.vendor="Parity Technologies" \
io.parity.image.description="Multistage Docker image for Polkadot-collator" \
io.parity.image.source="https://github.com/paritytech/polkadot/blob/${VCS_REF}/docker/test-parachain-collator.dockerfile" \
io.parity.image.documentation="https://github.com/paritytech/cumulus"
COPY --from=builder /cumulus/target/release/polkadot-collator /usr/local/bin
RUN useradd -m -u 1000 -U -s /bin/sh -d /cumulus polkadot-collator && \
mkdir -p /data /cumulus/.local/share && \
chown -R polkadot-collator:polkadot-collator /data && \
ln -s /data /cumulus/.local/share/polkadot-collator && \
# unclutter and minimize the attack surface
rm -rf /usr/bin /usr/sbin && \
# check if executable works in this container
/usr/local/bin/polkadot-collator --version
USER polkadot-collator
EXPOSE 30333 9933 9944 9615
VOLUME ["/data"]
ENTRYPOINT ["/usr/local/bin/polkadot-collator"]
+2 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
OWNER=parity
IMAGE_NAME=polkadot-collator
OWNER=${OWNER:-parity}
IMAGE_NAME=${IMAGE_NAME:-polkadot-collator}
docker build --no-cache --build-arg IMAGE_NAME=$IMAGE_NAME -t $OWNER/$IMAGE_NAME -f ./docker/injected.Dockerfile .
docker images | grep $IMAGE_NAME
+4 -19
View File
@@ -1,23 +1,8 @@
FROM rust:buster as builder
# This file is sourced from https://github.com/paritytech/polkadot/blob/master/scripts/dockerfiles/polkadot/polkadot_builder.Dockerfile
FROM docker.io/paritytech/ci-linux:production as builder
RUN apt-get update && apt-get install time clang libclang-dev llvm -y
RUN rustup toolchain install nightly
RUN rustup target add wasm32-unknown-unknown --toolchain nightly
RUN command -v wasm-gc || cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force
WORKDIR /paritytech/cumulus
# Ideally, we could just do something like `COPY . .`, but that doesn't work:
# it busts the cache every time non-source files like inject_bootnodes.sh change,
# as well as when non-`.dockerignore`'d transient files (*.log and friends)
# show up. There is no way to exclude particular files, or write a negative
# rule, using Docker's COPY syntax, which derives from go's filepath.Match rules.
#
# We can't combine these into a single big COPY operation like
# `COPY collator consensus network runtime test Cargo.* .`, because in that case
# docker will copy the _contents_ of each directory into the image workdir,
# not the actual directory. We're stuck just enumerating them.
COPY . .
WORKDIR /cumulus
COPY . /cumulus
RUN cargo build --release --locked -p polkadot-collator
-52
View File
@@ -1,52 +0,0 @@
[package]
name = "pallet-asset-tx-payment"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/cumulus/"
description = "pallet to manage transaction payments in assets"
readme = "README.md"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
# 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 }
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 }
# Other dependencies
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true }
[dev-dependencies]
smallvec = "1.4.1"
serde_json = "1.0.41"
pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = ["std"]
std = [
"serde",
"codec/std",
"sp-std/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"sp-io/std",
"sp-core/std",
"pallet-transaction-payment/std",
]
try-runtime = ["frame-support/try-runtime"]
-21
View File
@@ -1,21 +0,0 @@
# pallet-asset-tx-payment
## Asset Transaction Payment Pallet
This pallet allows runtimes that include it to pay for transactions in assets other than the
native token of the chain.
### Overview
It does this by extending transactions to include an optional `AssetId` that specifies the asset
to be used for payment (defaulting to the native token on `None`). It expects an
[`OnChargeAssetTransaction`] implementation analogously to [`pallet-transaction-payment`]. The
included [`FungiblesAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the fee
amount by converting the fee calculated by [`pallet-transaction-payment`] into the desired
asset.
### Integration
This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means
you should include both pallets in your `construct_runtime` macro, but only include this
pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]).
License: Apache-2.0
-295
View File
@@ -1,295 +0,0 @@
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Asset Transaction Payment Pallet
//!
//! This pallet allows runtimes that include it to pay for transactions in assets other than the
//! main token of the chain.
//!
//! ## Overview
//! It does this by extending transactions to include an optional `AssetId` that specifies the asset
//! to be used for payment (defaulting to the native token on `None`). It expects an
//! [`OnChargeAssetTransaction`] implementation analogously to [`pallet-transaction-payment`]. The
//! included [`FungiblesAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the fee
//! amount by converting the fee calculated by [`pallet-transaction-payment`] into the desired
//! asset.
//!
//! ## Integration
//! This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means
//! you should include both pallets in your `construct_runtime` macro, but only include this
//! pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]).
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::prelude::*;
use codec::{Decode, Encode};
use frame_support::{
dispatch::DispatchResult,
traits::{
tokens::{
fungibles::{Balanced, CreditOf, Inspect},
WithdrawConsequence,
},
IsType,
},
weights::{DispatchInfo, PostDispatchInfo},
DefaultNoBound,
};
use pallet_transaction_payment::OnChargeTransaction;
use scale_info::TypeInfo;
use sp_runtime::{
traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero},
transaction_validity::{
InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
},
FixedPointOperand,
};
#[cfg(test)]
mod tests;
mod payment;
pub use payment::*;
// Type aliases used for interaction with `OnChargeTransaction`.
pub(crate) type OnChargeTransactionOf<T> =
<T as pallet_transaction_payment::Config>::OnChargeTransaction;
// Balance type alias.
pub(crate) type BalanceOf<T> = <OnChargeTransactionOf<T> as OnChargeTransaction<T>>::Balance;
// Liquity info type alias.
pub(crate) type LiquidityInfoOf<T> =
<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::LiquidityInfo;
// Type alias used for interaction with fungibles (assets).
// Balance type alias.
pub(crate) type AssetBalanceOf<T> =
<<T as Config>::Fungibles as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
/// Asset id type alias.
pub(crate) type AssetIdOf<T> =
<<T as Config>::Fungibles as Inspect<<T as frame_system::Config>::AccountId>>::AssetId;
// Type aliases used for interaction with `OnChargeAssetTransaction`.
// Balance type alias.
pub(crate) type ChargeAssetBalanceOf<T> =
<<T as Config>::OnChargeAssetTransaction as OnChargeAssetTransaction<T>>::Balance;
// Asset id type alias.
pub(crate) type ChargeAssetIdOf<T> =
<<T as Config>::OnChargeAssetTransaction as OnChargeAssetTransaction<T>>::AssetId;
// Liquity info type alias.
pub(crate) type ChargeAssetLiquidityOf<T> =
<<T as Config>::OnChargeAssetTransaction as OnChargeAssetTransaction<T>>::LiquidityInfo;
/// Used to pass the initial payment info from pre- to post-dispatch.
#[derive(Encode, Decode, DefaultNoBound, TypeInfo)]
pub enum InitialPayment<T: Config> {
/// No initial fee was payed.
Nothing,
/// The initial fee was payed in the native currency.
Native(LiquidityInfoOf<T>),
/// The initial fee was payed in an asset.
Asset(CreditOf<T::AccountId, T::Fungibles>),
}
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
/// The fungibles instance used to pay for transactions in assets.
type Fungibles: Balanced<Self::AccountId>;
/// The actual transaction charging logic that charges the fees.
type OnChargeAssetTransaction: OnChargeAssetTransaction<Self>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
}
/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
/// in the queue. Allows paying via both `Currency` as well as `fungibles::Balanced`.
///
/// Wraps the transaction logic in [`pallet_transaction_payment`] and extends it with assets.
/// An asset id of `None` falls back to the underlying transaction payment via the native currency.
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ChargeAssetTxPayment<T: Config> {
#[codec(compact)]
tip: BalanceOf<T>,
asset_id: Option<ChargeAssetIdOf<T>>,
}
impl<T: Config> ChargeAssetTxPayment<T>
where
T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
AssetBalanceOf<T>: Send + Sync + FixedPointOperand,
BalanceOf<T>: Send + Sync + FixedPointOperand + IsType<ChargeAssetBalanceOf<T>>,
ChargeAssetIdOf<T>: Send + Sync,
CreditOf<T::AccountId, T::Fungibles>: IsType<ChargeAssetLiquidityOf<T>>,
{
/// utility constructor. Used only in client/factory code.
pub fn from(tip: BalanceOf<T>, asset_id: Option<ChargeAssetIdOf<T>>) -> Self {
Self { tip, asset_id }
}
/// Fee withdrawal logic that dispatches to either `OnChargeAssetTransaction` or `OnChargeTransaction`.
fn withdraw_fee(
&self,
who: &T::AccountId,
call: &T::Call,
info: &DispatchInfoOf<T::Call>,
len: usize,
) -> Result<(BalanceOf<T>, InitialPayment<T>), TransactionValidityError> {
let fee = pallet_transaction_payment::Pallet::<T>::compute_fee(len as u32, info, self.tip);
debug_assert!(self.tip <= fee, "tip should be included in the computed fee");
if fee.is_zero() {
Ok((fee, InitialPayment::Nothing))
} else if let Some(asset_id) = self.asset_id {
T::OnChargeAssetTransaction::withdraw_fee(
who,
call,
info,
asset_id,
fee.into(),
self.tip.into(),
)
.map(|i| (fee, InitialPayment::Asset(i.into())))
} else {
<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::withdraw_fee(
who, call, info, fee, self.tip,
)
.map(|i| (fee, InitialPayment::Native(i)))
.map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() })
}
}
}
impl<T: Config> sp_std::fmt::Debug for ChargeAssetTxPayment<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
write!(f, "ChargeAssetTxPayment<{:?}, {:?}>", self.tip, self.asset_id.encode())
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
Ok(())
}
}
impl<T: Config> SignedExtension for ChargeAssetTxPayment<T>
where
T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
AssetBalanceOf<T>: Send + Sync + FixedPointOperand,
BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand + IsType<ChargeAssetBalanceOf<T>>,
ChargeAssetIdOf<T>: Send + Sync,
CreditOf<T::AccountId, T::Fungibles>: IsType<ChargeAssetLiquidityOf<T>>,
{
const IDENTIFIER: &'static str = "ChargeAssetTxPayment";
type AccountId = T::AccountId;
type Call = T::Call;
type AdditionalSigned = ();
type Pre = (
// tip
BalanceOf<T>,
// who paid the fee
Self::AccountId,
// imbalance resulting from withdrawing the fee
InitialPayment<T>,
);
fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {
Ok(())
}
fn validate(
&self,
who: &Self::AccountId,
call: &Self::Call,
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> TransactionValidity {
use pallet_transaction_payment::ChargeTransactionPayment;
let (fee, _) = self.withdraw_fee(who, call, info, len)?;
let priority = ChargeTransactionPayment::<T>::get_priority(info, len, self.tip, fee);
Ok(ValidTransaction { priority, ..Default::default() })
}
fn pre_dispatch(
self,
who: &Self::AccountId,
call: &Self::Call,
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?;
Ok((self.tip, who.clone(), initial_payment))
}
fn post_dispatch(
pre: Self::Pre,
info: &DispatchInfoOf<Self::Call>,
post_info: &PostDispatchInfoOf<Self::Call>,
len: usize,
_result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
let (tip, who, initial_payment) = pre;
let actual_fee = pallet_transaction_payment::Pallet::<T>::compute_actual_fee(
len as u32, info, post_info, tip,
);
match initial_payment {
InitialPayment::Native(already_withdrawn) => {
<OnChargeTransactionOf<T> as OnChargeTransaction<T>>::correct_and_deposit_fee(
&who,
info,
post_info,
actual_fee,
tip,
already_withdrawn,
)?;
},
InitialPayment::Asset(already_withdrawn) => {
T::OnChargeAssetTransaction::correct_and_deposit_fee(
&who,
info,
post_info,
actual_fee.into(),
tip.into(),
already_withdrawn.into(),
)?;
},
InitialPayment::Nothing => {
debug_assert!(
actual_fee.is_zero(),
"actual fee should be zero if initial fee was zero."
);
debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero.");
},
}
Ok(())
}
}
-168
View File
@@ -1,168 +0,0 @@
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///! Traits and default implementation for paying transaction fees in assets.
use super::*;
use crate::Config;
use codec::FullCodec;
use frame_support::{
traits::{
fungibles::{Balanced, CreditOf, Inspect},
tokens::BalanceConversion,
},
unsigned::TransactionValidityError,
};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{
AtLeast32BitUnsigned, DispatchInfoOf, MaybeSerializeDeserialize, One, PostDispatchInfoOf,
},
transaction_validity::InvalidTransaction,
};
use sp_std::{fmt::Debug, marker::PhantomData};
/// Handle withdrawing, refunding and depositing of transaction fees.
pub trait OnChargeAssetTransaction<T: Config> {
/// The underlying integer type in which fees are calculated.
type Balance: AtLeast32BitUnsigned
+ FullCodec
+ Copy
+ MaybeSerializeDeserialize
+ Debug
+ Default
+ TypeInfo;
/// The type used to identify the assets used for transaction payment.
type AssetId: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo;
/// The type used to store the intermediate values between pre- and post-dispatch.
type LiquidityInfo;
/// Before the transaction is executed the payment of the transaction fees needs to be secured.
///
/// Note: The `fee` already includes the `tip`.
fn withdraw_fee(
who: &T::AccountId,
call: &T::Call,
dispatch_info: &DispatchInfoOf<T::Call>,
asset_id: Self::AssetId,
fee: Self::Balance,
tip: Self::Balance,
) -> Result<Self::LiquidityInfo, TransactionValidityError>;
/// After the transaction was executed the actual fee can be calculated.
/// This function should refund any overpaid fees and optionally deposit
/// the corrected amount.
///
/// Note: The `fee` already includes the `tip`.
fn correct_and_deposit_fee(
who: &T::AccountId,
dispatch_info: &DispatchInfoOf<T::Call>,
post_info: &PostDispatchInfoOf<T::Call>,
corrected_fee: Self::Balance,
tip: Self::Balance,
already_withdrawn: Self::LiquidityInfo,
) -> Result<(), TransactionValidityError>;
}
/// Allows specifying what to do with the withdrawn asset fees.
pub trait HandleCredit<AccountId, B: Balanced<AccountId>> {
/// Implement to determine what to do with the withdrawn asset fees.
/// Default for `CreditOf` from the assets pallet is to burn and
/// decrease total issuance.
fn handle_credit(credit: CreditOf<AccountId, B>);
}
/// Default implementation that just drops the credit according to the `OnDrop` in the underlying
/// imbalance type.
impl<A, B: Balanced<A>> HandleCredit<A, B> for () {
fn handle_credit(_credit: CreditOf<A, B>) {}
}
/// Implements the asset transaction for a balance to asset converter (implementing
/// [`BalanceConversion`]) and a credit handler (implementing [`HandleCredit`]).
///
/// The credit handler is given the complete fee in terms of the asset used for the transaction.
pub struct FungiblesAdapter<CON, HC>(PhantomData<(CON, HC)>);
/// Default implementation for a runtime instantiating this pallet, a balance to asset converter and
/// a credit handler.
impl<T, CON, HC> OnChargeAssetTransaction<T> for FungiblesAdapter<CON, HC>
where
T: Config,
CON: BalanceConversion<BalanceOf<T>, AssetIdOf<T>, AssetBalanceOf<T>>,
HC: HandleCredit<T::AccountId, T::Fungibles>,
AssetIdOf<T>: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo,
{
type Balance = BalanceOf<T>;
type AssetId = AssetIdOf<T>;
type LiquidityInfo = CreditOf<T::AccountId, T::Fungibles>;
/// Withdraw the predicted fee from the transaction origin.
///
/// Note: The `fee` already includes the `tip`.
fn withdraw_fee(
who: &T::AccountId,
_call: &T::Call,
_info: &DispatchInfoOf<T::Call>,
asset_id: Self::AssetId,
fee: Self::Balance,
_tip: Self::Balance,
) -> Result<Self::LiquidityInfo, TransactionValidityError> {
// We don't know the precision of the underlying asset. Because the converted fee could be
// less than one (e.g. 0.5) but gets rounded down by integer division we introduce a minimum
// fee.
let min_converted_fee = if fee.is_zero() { Zero::zero() } else { One::one() };
let converted_fee = CON::to_asset_balance(fee, asset_id)
.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?
.max(min_converted_fee);
let can_withdraw = <T::Fungibles as Inspect<T::AccountId>>::can_withdraw(
asset_id.into(),
who,
converted_fee,
);
if !matches!(can_withdraw, WithdrawConsequence::Success) {
return Err(InvalidTransaction::Payment.into())
}
<T::Fungibles as Balanced<T::AccountId>>::withdraw(asset_id.into(), who, converted_fee)
.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))
}
/// Hand the fee and the tip over to the `[HandleCredit]` implementation.
/// Since the predicted fee might have been too high, parts of the fee may be refunded.
///
/// Note: The `corrected_fee` already includes the `tip`.
fn correct_and_deposit_fee(
who: &T::AccountId,
_dispatch_info: &DispatchInfoOf<T::Call>,
_post_info: &PostDispatchInfoOf<T::Call>,
corrected_fee: Self::Balance,
_tip: Self::Balance,
paid: Self::LiquidityInfo,
) -> Result<(), TransactionValidityError> {
let min_converted_fee = if corrected_fee.is_zero() { Zero::zero() } else { One::one() };
// Convert the corrected fee into the asset used for payment.
let converted_fee = CON::to_asset_balance(corrected_fee, paid.asset().into())
.map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() })?
.max(min_converted_fee);
// Calculate how much refund we should return.
let (final_fee, refund) = paid.split(converted_fee);
// Refund to the account that paid the fees. If this fails, the account might have dropped
// below the existential balance. In that case we don't refund anything.
let _ = <T::Fungibles as Balanced<T::AccountId>>::resolve(who, refund);
// Handle the final fee, e.g. by transferring to the block author or burning.
HC::handle_credit(final_fee);
Ok(())
}
}
-636
View File
@@ -1,636 +0,0 @@
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::*;
use crate as pallet_asset_tx_payment;
use frame_support::{
assert_ok,
pallet_prelude::*,
parameter_types,
traits::{fungibles::Mutate, FindAuthor},
weights::{
DispatchClass, DispatchInfo, PostDispatchInfo, Weight, WeightToFeeCoefficient,
WeightToFeeCoefficients, WeightToFeePolynomial,
},
ConsensusEngineId,
};
use frame_system as system;
use frame_system::EnsureRoot;
use pallet_balances::Call as BalancesCall;
use pallet_transaction_payment::CurrencyAdapter;
use smallvec::smallvec;
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, ConvertInto, IdentityLookup, StaticLookup},
Perbill,
};
use std::cell::RefCell;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
type Block = frame_system::mocking::MockBlock<Runtime>;
type Balance = u64;
type AccountId = u64;
frame_support::construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: system::{Pallet, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
TransactionPayment: pallet_transaction_payment::{Pallet, Storage},
Assets: pallet_assets::{Pallet, Call, Storage, Event<T>},
Authorship: pallet_authorship::{Pallet, Call, Storage},
AssetTxPayment: pallet_asset_tx_payment::{Pallet},
}
);
const CALL: &<Runtime as frame_system::Config>::Call =
&Call::Balances(BalancesCall::transfer { dest: 2, value: 69 });
thread_local! {
static EXTRINSIC_BASE_WEIGHT: RefCell<u64> = RefCell::new(0);
}
pub struct BlockWeights;
impl Get<frame_system::limits::BlockWeights> for BlockWeights {
fn get() -> frame_system::limits::BlockWeights {
frame_system::limits::BlockWeights::builder()
.base_block(0)
.for_class(DispatchClass::all(), |weights| {
weights.base_extrinsic = EXTRINSIC_BASE_WEIGHT.with(|v| *v.borrow()).into();
})
.for_class(DispatchClass::non_mandatory(), |weights| {
weights.max_total = 1024.into();
})
.build_or_panic()
}
}
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub static TransactionByteFee: u64 = 1;
pub static WeightToFee: u64 = 1;
}
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = BlockWeights;
type BlockLength = ();
type DbWeight = ();
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Call = Call;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
}
parameter_types! {
pub const ExistentialDeposit: u64 = 10;
pub const MaxReserves: u32 = 50;
}
impl pallet_balances::Config for Runtime {
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type MaxLocks = ();
type WeightInfo = ();
type MaxReserves = MaxReserves;
type ReserveIdentifier = [u8; 8];
}
impl WeightToFeePolynomial for WeightToFee {
type Balance = u64;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
smallvec![WeightToFeeCoefficient {
degree: 1,
coeff_frac: Perbill::zero(),
coeff_integer: WEIGHT_TO_FEE.with(|v| *v.borrow()),
negative: false,
}]
}
}
parameter_types! {
pub const OperationalFeeMultiplier: u8 = 5;
}
impl pallet_transaction_payment::Config for Runtime {
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = WeightToFee;
type FeeMultiplierUpdate = ();
type OperationalFeeMultiplier = OperationalFeeMultiplier;
}
parameter_types! {
pub const AssetDeposit: u64 = 2;
pub const MetadataDeposit: u64 = 0;
pub const StringLimit: u32 = 20;
}
impl pallet_assets::Config for Runtime {
type Event = Event;
type Balance = Balance;
type AssetId = u32;
type Currency = Balances;
type ForceOrigin = EnsureRoot<AccountId>;
type AssetDeposit = AssetDeposit;
type MetadataDepositBase = MetadataDeposit;
type MetadataDepositPerByte = MetadataDeposit;
type ApprovalDeposit = MetadataDeposit;
type StringLimit = StringLimit;
type Freezer = ();
type Extra = ();
type WeightInfo = ();
}
pub struct HardcodedAuthor;
const BLOCK_AUTHOR: AccountId = 1234;
impl FindAuthor<AccountId> for HardcodedAuthor {
fn find_author<'a, I>(_: I) -> Option<AccountId>
where
I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
{
Some(BLOCK_AUTHOR)
}
}
impl pallet_authorship::Config for Runtime {
type FindAuthor = HardcodedAuthor;
type UncleGenerations = ();
type FilterUncle = ();
type EventHandler = ();
}
pub struct CreditToBlockAuthor;
impl HandleCredit<AccountId, Assets> for CreditToBlockAuthor {
fn handle_credit(credit: CreditOf<AccountId, Assets>) {
let author = pallet_authorship::Pallet::<Runtime>::author();
// TODO: what to do in case paying the author fails (e.g. because `fee < min_balance`)
// default: drop the result which will trigger the `OnDrop` of the imbalance.
let _ = <Assets as Balanced<AccountId>>::resolve(&author, credit);
}
}
impl Config for Runtime {
type Fungibles = Assets;
type OnChargeAssetTransaction = FungiblesAdapter<
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto>,
CreditToBlockAuthor,
>;
}
pub struct ExtBuilder {
balance_factor: u64,
base_weight: u64,
byte_fee: u64,
weight_to_fee: u64,
}
impl Default for ExtBuilder {
fn default() -> Self {
Self { balance_factor: 1, base_weight: 0, byte_fee: 1, weight_to_fee: 1 }
}
}
impl ExtBuilder {
pub fn base_weight(mut self, base_weight: u64) -> Self {
self.base_weight = base_weight;
self
}
pub fn balance_factor(mut self, factor: u64) -> Self {
self.balance_factor = factor;
self
}
fn set_constants(&self) {
EXTRINSIC_BASE_WEIGHT.with(|v| *v.borrow_mut() = self.base_weight);
TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee);
WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee);
}
pub fn build(self) -> sp_io::TestExternalities {
self.set_constants();
let mut t = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: if self.balance_factor > 0 {
vec![
(1, 10 * self.balance_factor),
(2, 20 * self.balance_factor),
(3, 30 * self.balance_factor),
(4, 40 * self.balance_factor),
(5, 50 * self.balance_factor),
(6, 60 * self.balance_factor),
]
} else {
vec![]
},
}
.assimilate_storage(&mut t)
.unwrap();
t.into()
}
}
/// create a transaction info struct from weight. Handy to avoid building the whole struct.
pub fn info_from_weight(w: Weight) -> DispatchInfo {
// pays_fee: Pays::Yes -- class: DispatchClass::Normal
DispatchInfo { weight: w, ..Default::default() }
}
fn post_info_from_weight(w: Weight) -> PostDispatchInfo {
PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() }
}
fn info_from_pays(p: Pays) -> DispatchInfo {
DispatchInfo { pays_fee: p, ..Default::default() }
}
fn post_info_from_pays(p: Pays) -> PostDispatchInfo {
PostDispatchInfo { actual_weight: None, pays_fee: p }
}
fn default_post_info() -> PostDispatchInfo {
PostDispatchInfo { actual_weight: None, pays_fee: Default::default() }
}
#[test]
fn transaction_payment_in_native_possible() {
let balance_factor = 100;
ExtBuilder::default()
.balance_factor(balance_factor)
.base_weight(5)
.build()
.execute_with(|| {
let len = 10;
let pre = ChargeAssetTxPayment::<Runtime>::from(0, None)
.pre_dispatch(&1, CALL, &info_from_weight(5), len)
.unwrap();
let initial_balance = 10 * balance_factor;
assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(5),
&default_post_info(),
len,
&Ok(())
));
assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10);
let pre = ChargeAssetTxPayment::<Runtime>::from(5 /* tipped */, None)
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
.unwrap();
let initial_balance_for_2 = 20 * balance_factor;
assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 5);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(100),
&post_info_from_weight(50),
len,
&Ok(())
));
assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 5);
});
}
#[test]
fn transaction_payment_in_asset_possible() {
let base_weight = 5;
let balance_factor = 100;
ExtBuilder::default()
.balance_factor(balance_factor)
.base_weight(base_weight)
.build()
.execute_with(|| {
// create the asset
let asset_id = 1;
let min_balance = 2;
assert_ok!(Assets::force_create(
Origin::root(),
asset_id,
42, /* owner */
true, /* is_sufficient */
min_balance
));
// mint into the caller account
let caller = 1;
let beneficiary = <Runtime as system::Config>::Lookup::unlookup(caller);
let balance = 100;
assert_ok!(Assets::mint_into(asset_id, &beneficiary, balance));
assert_eq!(Assets::balance(asset_id, caller), balance);
let weight = 5;
let len = 10;
// we convert the from weight to fee based on the ratio between asset min balance and
// existential deposit
let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get();
let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.unwrap();
// assert that native balance is not used
assert_eq!(Balances::free_balance(caller), 10 * balance_factor);
// check that fee was charged in the given asset
assert_eq!(Assets::balance(asset_id, caller), balance - fee);
assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), 0);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(weight),
&default_post_info(),
len,
&Ok(())
));
assert_eq!(Assets::balance(asset_id, caller), balance - fee);
// check that the block author gets rewarded
assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), fee);
});
}
#[test]
fn transaction_payment_without_fee() {
let base_weight = 5;
let balance_factor = 100;
ExtBuilder::default()
.balance_factor(balance_factor)
.base_weight(base_weight)
.build()
.execute_with(|| {
// create the asset
let asset_id = 1;
let min_balance = 2;
assert_ok!(Assets::force_create(
Origin::root(),
asset_id,
42, /* owner */
true, /* is_sufficient */
min_balance
));
// mint into the caller account
let caller = 1;
let beneficiary = <Runtime as system::Config>::Lookup::unlookup(caller);
let balance = 100;
assert_ok!(Assets::mint_into(asset_id, &beneficiary, balance));
assert_eq!(Assets::balance(asset_id, caller), balance);
let weight = 5;
let len = 10;
// we convert the from weight to fee based on the ratio between asset min balance and
// existential deposit
let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get();
let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.unwrap();
// assert that native balance is not used
assert_eq!(Balances::free_balance(caller), 10 * balance_factor);
// check that fee was charged in the given asset
assert_eq!(Assets::balance(asset_id, caller), balance - fee);
assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), 0);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(weight),
&post_info_from_pays(Pays::No),
len,
&Ok(())
));
// caller should be refunded
assert_eq!(Assets::balance(asset_id, caller), balance);
// check that the block author did not get rewarded
assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), 0);
});
}
#[test]
fn asset_transaction_payment_with_tip_and_refund() {
let base_weight = 5;
ExtBuilder::default()
.balance_factor(100)
.base_weight(base_weight)
.build()
.execute_with(|| {
// create the asset
let asset_id = 1;
let min_balance = 2;
assert_ok!(Assets::force_create(
Origin::root(),
asset_id,
42, /* owner */
true, /* is_sufficient */
min_balance
));
// mint into the caller account
let caller = 2;
let beneficiary = <Runtime as system::Config>::Lookup::unlookup(caller);
let balance = 1000;
assert_ok!(Assets::mint_into(asset_id, &beneficiary, balance));
assert_eq!(Assets::balance(asset_id, caller), balance);
let weight = 100;
let tip = 5;
let len = 10;
// we convert the from weight to fee based on the ratio between asset min balance and
// existential deposit
let fee_with_tip =
(base_weight + weight + len as u64 + tip) * min_balance / ExistentialDeposit::get();
let pre = ChargeAssetTxPayment::<Runtime>::from(tip, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.unwrap();
assert_eq!(Assets::balance(asset_id, caller), balance - fee_with_tip);
let final_weight = 50;
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(weight),
&post_info_from_weight(final_weight),
len,
&Ok(())
));
let final_fee =
fee_with_tip - (weight - final_weight) * min_balance / ExistentialDeposit::get();
assert_eq!(Assets::balance(asset_id, caller), balance - (final_fee));
assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), final_fee);
});
}
#[test]
fn payment_from_account_with_only_assets() {
let base_weight = 5;
ExtBuilder::default()
.balance_factor(100)
.base_weight(base_weight)
.build()
.execute_with(|| {
// create the asset
let asset_id = 1;
let min_balance = 2;
assert_ok!(Assets::force_create(
Origin::root(),
asset_id,
42, /* owner */
true, /* is_sufficient */
min_balance
));
// mint into the caller account
let caller = 333;
let beneficiary = <Runtime as system::Config>::Lookup::unlookup(caller);
let balance = 100;
assert_ok!(Assets::mint_into(asset_id, &beneficiary, balance));
assert_eq!(Assets::balance(asset_id, caller), balance);
// assert that native balance is not necessary
assert_eq!(Balances::free_balance(caller), 0);
let weight = 5;
let len = 10;
// we convert the from weight to fee based on the ratio between asset min balance and
// existential deposit
let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get();
let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.unwrap();
assert_eq!(Balances::free_balance(caller), 0);
// check that fee was charged in the given asset
assert_eq!(Assets::balance(asset_id, caller), balance - fee);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(weight),
&default_post_info(),
len,
&Ok(())
));
assert_eq!(Assets::balance(asset_id, caller), balance - fee);
assert_eq!(Balances::free_balance(caller), 0);
});
}
#[test]
fn payment_only_with_existing_sufficient_asset() {
let base_weight = 5;
ExtBuilder::default()
.balance_factor(100)
.base_weight(base_weight)
.build()
.execute_with(|| {
let asset_id = 1;
let caller = 1;
let weight = 5;
let len = 10;
// pre_dispatch fails for non-existent asset
assert!(ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.is_err());
// create the non-sufficient asset
let min_balance = 2;
assert_ok!(Assets::force_create(
Origin::root(),
asset_id,
42, /* owner */
false, /* is_sufficient */
min_balance
));
// pre_dispatch fails for non-sufficient asset
assert!(ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.is_err());
});
}
#[test]
fn converted_fee_is_never_zero_if_input_fee_is_not() {
let base_weight = 1;
ExtBuilder::default()
.balance_factor(100)
.base_weight(base_weight)
.build()
.execute_with(|| {
// create the asset
let asset_id = 1;
let min_balance = 1;
assert_ok!(Assets::force_create(
Origin::root(),
asset_id,
42, /* owner */
true, /* is_sufficient */
min_balance
));
// mint into the caller account
let caller = 333;
let beneficiary = <Runtime as system::Config>::Lookup::unlookup(caller);
let balance = 100;
assert_ok!(Assets::mint_into(asset_id, &beneficiary, balance));
assert_eq!(Assets::balance(asset_id, caller), balance);
let weight = 1;
let len = 1;
// we convert the from weight to fee based on the ratio between asset min balance and
// existential deposit
let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get();
// naive fee calculation would round down to zero
assert_eq!(fee, 0);
{
let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len)
.unwrap();
// `Pays::No` still implies no fees
assert_eq!(Assets::balance(asset_id, caller), balance);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_pays(Pays::No),
&post_info_from_pays(Pays::No),
len,
&Ok(())
));
assert_eq!(Assets::balance(asset_id, caller), balance);
}
let pre = ChargeAssetTxPayment::<Runtime>::from(0, Some(asset_id))
.pre_dispatch(&caller, CALL, &info_from_weight(weight), len)
.unwrap();
// check that at least one coin was charged in the given asset
assert_eq!(Assets::balance(asset_id, caller), balance - 1);
assert_ok!(ChargeAssetTxPayment::<Runtime>::post_dispatch(
pre,
&info_from_weight(weight),
&default_post_info(),
len,
&Ok(())
));
assert_eq!(Assets::balance(asset_id, caller), balance - 1);
});
}
+2 -2
View File
@@ -2,7 +2,7 @@
name = "cumulus-pallet-aura-ext"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
description = "AURA consensus extension pallet for parachains"
[dependencies]
@@ -19,7 +19,7 @@ sp-application-crypto = { git = "https://github.com/paritytech/substrate", defau
# Other Dependencies
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"]}
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
[dev-dependencies]
cumulus-pallet-parachain-system = { path = "../parachain-system" }
+46 -44
View File
@@ -1,62 +1,64 @@
[package]
authors = ['Anonymous']
description = 'Simple staking pallet with a fixed stake.'
edition = '2018'
homepage = 'https://substrate.io'
license = 'Apache-2.0'
name = 'pallet-collator-selection'
readme = 'README.md'
repository = 'https://github.com/paritytech/cumulus/'
version = '3.0.0'
authors = ["Anonymous"]
description = "Simple staking pallet with a fixed stake."
edition = "2021"
homepage = "https://substrate.io"
license = "Apache-2.0"
name = "pallet-collator-selection"
readme = "README.md"
repository = "https://github.com/paritytech/cumulus/"
version = "3.0.0"
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
log = { version = "0.4.0", default-features = false }
codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.3.0' }
codec = { default-features = false, features = ["derive"], package = "parity-scale-codec", version = "2.3.0" }
rand = { version = "0.7.2", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.119", default-features = false }
serde = { version = "1.0.132", default-features = false }
sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-staking = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-authorship = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-session = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
sp-staking = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-authorship = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-session = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
frame-benchmarking = { default-features = false, optional = true, git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "master" }
[dev-dependencies]
sp-core = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-io = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-tracing = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-consensus-aura = { git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-balances = { git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-aura = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = ['std']
default = ["std"]
runtime-benchmarks = [
'frame-benchmarking',
'frame-support/runtime-benchmarks',
'frame-system/runtime-benchmarks',
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
std = [
'codec/std',
'log/std',
'scale-info/std',
'rand/std',
'sp-runtime/std',
'sp-staking/std',
'sp-std/std',
'frame-support/std',
'frame-system/std',
'frame-benchmarking/std',
'pallet-authorship/std',
'pallet-session/std',
"codec/std",
"log/std",
"scale-info/std",
"rand/std",
"sp-runtime/std",
"sp-staking/std",
"sp-std/std",
"frame-support/std",
"frame-system/std",
"frame-benchmarking/std",
"pallet-authorship/std",
"pallet-session/std",
]
try-runtime = [ "frame-support/try-runtime" ]
@@ -88,7 +88,7 @@ fn register_validators<T: Config + session::Config>(count: u32) {
let validators = (0..count).map(|c| validator::<T>(c)).collect::<Vec<_>>();
for (who, keys) in validators {
<session::Module<T>>::set_keys(RawOrigin::Signed(who).into(), keys, vec![]).unwrap();
<session::Pallet<T>>::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()).unwrap();
}
}
@@ -157,10 +157,10 @@ benchmarks! {
let bond: BalanceOf<T> = T::Currency::minimum_balance() * 2u32.into();
T::Currency::make_free_balance_be(&caller, bond.clone());
<session::Module<T>>::set_keys(
<session::Pallet<T>>::set_keys(
RawOrigin::Signed(caller.clone()).into(),
keys::<T>(c + 1),
vec![]
Vec::new()
).unwrap();
}: _(RawOrigin::Signed(caller.clone()))
+11 -10
View File
@@ -78,6 +78,7 @@ impl system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -145,7 +146,7 @@ impl From<UintAuthorityId> for MockSessionKeys {
}
parameter_types! {
pub static SessionHandlerCollators: Vec<u64> = vec![];
pub static SessionHandlerCollators: Vec<u64> = Vec::new();
pub static SessionChangeBlock: u64 = 0;
}
@@ -224,21 +225,21 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
sp_tracing::try_init_simple();
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
let invulnerables = vec![1, 2];
let keys = invulnerables
.iter()
.map(|i| (*i, *i, MockSessionKeys { aura: UintAuthorityId(*i) }))
.collect::<Vec<_>>();
let balances = pallet_balances::GenesisConfig::<Test> {
balances: vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)],
};
let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)];
let keys = balances
.iter()
.map(|&(i, _)| (i, i, MockSessionKeys { aura: UintAuthorityId(i) }))
.collect::<Vec<_>>();
let collator_selection = collator_selection::GenesisConfig::<Test> {
desired_candidates: 2,
candidacy_bond: 10,
invulnerables,
};
let session = pallet_session::GenesisConfig::<Test> { keys };
balances.assimilate_storage(&mut t).unwrap();
pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
.unwrap();
// collator selection must be initialized before session.
collator_selection.assimilate_storage(&mut t).unwrap();
session.assimilate_storage(&mut t).unwrap();
@@ -249,6 +250,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
pub fn initialize_to_block(n: u64) {
for i in System::block_number() + 1..=n {
System::set_block_number(i);
<AllPallets as frame_support::traits::OnInitialize<u64>>::on_initialize(i);
<AllPalletsWithSystem as frame_support::traits::OnInitialize<u64>>::on_initialize(i);
}
}
+1 -1
View File
@@ -188,7 +188,7 @@ fn register_as_candidate_works() {
// given
assert_eq!(CollatorSelection::desired_candidates(), 2);
assert_eq!(CollatorSelection::candidacy_bond(), 10);
assert_eq!(CollatorSelection::candidates(), vec![]);
assert_eq!(CollatorSelection::candidates(), Vec::new());
assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
// take two endowed, non-invulnerables accounts.
+1 -1
View File
@@ -2,7 +2,7 @@
name = "cumulus-pallet-dmp-queue"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Other dependencies
+5 -3
View File
@@ -307,7 +307,7 @@ pub mod pallet {
id, remaining, required,
));
}
}
},
}
}
// Cannot be an `else` here since the `maybe_enqueue_page` may have changed.
@@ -371,6 +371,7 @@ mod tests {
impl_version: 1,
apis: sp_version::create_apis_vec!([]),
transaction_version: 1,
state_version: 1,
};
pub const ParachainId: ParaId = ParaId::new(200);
pub const ReservedXcmpWeight: Weight = 0;
@@ -403,6 +404,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
thread_local! {
@@ -475,7 +477,7 @@ mod tests {
Xcm(vec![Transact {
origin_kind: OriginKind::Native,
require_weight_at_most: weight,
call: vec![].into(),
call: Vec::new().into(),
}])
}
@@ -506,7 +508,7 @@ mod tests {
new_test_ext().execute_with(|| {
let weight_used = handle_messages(&[], 1000);
assert_eq!(weight_used, 0);
assert_eq!(take_trace(), vec![]);
assert_eq!(take_trace(), Vec::new());
assert!(queue_is_empty());
});
}
+2 -2
View File
@@ -2,7 +2,7 @@
name = "cumulus-pallet-parachain-system"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
description = "Base pallet for cumulus-based parachains"
[dependencies]
@@ -32,7 +32,7 @@ sp-externalities = { git = "https://github.com/paritytech/substrate", default-fe
# Other Dependencies
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"]}
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
log = { version = "0.4.14", default-features = false }
environmental = { version = "1.1.2", default-features = false }
@@ -2,15 +2,15 @@
name = "cumulus-pallet-parachain-system-proc-macro"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
description = "Proc macros provided by the parachain-system pallet"
[lib]
proc-macro = true
[dependencies]
syn = "1.0.73"
proc-macro2 = "1.0.27"
syn = "1.0.81"
proc-macro2 = "1.0.36"
quote = "1.0.9"
proc-macro-crate = "1.0.0"
+37 -46
View File
@@ -33,7 +33,7 @@ use cumulus_primitives_core::{
OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, UpwardMessageSender,
XcmpMessageHandler, XcmpMessageSource,
};
use cumulus_primitives_parachain_inherent::ParachainInherentData;
use cumulus_primitives_parachain_inherent::{MessageQueueChain, ParachainInherentData};
use frame_support::{
dispatch::{DispatchError, DispatchResult},
ensure,
@@ -46,7 +46,7 @@ use frame_system::{ensure_none, ensure_root};
use polkadot_parachain::primitives::RelayChainBlockNumber;
use relay_state_snapshot::MessagingStateSnapshot;
use sp_runtime::{
traits::{BlakeTwo256, Block as BlockT, BlockNumberProvider, Hash},
traits::{Block as BlockT, BlockNumberProvider, Hash},
transaction_validity::{
InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
ValidTransaction,
@@ -236,8 +236,9 @@ pub mod pallet {
HrmpWatermark::<T>::kill();
UpwardMessages::<T>::kill();
HrmpOutboundMessages::<T>::kill();
CustomValidationHeadData::<T>::kill();
weight += T::DbWeight::get().writes(5);
weight += T::DbWeight::get().writes(6);
// Here, in `on_initialize` we must report the weight for both `on_initialize` and
// `on_finalize`.
@@ -567,6 +568,12 @@ pub mod pallet {
#[pallet::storage]
pub(super) type AuthorizedUpgrade<T: Config> = StorageValue<_, T::Hash>;
/// A custom head data that should be returned as result of `validate_block`.
///
/// See [`Pallet::set_custom_validation_head_data`] for more information.
#[pallet::storage]
pub(super) type CustomValidationHeadData<T: Config> = StorageValue<_, Vec<u8>, OptionQuery>;
#[pallet::inherent]
impl<T: Config> ProvideInherent for Pallet<T> {
type Call = Call<T>;
@@ -609,7 +616,7 @@ pub mod pallet {
if let Ok(hash) = Self::validate_authorized_upgrade(code) {
return Ok(ValidTransaction {
priority: 100,
requires: vec![],
requires: Vec::new(),
provides: vec![hash.as_ref().to_vec()],
longevity: TransactionLongevity::max_value(),
propagate: true,
@@ -692,6 +699,9 @@ impl<T: Config> Pallet<T> {
/// import, this is a no-op.
///
/// # Panics
///
/// Panics while validating the `PoV` on the relay chain if the [`PersistedValidationData`]
/// passed by the block author was incorrect.
fn validate_validation_data(validation_data: &PersistedValidationData) {
validate_block::with_validation_params(|params| {
assert_eq!(
@@ -714,8 +724,10 @@ impl<T: Config> Pallet<T> {
/// Checks if the sequence of the messages is valid, dispatches them and communicates the
/// number of processed messages to the collator via a storage update.
///
/// **Panics** if it turns out that after processing all messages the Message Queue Chain
/// hash doesn't match the expected.
/// # Panics
///
/// If it turns out that after processing all messages the Message Queue Chain
/// hash doesn't match the expected.
fn process_inbound_downward_messages(
expected_dmq_mqc_head: relay_chain::Hash,
downward_messages: Vec<InboundDownwardMessage>,
@@ -738,7 +750,7 @@ impl<T: Config> Pallet<T> {
weight_used += T::DmpMessageHandler::handle_dmp_messages(message_iter, max_weight);
<LastDmqMqcHead<T>>::put(&dmq_head);
Self::deposit_event(Event::DownwardMessagesProcessed(weight_used, dmq_head.0));
Self::deposit_event(Event::DownwardMessagesProcessed(weight_used, dmq_head.head()));
}
// After hashing each message in the message queue chain submitted by the collator, we
@@ -746,7 +758,7 @@ impl<T: Config> Pallet<T> {
//
// A mismatch means that at least some of the submitted messages were altered, omitted or
// added improperly.
assert_eq!(dmq_head.0, expected_dmq_mqc_head);
assert_eq!(dmq_head.head(), expected_dmq_mqc_head);
ProcessedDownwardMessages::<T>::put(dm_count);
@@ -907,6 +919,22 @@ impl<T: Config> Pallet<T> {
new_validation_code: NewValidationCode::<T>::get().map(Into::into),
}
}
/// Set a custom head data that should be returned as result of `validate_block`.
///
/// This will overwrite the head data that is returned as result of `validate_block` while
/// validating a `PoV` on the relay chain. Normally the head data that is being returned
/// by `validate_block` is the header of the block that is validated, thus it can be
/// enacted as the new best block. However, for features like forking it can be useful
/// to overwrite the head data with a custom header.
///
/// # Attention
///
/// This should only be used when you are sure what you are doing as this can brick
/// your Parachain.
pub fn set_custom_validation_head_data(head_data: Vec<u8>) {
CustomValidationHeadData::<T>::put(head_data);
}
}
pub struct ParachainSetCode<T>(sp_std::marker::PhantomData<T>);
@@ -917,43 +945,6 @@ impl<T: Config> frame_system::SetCode<T> for ParachainSetCode<T> {
}
}
/// This struct provides ability to extend a message queue chain (MQC) and compute a new head.
///
/// MQC is an instance of a [hash chain] applied to a message queue. Using a hash chain it's
/// possible to represent a sequence of messages using only a single hash.
///
/// A head for an empty chain is agreed to be a zero hash.
///
/// [hash chain]: https://en.wikipedia.org/wiki/Hash_chain
#[derive(Default, Clone, codec::Encode, codec::Decode, scale_info::TypeInfo)]
struct MessageQueueChain(relay_chain::Hash);
impl MessageQueueChain {
fn extend_hrmp(&mut self, horizontal_message: &InboundHrmpMessage) -> &mut Self {
let prev_head = self.0;
self.0 = BlakeTwo256::hash_of(&(
prev_head,
horizontal_message.sent_at,
BlakeTwo256::hash_of(&horizontal_message.data),
));
self
}
fn extend_downward(&mut self, downward_message: &InboundDownwardMessage) -> &mut Self {
let prev_head = self.0;
self.0 = BlakeTwo256::hash_of(&(
prev_head,
downward_message.sent_at,
BlakeTwo256::hash_of(&downward_message.msg),
));
self
}
fn head(&self) -> relay_chain::Hash {
self.0
}
}
impl<T: Config> Pallet<T> {
pub fn send_upward_message(message: UpwardMessage) -> Result<u32, MessageSendError> {
// Check if the message fits into the relay-chain constraints.
@@ -1008,7 +999,7 @@ pub trait CheckInherents<Block: BlockT> {
) -> frame_support::inherent::CheckInherentsResult;
}
/// Implements [`BlockNumberProvider`] that returns relaychain block number fetched from
/// Implements [`BlockNumberProvider`] that returns relay chain block number fetched from
/// validation data.
/// NTOE: When validation data is not available (e.g. within on_initialize), 0 will be returned.
pub struct RelaychainBlockNumberProvider<T>(sp_std::marker::PhantomData<T>);
+6 -1
View File
@@ -33,7 +33,10 @@ use frame_system::{InitKind, RawOrigin};
use hex_literal::hex;
use relay_chain::v1::HrmpChannelId;
use sp_core::H256;
use sp_runtime::{testing::Header, traits::IdentityLookup};
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
use sp_version::RuntimeVersion;
use std::cell::RefCell;
@@ -63,6 +66,7 @@ parameter_types! {
impl_version: 1,
apis: sp_version::create_apis_vec!([]),
transaction_version: 1,
state_version: 1,
};
pub const ParachainId: ParaId = ParaId::new(200);
pub const ReservedXcmpWeight: Weight = 0;
@@ -92,6 +96,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ParachainSetCode<Self>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl Config for Test {
type Event = Event;
@@ -17,7 +17,7 @@
//! The actual implementation of the validate block functionality.
use frame_support::traits::{ExecuteBlock, ExtrinsicCall, Get, IsSubType};
use sp_runtime::traits::{Block as BlockT, Extrinsic, HashFor, Header as HeaderT, NumberFor};
use sp_runtime::traits::{Block as BlockT, Extrinsic, HashFor, Header as HeaderT};
use sp_io::KillStorageResult;
use sp_std::prelude::*;
@@ -26,13 +26,13 @@ use polkadot_parachain::primitives::{HeadData, ValidationParams, ValidationResul
use codec::{Decode, Encode};
use sp_core::storage::ChildInfo;
use sp_core::storage::{ChildInfo, StateVersion};
use sp_externalities::{set_and_run_with_externalities, Externalities};
use sp_trie::MemoryDB;
type TrieBackend<B> = sp_state_machine::TrieBackend<MemoryDB<HashFor<B>>, HashFor<B>>;
type Ext<'a, B> = sp_state_machine::Ext<'a, HashFor<B>, NumberFor<B>, TrieBackend<B>>;
type Ext<'a, B> = sp_state_machine::Ext<'a, HashFor<B>, TrieBackend<B>>;
fn with_externalities<F: FnOnce(&mut dyn Externalities) -> R, R>(f: F) -> R {
sp_externalities::with_externalities(f).expect("Environmental externalities not set.")
@@ -68,7 +68,7 @@ where
// Uncompress
let mut db = MemoryDB::default();
let root = match sp_trie::decode_compact::<sp_trie::Layout<HashFor<B>>, _, _>(
let root = match sp_trie::decode_compact::<sp_trie::LayoutV1<HashFor<B>>, _, _>(
&mut db,
storage_proof.iter_compact_encoded_nodes(),
Some(parent_head.state_root()),
@@ -89,7 +89,6 @@ where
sp_io::storage::host_clear.replace_implementation(host_storage_clear),
sp_io::storage::host_root.replace_implementation(host_storage_root),
sp_io::storage::host_clear_prefix.replace_implementation(host_storage_clear_prefix),
sp_io::storage::host_changes_root.replace_implementation(host_storage_changes_root),
sp_io::storage::host_append.replace_implementation(host_storage_append),
sp_io::storage::host_next_key.replace_implementation(host_storage_next_key),
sp_io::storage::host_start_transaction
@@ -162,6 +161,13 @@ where
let horizontal_messages = crate::HrmpOutboundMessages::<PSC>::get();
let hrmp_watermark = crate::HrmpWatermark::<PSC>::get();
let head_data =
if let Some(custom_head_data) = crate::CustomValidationHeadData::<PSC>::get() {
HeadData(custom_head_data)
} else {
head_data
};
ValidationResult {
head_data,
new_validation_code: new_validation_code.map(Into::into),
@@ -215,8 +221,8 @@ fn host_storage_clear(key: &[u8]) {
with_externalities(|ext| ext.place_storage(key.to_vec(), None))
}
fn host_storage_root() -> Vec<u8> {
with_externalities(|ext| ext.storage_root())
fn host_storage_root(version: StateVersion) -> Vec<u8> {
with_externalities(|ext| ext.storage_root(version))
}
fn host_storage_clear_prefix(prefix: &[u8], limit: Option<u32>) -> KillStorageResult {
@@ -229,10 +235,6 @@ fn host_storage_clear_prefix(prefix: &[u8], limit: Option<u32>) -> KillStorageRe
})
}
fn host_storage_changes_root(parent_hash: &[u8]) -> Option<Vec<u8>> {
with_externalities(|ext| ext.storage_changes_root(parent_hash).ok().flatten())
}
fn host_storage_append(key: &[u8], value: Vec<u8>) {
with_externalities(|ext| ext.storage_append(key.to_vec(), value))
}
@@ -325,9 +327,9 @@ fn host_default_child_storage_clear_prefix(
})
}
fn host_default_child_storage_root(storage_key: &[u8]) -> Vec<u8> {
fn host_default_child_storage_root(storage_key: &[u8], version: StateVersion) -> Vec<u8> {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.child_storage_root(&child_info))
with_externalities(|ext| ext.child_storage_root(&child_info, version))
}
fn host_default_child_storage_next_key(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
@@ -17,7 +17,8 @@
use codec::{Decode, Encode};
use cumulus_primitives_core::{ParachainBlockData, PersistedValidationData};
use cumulus_test_client::{
runtime::{Block, Hash, Header, UncheckedExtrinsic, WASM_BINARY},
generate_extrinsic,
runtime::{Block, Hash, Header, TestPalletCall, UncheckedExtrinsic, WASM_BINARY},
transfer, BlockData, BuildParachainBlockData, Client, DefaultTestClientBuilderExt, HeadData,
InitBlockBuilder, TestClientBuilder, TestClientBuilderExt, ValidationParams,
};
@@ -26,11 +27,11 @@ use sp_keyring::AccountKeyring::*;
use sp_runtime::{generic::BlockId, traits::Header as HeaderT};
use std::{env, process::Command};
fn call_validate_block(
fn call_validate_block_encoded_header(
parent_head: Header,
block_data: ParachainBlockData<Block>,
relay_parent_storage_root: Hash,
) -> cumulus_test_client::ExecutorResult<Header> {
) -> cumulus_test_client::ExecutorResult<Vec<u8>> {
cumulus_test_client::validate_block(
ValidationParams {
block_data: BlockData(block_data.encode()),
@@ -40,7 +41,16 @@ fn call_validate_block(
},
&WASM_BINARY.expect("You need to build the WASM binaries to run the tests!"),
)
.map(|v| Header::decode(&mut &v.head_data.0[..]).expect("Decodes `Header`."))
.map(|v| v.head_data.0)
}
fn call_validate_block(
parent_head: Header,
block_data: ParachainBlockData<Block>,
relay_parent_storage_root: Hash,
) -> cumulus_test_client::ExecutorResult<Header> {
call_validate_block_encoded_header(parent_head, block_data, relay_parent_storage_root)
.map(|v| Header::decode(&mut &v[..]).expect("Decodes `Header`."))
}
fn create_test_client() -> (Client, Header) {
@@ -92,7 +102,7 @@ fn validate_block_no_extra_extrinsics() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, validation_data } =
build_block_with_witness(&client, vec![], parent_head.clone(), Default::default());
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
let header = block.header().clone();
let res_header =
@@ -126,6 +136,43 @@ fn validate_block_with_extra_extrinsics() {
assert_eq!(header, res_header);
}
#[test]
fn validate_block_returns_custom_head_data() {
sp_tracing::try_init_simple();
let expected_header = vec![1, 3, 3, 7, 4, 5, 6];
let (client, parent_head) = create_test_client();
let extra_extrinsics = vec![
transfer(&client, Alice, Bob, 69),
generate_extrinsic(
&client,
Charlie,
TestPalletCall::set_custom_validation_head_data {
custom_header: expected_header.clone(),
},
),
transfer(&client, Bob, Charlie, 100),
];
let TestBlockData { block, validation_data } = build_block_with_witness(
&client,
extra_extrinsics,
parent_head.clone(),
Default::default(),
);
let header = block.header().clone();
assert_ne!(expected_header, header.encode());
let res_header = call_validate_block_encoded_header(
parent_head,
block,
validation_data.relay_parent_storage_root,
)
.expect("Calls `validate_block`");
assert_eq!(expected_header, res_header);
}
#[test]
fn validate_block_invalid_parent_hash() {
sp_tracing::try_init_simple();
@@ -133,7 +180,7 @@ fn validate_block_invalid_parent_hash() {
if env::var("RUN_TEST").is_ok() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, validation_data } =
build_block_with_witness(&client, vec![], parent_head.clone(), Default::default());
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
let (mut header, extrinsics, witness) = block.deconstruct();
header.set_parent_hash(Hash::from_low_u64_be(1));
@@ -159,7 +206,7 @@ fn validate_block_fails_on_invalid_validation_data() {
if env::var("RUN_TEST").is_ok() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, .. } =
build_block_with_witness(&client, vec![], parent_head.clone(), Default::default());
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
call_validate_block(parent_head, block, Hash::random()).unwrap_err();
} else {
@@ -184,7 +231,7 @@ fn check_inherent_fails_on_validate_block_as_expected() {
let TestBlockData { block, validation_data } = build_block_with_witness(
&client,
vec![],
Vec::new(),
parent_head.clone(),
RelayStateSproofBuilder { current_slot: 1337.into(), ..Default::default() },
);
+12 -10
View File
@@ -2,7 +2,7 @@
name = "cumulus-pallet-session-benchmarking"
version = "3.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/cumulus/"
@@ -13,21 +13,23 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-benchmarking = { default-features = false, optional = true, git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-session = { default-features = false, git = 'https://github.com/paritytech/substrate', branch = "master" }
parity-scale-codec = { version = "2.3.1", default-features = false }
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-session = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = ["std"]
runtime-benchmarks = [
'frame-benchmarking',
'frame-support/runtime-benchmarks',
'frame-system/runtime-benchmarks',
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
std = [
"parity-scale-codec/std",
"sp-std/std",
"sp-runtime/std",
"frame-system/std",
+4 -5
View File
@@ -18,9 +18,10 @@
#![cfg(feature = "runtime-benchmarks")]
use sp_std::{prelude::*, vec};
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller};
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_system::RawOrigin;
use pallet_session::*;
use parity_scale_codec::Decode;
pub struct Pallet<T: Config>(pallet_session::Pallet<T>);
pub trait Config: pallet_session::Config {}
@@ -28,17 +29,15 @@ benchmarks! {
set_keys {
let caller: T::AccountId = whitelisted_caller();
frame_system::Pallet::<T>::inc_providers(&caller);
let keys = T::Keys::default();
let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap();
let proof: Vec<u8> = vec![0,1,2,3];
}: _(RawOrigin::Signed(caller), keys, proof)
purge_keys {
let caller: T::AccountId = whitelisted_caller();
frame_system::Pallet::<T>::inc_providers(&caller);
let keys = T::Keys::default();
let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap();
let proof: Vec<u8> = vec![0,1,2,3];
let _t = pallet_session::Pallet::<T>::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, proof);
}: _(RawOrigin::Signed(caller))
}
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test, extra = false,);
+2 -2
View File
@@ -1,13 +1,13 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
name = "cumulus-pallet-xcm"
version = "0.1.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+4 -4
View File
@@ -2,7 +2,7 @@
name = "cumulus-pallet-xcmp-queue"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
# Other dependencies
@@ -25,11 +25,11 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", default-feature
cumulus-primitives-core = { path = "../../primitives/core", default-features = false }
[dev-dependencies]
sp-core = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-io = { git = 'https://github.com/paritytech/substrate', branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
cumulus-pallet-parachain-system = { path = "../parachain-system" }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master" }
pallet-balances = { git = 'https://github.com/paritytech/substrate', branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = [ "std" ]
+256 -75
View File
@@ -25,6 +25,8 @@
#![cfg_attr(not(feature = "std"), no_std)]
pub mod migration;
#[cfg(test)]
mod mock;
@@ -36,7 +38,7 @@ use cumulus_primitives_core::{
relay_chain::BlockNumber as RelayBlockNumber, ChannelStatus, GetChannelInfo, MessageSendError,
ParaId, XcmpMessageFormat, XcmpMessageHandler, XcmpMessageSource,
};
use frame_support::weights::Weight;
use frame_support::weights::{constants::WEIGHT_PER_MILLIS, Weight};
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaChaRng,
@@ -48,6 +50,11 @@ use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH};
pub use pallet::*;
/// Index used to identify overweight XCMs.
pub type OverweightIndex = u64;
const LOG_TARGET: &str = "xcmp_queue";
#[frame_support::pallet]
pub mod pallet {
use super::*;
@@ -56,6 +63,7 @@ pub mod pallet {
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::storage_version(migration::STORAGE_VERSION)]
pub struct Pallet<T>(_);
#[pallet::config]
@@ -70,22 +78,17 @@ pub mod pallet {
/// Means of converting an `Xcm` into a `VersionedXcm`.
type VersionWrapper: WrapVersion;
}
impl Default for QueueConfigData {
fn default() -> Self {
Self {
suspend_threshold: 2,
drop_threshold: 5,
resume_threshold: 1,
threshold_weight: 100_000,
weight_restrict_decay: 2,
}
}
/// The origin that is allowed to execute overweight messages.
type ExecuteOverweightOrigin: EnsureOrigin<Self::Origin>;
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_runtime_upgrade() -> Weight {
migration::migrate_to_latest::<T>()
}
fn on_idle(_now: T::BlockNumber, max_weight: Weight) -> Weight {
// on_idle processes additional messages with any remaining block weight.
Self::service_xcmp_queue(max_weight)
@@ -93,7 +96,40 @@ pub mod pallet {
}
#[pallet::call]
impl<T: Config> Pallet<T> {}
impl<T: Config> Pallet<T> {
/// Services a single overweight XCM.
///
/// - `origin`: Must pass `ExecuteOverweightOrigin`.
/// - `index`: The index of the overweight XCM to service
/// - `weight_limit`: The amount of weight that XCM execution may take.
///
/// Errors:
/// - `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map.
/// - `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format.
/// - `WeightOverLimit`: XCM execution may use greater `weight_limit`.
///
/// Events:
/// - `OverweightServiced`: On success.
#[pallet::weight(weight_limit.saturating_add(1_000_000))]
pub fn service_overweight(
origin: OriginFor<T>,
index: OverweightIndex,
weight_limit: Weight,
) -> DispatchResultWithPostInfo {
T::ExecuteOverweightOrigin::ensure_origin(origin)?;
let (sender, sent_at, data) =
Overweight::<T>::get(index).ok_or(Error::<T>::BadOverweightIndex)?;
let xcm =
VersionedXcm::<T::Call>::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &data)
.map_err(|_| Error::<T>::BadXcm)?;
let used = Self::handle_xcm_message(sender, sent_at, xcm, weight_limit)
.map_err(|_| Error::<T>::WeightOverLimit)?;
Overweight::<T>::remove(index);
Self::deposit_event(Event::OverweightServiced(index, used));
Ok(Some(used.saturating_add(1_000_000)).into())
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
@@ -110,6 +146,10 @@ pub mod pallet {
UpwardMessageSent(Option<T::Hash>),
/// An HRMP message was sent to a sibling parachain.
XcmpMessageSent(Option<T::Hash>),
/// An XCM exceeded the individual message weight budget.
OverweightEnqueued(ParaId, RelayBlockNumber, OverweightIndex, Weight),
/// An XCM from the overweight queue was executed with the given actual weight used.
OverweightServiced(OverweightIndex, Weight),
}
#[pallet::error]
@@ -120,15 +160,16 @@ pub mod pallet {
BadXcmOrigin,
/// Bad XCM data.
BadXcm,
/// Bad overweight index.
BadOverweightIndex,
/// Provided weight is possibly not enough to execute the message.
WeightOverLimit,
}
/// Status of the inbound XCMP channels.
#[pallet::storage]
pub(super) type InboundXcmpStatus<T: Config> = StorageValue<
_,
Vec<(ParaId, InboundStatus, Vec<(RelayBlockNumber, XcmpMessageFormat)>)>,
ValueQuery,
>;
pub(super) type InboundXcmpStatus<T: Config> =
StorageValue<_, Vec<InboundChannelDetails>, ValueQuery>;
/// Inbound aggregate XCMP messages. It can only be one per ParaId/block.
#[pallet::storage]
@@ -150,7 +191,7 @@ pub mod pallet {
/// The bool is true if there is a signal message waiting to be sent.
#[pallet::storage]
pub(super) type OutboundXcmpStatus<T: Config> =
StorageValue<_, Vec<(ParaId, OutboundStatus, bool, u16, u16)>, ValueQuery>;
StorageValue<_, Vec<OutboundChannelDetails>, ValueQuery>;
// The new way of doing it:
/// The messages outbound in a given XCMP channel.
@@ -166,20 +207,84 @@ pub mod pallet {
/// The configuration which controls the dynamics of the outbound queue.
#[pallet::storage]
pub(super) type QueueConfig<T: Config> = StorageValue<_, QueueConfigData, ValueQuery>;
/// The messages that exceeded max individual message weight budget.
///
/// These message stay in this storage map until they are manually dispatched via
/// `service_overweight`.
#[pallet::storage]
pub(super) type Overweight<T: Config> =
StorageMap<_, Twox64Concat, OverweightIndex, (ParaId, RelayBlockNumber, Vec<u8>)>;
/// The number of overweight messages ever recorded in `Overweight`. Also doubles as the next
/// available free overweight index.
#[pallet::storage]
pub(super) type OverweightCount<T: Config> = StorageValue<_, OverweightIndex, ValueQuery>;
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum InboundStatus {
pub enum InboundState {
Ok,
Suspended,
}
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum OutboundStatus {
pub enum OutboundState {
Ok,
Suspended,
}
/// Struct containing detailed information about the inbound channel.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
pub struct InboundChannelDetails {
/// The `ParaId` of the parachain that this channel is connected with.
sender: ParaId,
/// The state of the channel.
state: InboundState,
/// The ordered metadata of each inbound message.
///
/// Contains info about the relay block number that the message was sent at, and the format
/// of the incoming message.
message_metadata: Vec<(RelayBlockNumber, XcmpMessageFormat)>,
}
/// Struct containing detailed information about the outbound channel.
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct OutboundChannelDetails {
/// The `ParaId` of the parachain that this channel is connected with.
recipient: ParaId,
/// The state of the channel.
state: OutboundState,
/// Whether or not any signals exist in this channel.
signals_exist: bool,
/// The index of the first outbound message.
first_index: u16,
/// The index of the last outbound message.
last_index: u16,
}
impl OutboundChannelDetails {
pub fn new(recipient: ParaId) -> OutboundChannelDetails {
OutboundChannelDetails {
recipient,
state: OutboundState::Ok,
signals_exist: false,
first_index: 0,
last_index: 0,
}
}
pub fn with_signals(mut self) -> OutboundChannelDetails {
self.signals_exist = true;
self
}
pub fn with_suspended_state(mut self) -> OutboundChannelDetails {
self.state = OutboundState::Suspended;
self
}
}
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct QueueConfigData {
/// The number of pages of messages which must be in the queue for the other side to be told to
@@ -196,6 +301,22 @@ pub struct QueueConfigData {
/// The speed to which the available weight approaches the maximum weight. A lower number
/// results in a faster progression. A value of 1 makes the entire weight available initially.
weight_restrict_decay: Weight,
/// The maximum amount of weight any individual message may consume. Messages above this weight
/// go into the overweight queue and may only be serviced explicitly.
xcmp_max_individual_weight: Weight,
}
impl Default for QueueConfigData {
fn default() -> Self {
Self {
suspend_threshold: 2,
drop_threshold: 5,
resume_threshold: 1,
threshold_weight: 100_000,
weight_restrict_decay: 2,
xcmp_max_individual_weight: 20 * WEIGHT_PER_MILLIS,
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, TypeInfo)]
@@ -242,13 +363,13 @@ impl<T: Config> Pallet<T> {
}
let mut s = <OutboundXcmpStatus<T>>::get();
let index = s.iter().position(|item| item.0 == recipient).unwrap_or_else(|| {
s.push((recipient, OutboundStatus::Ok, false, 0, 0));
let index = s.iter().position(|item| item.recipient == recipient).unwrap_or_else(|| {
s.push(OutboundChannelDetails::new(recipient));
s.len() - 1
});
let have_active = s[index].4 > s[index].3;
let have_active = s[index].last_index > s[index].first_index;
let appended = have_active &&
<OutboundXcmpMessages<T>>::mutate(recipient, s[index].4 - 1, |s| {
<OutboundXcmpMessages<T>>::mutate(recipient, s[index].last_index - 1, |s| {
if XcmpMessageFormat::decode_and_advance_with_depth_limit(
MAX_XCM_DECODE_DEPTH,
&mut &s[..],
@@ -263,15 +384,15 @@ impl<T: Config> Pallet<T> {
return true
});
if appended {
Ok((s[index].4 - s[index].3 - 1) as u32)
Ok((s[index].last_index - s[index].first_index - 1) as u32)
} else {
// Need to add a new page.
let page_index = s[index].4;
s[index].4 += 1;
let page_index = s[index].last_index;
s[index].last_index += 1;
let mut new_page = format.encode();
new_page.extend_from_slice(&data[..]);
<OutboundXcmpMessages<T>>::insert(recipient, page_index, new_page);
let r = (s[index].4 - s[index].3 - 1) as u32;
let r = (s[index].last_index - s[index].first_index - 1) as u32;
<OutboundXcmpStatus<T>>::put(s);
Ok(r)
}
@@ -281,10 +402,10 @@ impl<T: Config> Pallet<T> {
/// block.
fn send_signal(dest: ParaId, signal: ChannelSignal) -> Result<(), ()> {
let mut s = <OutboundXcmpStatus<T>>::get();
if let Some(index) = s.iter().position(|item| item.0 == dest) {
s[index].2 = true;
if let Some(index) = s.iter().position(|item| item.recipient == dest) {
s[index].signals_exist = true;
} else {
s.push((dest, OutboundStatus::Ok, true, 0, 0));
s.push(OutboundChannelDetails::new(dest).with_signals());
}
<SignalMessages<T>>::mutate(dest, |page| {
if page.is_empty() {
@@ -366,6 +487,7 @@ impl<T: Config> Pallet<T> {
sender: ParaId,
(sent_at, format): (RelayBlockNumber, XcmpMessageFormat),
max_weight: Weight,
max_individual_weight: Weight,
) -> (Weight, bool) {
let data = <InboundXcmpMessages<T>>::get(sender, sent_at);
let mut last_remaining_fragments;
@@ -382,7 +504,22 @@ impl<T: Config> Pallet<T> {
let weight = max_weight - weight_used;
match Self::handle_xcm_message(sender, sent_at, xcm, weight) {
Ok(used) => weight_used = weight_used.saturating_add(used),
Err(XcmError::TooMuchWeightRequired) => {
Err(XcmError::WeightLimitReached(required))
if required > max_individual_weight =>
{
// overweight - add to overweight queue and continue with message
// execution consuming the message.
let msg_len = last_remaining_fragments
.len()
.saturating_sub(remaining_fragments.len());
let overweight_xcm = last_remaining_fragments[..msg_len].to_vec();
let index = Self::stash_overweight(sender, sent_at, overweight_xcm);
let e = Event::OverweightEnqueued(sender, sent_at, index, required);
Self::deposit_event(e);
},
Err(XcmError::WeightLimitReached(required))
if required <= max_weight =>
{
// That message didn't get processed this time because of being
// too heavy. We leave it around for next time and bail.
remaining_fragments = last_remaining_fragments;
@@ -438,6 +575,22 @@ impl<T: Config> Pallet<T> {
(weight_used, is_empty)
}
/// Puts a given XCM into the list of overweight messages, allowing it to be executed later.
fn stash_overweight(
sender: ParaId,
sent_at: RelayBlockNumber,
xcm: Vec<u8>,
) -> OverweightIndex {
let index = <Self as Store>::OverweightCount::mutate(|count| {
let index = *count;
*count += 1;
index
});
<Self as Store>::Overweight::insert(index, (sender, sent_at, xcm));
index
}
/// Service the incoming XCMP message queue attempting to execute up to `max_weight` execution
/// weight of messages.
///
@@ -471,8 +624,13 @@ impl<T: Config> Pallet<T> {
return 0
}
let QueueConfigData { resume_threshold, threshold_weight, weight_restrict_decay, .. } =
<QueueConfig<T>>::get();
let QueueConfigData {
resume_threshold,
threshold_weight,
weight_restrict_decay,
xcmp_max_individual_weight,
..
} = <QueueConfig<T>>::get();
let mut shuffled = Self::create_shuffle(status.len());
let mut weight_used = 0;
@@ -491,7 +649,7 @@ impl<T: Config> Pallet<T> {
max_weight.saturating_sub(weight_used) >= threshold_weight
{
let index = shuffled[shuffle_index];
let sender = status[index].0;
let sender = status[index].sender;
if weight_available != max_weight {
// Get incrementally closer to freeing up max_weight for message execution over the
@@ -508,34 +666,38 @@ impl<T: Config> Pallet<T> {
}
}
let weight_processed = if status[index].2.is_empty() {
let weight_processed = if status[index].message_metadata.is_empty() {
debug_assert!(false, "channel exists in status; there must be messages; qed");
0
} else {
// Process up to one block's worth for now.
let weight_remaining = weight_available.saturating_sub(weight_used);
let (weight_processed, is_empty) =
Self::process_xcmp_message(sender, status[index].2[0], weight_remaining);
let (weight_processed, is_empty) = Self::process_xcmp_message(
sender,
status[index].message_metadata[0],
weight_remaining,
xcmp_max_individual_weight,
);
if is_empty {
status[index].2.remove(0);
status[index].message_metadata.remove(0);
}
weight_processed
};
weight_used += weight_processed;
if status[index].2.len() as u32 <= resume_threshold &&
status[index].1 == InboundStatus::Suspended
if status[index].message_metadata.len() as u32 <= resume_threshold &&
status[index].state == InboundState::Suspended
{
// Resume
let r = Self::send_signal(sender, ChannelSignal::Resume);
debug_assert!(r.is_ok(), "WARNING: Failed sending resume into suspended channel");
status[index].1 = InboundStatus::Ok;
status[index].state = InboundState::Ok;
}
// If there are more and we're making progress, we process them after we've given the
// other channels a look in. If we've still not unlocked all weight, then we set them
// up for processing a second time anyway.
if !status[index].2.is_empty() &&
if !status[index].message_metadata.is_empty() &&
(weight_processed > 0 || weight_available != max_weight)
{
if shuffle_index + 1 == shuffled.len() {
@@ -548,7 +710,7 @@ impl<T: Config> Pallet<T> {
}
// Only retain the senders that have non-empty queues.
status.retain(|item| !item.2.is_empty());
status.retain(|item| !item.message_metadata.is_empty());
<InboundXcmpStatus<T>>::put(status);
weight_used
@@ -556,28 +718,28 @@ impl<T: Config> Pallet<T> {
fn suspend_channel(target: ParaId) {
<OutboundXcmpStatus<T>>::mutate(|s| {
if let Some(index) = s.iter().position(|item| item.0 == target) {
let ok = s[index].1 == OutboundStatus::Ok;
if let Some(index) = s.iter().position(|item| item.recipient == target) {
let ok = s[index].state == OutboundState::Ok;
debug_assert!(ok, "WARNING: Attempt to suspend channel that was not Ok.");
s[index].1 = OutboundStatus::Suspended;
s[index].state = OutboundState::Suspended;
} else {
s.push((target, OutboundStatus::Suspended, false, 0, 0));
s.push(OutboundChannelDetails::new(target).with_suspended_state());
}
});
}
fn resume_channel(target: ParaId) {
<OutboundXcmpStatus<T>>::mutate(|s| {
if let Some(index) = s.iter().position(|item| item.0 == target) {
let suspended = s[index].1 == OutboundStatus::Suspended;
if let Some(index) = s.iter().position(|item| item.recipient == target) {
let suspended = s[index].state == OutboundState::Suspended;
debug_assert!(
suspended,
"WARNING: Attempt to resume channel that was not suspended."
);
if s[index].3 == s[index].4 {
if s[index].first_index == s[index].last_index {
s.remove(index);
} else {
s[index].1 = OutboundStatus::Ok;
s[index].state = OutboundState::Ok;
}
} else {
debug_assert!(false, "WARNING: Attempt to resume channel that was not suspended.");
@@ -619,11 +781,12 @@ impl<T: Config> XcmpMessageHandler for Pallet<T> {
}
} else {
// Record the fact we received it.
match status.binary_search_by_key(&sender, |item| item.0) {
match status.binary_search_by_key(&sender, |item| item.sender) {
Ok(i) => {
let count = status[i].2.len();
if count as u32 >= suspend_threshold && status[i].1 == InboundStatus::Ok {
status[i].1 = InboundStatus::Suspended;
let count = status[i].message_metadata.len();
if count as u32 >= suspend_threshold && status[i].state == InboundState::Ok
{
status[i].state = InboundState::Suspended;
let r = Self::send_signal(sender, ChannelSignal::Suspend);
if r.is_err() {
log::warn!(
@@ -632,7 +795,7 @@ impl<T: Config> XcmpMessageHandler for Pallet<T> {
}
}
if (count as u32) < drop_threshold {
status[i].2.push((sent_at, format));
status[i].message_metadata.push((sent_at, format));
} else {
debug_assert!(
false,
@@ -640,7 +803,11 @@ impl<T: Config> XcmpMessageHandler for Pallet<T> {
);
}
},
Err(_) => status.push((sender, InboundStatus::Ok, vec![(sent_at, format)])),
Err(_) => status.push(InboundChannelDetails {
sender,
state: InboundState::Ok,
message_metadata: vec![(sent_at, format)],
}),
}
// Queue the payload for later execution.
<InboundXcmpMessages<T>>::insert(sender, sent_at, data_ref);
@@ -664,47 +831,53 @@ impl<T: Config> XcmpMessageSource for Pallet<T> {
let mut result = Vec::with_capacity(max_message_count);
for status in statuses.iter_mut() {
let (para_id, outbound_status, mut signalling, mut begin, mut end) = *status;
let OutboundChannelDetails {
recipient: para_id,
state: outbound_state,
mut signals_exist,
mut first_index,
mut last_index,
} = *status;
if result.len() == max_message_count {
// We check this condition in the beginning of the loop so that we don't include
// a message where the limit is 0.
break
}
if outbound_status == OutboundStatus::Suspended {
if outbound_state == OutboundState::Suspended {
continue
}
let (max_size_now, max_size_ever) = match T::ChannelInfo::get_channel_status(para_id) {
ChannelStatus::Closed => {
// This means that there is no such channel anymore. Nothing to be done but
// swallow the messages and discard the status.
for i in begin..end {
for i in first_index..last_index {
<OutboundXcmpMessages<T>>::remove(para_id, i);
}
if signalling {
if signals_exist {
<SignalMessages<T>>::remove(para_id);
}
*status = (para_id, OutboundStatus::Ok, false, 0, 0);
*status = OutboundChannelDetails::new(para_id);
continue
},
ChannelStatus::Full => continue,
ChannelStatus::Ready(n, e) => (n, e),
};
let page = if signalling {
let page = if signals_exist {
let page = <SignalMessages<T>>::get(para_id);
if page.len() < max_size_now {
<SignalMessages<T>>::remove(para_id);
signalling = false;
signals_exist = false;
page
} else {
continue
}
} else if end > begin {
let page = <OutboundXcmpMessages<T>>::get(para_id, begin);
} else if last_index > first_index {
let page = <OutboundXcmpMessages<T>>::get(para_id, first_index);
if page.len() < max_size_now {
<OutboundXcmpMessages<T>>::remove(para_id, begin);
begin += 1;
<OutboundXcmpMessages<T>>::remove(para_id, first_index);
first_index += 1;
page
} else {
continue
@@ -712,9 +885,9 @@ impl<T: Config> XcmpMessageSource for Pallet<T> {
} else {
continue
};
if begin == end {
begin = 0;
end = 0;
if first_index == last_index {
first_index = 0;
last_index = 0;
}
if page.len() > max_size_ever {
@@ -726,7 +899,13 @@ impl<T: Config> XcmpMessageSource for Pallet<T> {
result.push((para_id, page));
}
*status = (para_id, outbound_status, signalling, begin, end);
*status = OutboundChannelDetails {
recipient: para_id,
state: outbound_state,
signals_exist,
first_index,
last_index,
};
}
// Sort the outbound messages by ascending recipient para id to satisfy the acceptance
@@ -741,7 +920,9 @@ impl<T: Config> XcmpMessageSource for Pallet<T> {
//
// To mitigate this we shift all processed elements towards the end of the vector using
// `rotate_left`. To get intuition how it works see the examples in its rustdoc.
statuses.retain(|x| x.1 == OutboundStatus::Suspended || x.2 || x.3 < x.4);
statuses.retain(|x| {
x.state == OutboundState::Suspended || x.signals_exist || x.first_index < x.last_index
});
// old_status_len must be >= status.len() since we never add anything to status.
let pruned = old_statuses_len - statuses.len();
+128
View File
@@ -0,0 +1,128 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot 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.
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! A module that is responsible for migration of storage.
use crate::{Config, Pallet, Store};
use frame_support::{pallet_prelude::*, traits::StorageVersion, weights::Weight};
/// The current storage version.
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
/// Migrates the pallet storage to the most recent version, checking and setting the
/// `StorageVersion`.
pub fn migrate_to_latest<T: Config>() -> Weight {
let mut weight = 0;
if StorageVersion::get::<Pallet<T>>() == 0 {
weight += migrate_to_v1::<T>();
StorageVersion::new(1).put::<Pallet<T>>();
}
weight
}
mod v0 {
use super::*;
use codec::{Decode, Encode};
#[derive(Encode, Decode, Debug)]
pub struct QueueConfigData {
pub suspend_threshold: u32,
pub drop_threshold: u32,
pub resume_threshold: u32,
pub threshold_weight: Weight,
pub weight_restrict_decay: Weight,
}
impl Default for QueueConfigData {
fn default() -> Self {
QueueConfigData {
suspend_threshold: 2,
drop_threshold: 5,
resume_threshold: 1,
threshold_weight: 100_000,
weight_restrict_decay: 2,
}
}
}
}
/// Migrates `QueueConfigData` from v0 (without the `xcmp_max_individual_weight` field) to v1 (with
/// max individual weight).
/// Uses the `Default` implementation of `QueueConfigData` to choose a value for
/// `xcmp_max_individual_weight`.
///
/// NOTE: Only use this function if you know what you're doing. Default to using
/// `migrate_to_latest`.
pub fn migrate_to_v1<T: Config>() -> Weight {
let translate = |pre: v0::QueueConfigData| -> super::QueueConfigData {
super::QueueConfigData {
suspend_threshold: pre.suspend_threshold,
drop_threshold: pre.drop_threshold,
resume_threshold: pre.resume_threshold,
threshold_weight: pre.threshold_weight,
weight_restrict_decay: pre.weight_restrict_decay,
xcmp_max_individual_weight: super::QueueConfigData::default()
.xcmp_max_individual_weight,
}
};
if let Err(_) = <Pallet<T> as Store>::QueueConfig::translate(|pre| pre.map(translate)) {
log::error!(
target: super::LOG_TARGET,
"unexpected error when performing translation of the QueueConfig type during storage upgrade to v1"
);
}
T::DbWeight::get().reads_writes(1, 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, Test};
#[test]
fn test_migration_to_v1() {
let v0 = v0::QueueConfigData {
suspend_threshold: 5,
drop_threshold: 12,
resume_threshold: 3,
threshold_weight: 333_333,
weight_restrict_decay: 1,
};
new_test_ext().execute_with(|| {
// Put the v0 version in the state
frame_support::storage::unhashed::put_raw(
&crate::QueueConfig::<Test>::hashed_key(),
&v0.encode(),
);
migrate_to_v1::<Test>();
let v1 = crate::QueueConfig::<Test>::get();
assert_eq!(v0.suspend_threshold, v1.suspend_threshold);
assert_eq!(v0.drop_threshold, v1.drop_threshold);
assert_eq!(v0.resume_threshold, v1.resume_threshold);
assert_eq!(v0.threshold_weight, v1.threshold_weight);
assert_eq!(v0.weight_restrict_decay, v1.weight_restrict_decay);
assert_eq!(v1.xcmp_max_individual_weight, 20_000_000_000);
});
}
}
+3
View File
@@ -16,6 +16,7 @@
use super::*;
use crate as xcmp_queue;
use frame_support::parameter_types;
use frame_system::EnsureRoot;
use sp_core::H256;
use sp_runtime::{
testing::Header,
@@ -75,6 +76,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Test>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -160,6 +162,7 @@ impl Config for Test {
type XcmExecutor = xcm_executor::XcmExecutor<XcmConfig>;
type ChannelInfo = ParachainSystem;
type VersionWrapper = ();
type ExecuteOverweightOrigin = EnsureRoot<AccountId>;
}
pub fn new_test_ext() -> sp_io::TestExternalities {
+26 -3
View File
@@ -15,7 +15,8 @@
use super::*;
use cumulus_primitives_core::XcmpMessageHandler;
use mock::{new_test_ext, Test, XcmpQueue};
use frame_support::assert_noop;
use mock::{new_test_ext, Origin, Test, XcmpQueue};
#[test]
fn one_message_does_not_panic() {
@@ -30,6 +31,7 @@ fn one_message_does_not_panic() {
#[test]
#[should_panic = "Invalid incoming blob message data"]
#[cfg(debug_assertions)]
fn bad_message_is_handled() {
new_test_ext().execute_with(|| {
let bad_data = vec![
@@ -40,12 +42,13 @@ fn bad_message_is_handled() {
InboundXcmpMessages::<Test>::insert(ParaId::from(1000), 1, bad_data);
let format = XcmpMessageFormat::ConcatenatedEncodedBlob;
// This should exit with an error.
XcmpQueue::process_xcmp_message(1000.into(), (1, format), 10_000_000_000);
XcmpQueue::process_xcmp_message(1000.into(), (1, format), 10_000_000_000, 10_000_000_000);
});
}
#[test]
#[should_panic = "Invalid incoming blob message data"]
#[cfg(debug_assertions)]
fn other_bad_message_is_handled() {
new_test_ext().execute_with(|| {
let bad_data = vec![
@@ -56,6 +59,26 @@ fn other_bad_message_is_handled() {
InboundXcmpMessages::<Test>::insert(ParaId::from(1000), 1, bad_data);
let format = XcmpMessageFormat::ConcatenatedEncodedBlob;
// This should exit with an error.
XcmpQueue::process_xcmp_message(1000.into(), (1, format), 10_000_000_000);
XcmpQueue::process_xcmp_message(1000.into(), (1, format), 10_000_000_000, 10_000_000_000);
});
}
#[test]
fn service_overweight_unknown() {
new_test_ext().execute_with(|| {
assert_noop!(
XcmpQueue::service_overweight(Origin::root(), 0, 1000),
Error::<Test>::BadOverweightIndex,
);
});
}
#[test]
fn service_overweight_bad_xcm_format() {
new_test_ext().execute_with(|| {
let bad_xcm = vec![255];
Overweight::<Test>::insert(0, (ParaId::from(1000), 0, bad_xcm));
assert_noop!(XcmpQueue::service_overweight(Origin::root(), 0, 1000), Error::<Test>::BadXcm);
});
}
+13 -5
View File
@@ -2,13 +2,21 @@
A new [Cumulus](https://github.com/paritytech/cumulus/)-based Substrate node, ready for hacking ☁️..
This project is a fork of the [Substrate Node Template](https://github.com/substrate-developer-hub/substrate-node-template)
This project is originally a fork of the
[Substrate Node Template](https://github.com/substrate-developer-hub/substrate-node-template)
modified to include dependencies required for registering this node as a **parathread** or
**parachain** to the Rococo **relay chain**.
Rococo is [Polkadot's parachain testnet](https://polkadot.network/blog/introducing-rococo-polkadots-parachain-testnet/) 👑.
**parachain** to a **relay chain**.
The stand-alone version of this template is hosted on the
[Substrate Devhub Parachain Template](https://github.com/substrate-developer-hub/substrate-parachain-template/)
for each release of Polkadot. It is generated directly to the upstream
[Parachain Template in Cumulus](https://github.com/paritytech/cumulus/tree/master/parachain-template)
at each release branch using the
[Substrate Template Generator](https://github.com/paritytech/substrate-template-generator/).
👉 Learn more about parachains [here](https://wiki.polkadot.network/docs/learn-parachains), and
parathreads [here](https://wiki.polkadot.network/docs/learn-parathreads).
To learn about how to actually use the template to hack together your own parachain check out the
`README` from the [`substrate-parachain-template` repository](https://github.com/substrate-developer-hub/substrate-parachain-template/).
🧙 Learn about how to use this template and run your own parachain testnet for it in the
[Devhub Cumulus Tutorial](https://docs.substrate.io/tutorials/v3/cumulus/start-relay/).
+7 -3
View File
@@ -6,7 +6,7 @@ description = "A new Cumulus FRAME-based Substrate Node, ready for hacking toget
license = "Unlicense"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/cumulus/"
edition = "2018"
edition = "2021"
build = "build.rs"
[package.metadata.docs.rs]
@@ -21,13 +21,14 @@ path = "src/main.rs"
[features]
runtime-benchmarks = ["parachain-template-runtime/runtime-benchmarks"]
try-runtime = [ "parachain-template-runtime/try-runtime" ]
[dependencies]
derive_more = "0.99.2"
log = "0.4.14"
codec = { package = "parity-scale-codec", version = "2.0.0" }
structopt = "0.3.8"
serde = { version = "1.0.119", features = ["derive"] }
serde = { version = "1.0.132", features = ["derive"] }
hex-literal = "0.3.1"
# RPC related Dependencies
@@ -39,6 +40,7 @@ parachain-template-runtime = { path = "../runtime" }
# Substrate Dependencies
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
try-runtime-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -86,10 +88,12 @@ cumulus-client-network = { path = "../../client/network" }
cumulus-client-service = { path = "../../client/service" }
cumulus-primitives-core = { path = "../../primitives/core" }
cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" }
cumulus-relay-chain-interface = { path = "../../client/relay-chain-interface" }
cumulus-relay-chain-local = { path = "../../client/relay-chain-local" }
# Polkadot dependencies
polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-test-service = { git = "https://github.com/paritytech/polkadot", branch = "master" }
+14 -16
View File
@@ -11,7 +11,7 @@ pub type ChainSpec =
sc_service::GenericChainSpec<parachain_template_runtime::GenesisConfig, Extensions>;
/// Helper function to generate a crypto pair from seed
pub fn get_pair_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
pub fn get_public_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
@@ -40,7 +40,7 @@ type AccountPublic = <Signature as Verify>::Signer;
///
/// This function's return type must always match the session keys of the chain in tuple format.
pub fn get_collator_keys_from_seed(seed: &str) -> AuraId {
get_pair_from_seed::<AuraId>(seed)
get_public_from_seed::<AuraId>(seed)
}
/// Helper function to generate an account ID from seed
@@ -48,7 +48,7 @@ pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
AccountPublic::from(get_pair_from_seed::<TPublic>(seed)).into_account()
AccountPublic::from(get_public_from_seed::<TPublic>(seed)).into_account()
}
/// Generate the session keys from individual elements.
@@ -58,10 +58,10 @@ pub fn template_session_keys(keys: AuraId) -> parachain_template_runtime::Sessio
parachain_template_runtime::SessionKeys { aura: keys }
}
pub fn development_config(id: ParaId) -> ChainSpec {
pub fn development_config() -> ChainSpec {
// Give your base currency a unit name and decimal places
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "ROC".into());
properties.insert("tokenSymbol".into(), "UNIT".into());
properties.insert("tokenDecimals".into(), 12.into());
properties.insert("ss58Format".into(), 42.into());
@@ -98,24 +98,24 @@ pub fn development_config(id: ParaId) -> ChainSpec {
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
None,
Extensions {
relay_chain: "rococo-local".into(), // You MUST set this to the correct network!
para_id: id.into(),
para_id: 1000,
},
)
}
pub fn local_testnet_config(id: ParaId) -> ChainSpec {
pub fn local_testnet_config() -> ChainSpec {
// Give your base currency a unit name and decimal places
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "ROC".into());
properties.insert("tokenSymbol".into(), "UNIT".into());
properties.insert("tokenDecimals".into(), 12.into());
properties.insert("ss58Format".into(), 42.into());
@@ -152,11 +152,11 @@ pub fn local_testnet_config(id: ParaId) -> ChainSpec {
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
id,
1000.into(),
)
},
// Bootnodes
vec![],
Vec::new(),
// Telemetry
None,
// Protocol ID
@@ -166,7 +166,7 @@ pub fn local_testnet_config(id: ParaId) -> ChainSpec {
// Extensions
Extensions {
relay_chain: "rococo-local".into(), // You MUST set this to the correct network!
para_id: id.into(),
para_id: 1000,
},
)
}
@@ -181,7 +181,6 @@ fn testnet_genesis(
code: parachain_template_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
},
balances: parachain_template_runtime::BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
@@ -194,8 +193,7 @@ fn testnet_genesis(
},
session: parachain_template_runtime::SessionConfig {
keys: invulnerables
.iter()
.cloned()
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
+6 -9
View File
@@ -37,6 +37,9 @@ pub enum Subcommand {
/// The custom benchmark subcommmand benchmarking runtime pallets.
#[structopt(name = "benchmark", about = "Benchmark runtime pallets.")]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try some testing command against a specified runtime state.
TryRuntime(try_runtime_cli::TryRuntimeCmd),
}
/// Command for exporting the genesis state of the parachain
@@ -46,18 +49,12 @@ pub struct ExportGenesisStateCommand {
#[structopt(parse(from_os_str))]
pub output: Option<PathBuf>,
/// Id of the parachain this state is for.
///
/// Default: 100
#[structopt(long, conflicts_with = "chain")]
pub parachain_id: Option<u32>,
/// Write output in binary. Default is to write in hex.
#[structopt(short, long)]
pub raw: bool,
/// The name of the chain for that the genesis state should be exported.
#[structopt(long, conflicts_with = "parachain-id")]
#[structopt(long)]
pub chain: Option<String>,
}
@@ -90,9 +87,9 @@ pub struct Cli {
#[structopt(flatten)]
pub run: cumulus_client_cli::RunCmd,
/// Relaychain arguments
/// Relay chain arguments
#[structopt(raw = true)]
pub relaychain_args: Vec<String>,
pub relay_chain_args: Vec<String>,
}
#[derive(Debug)]
+60 -31
View File
@@ -13,19 +13,19 @@ use sc_cli::{
ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli,
};
use sc_service::config::{BasePath, PrometheusConfig};
use sc_service::{
config::{BasePath, PrometheusConfig},
TaskManager,
};
use sp_core::hexdisplay::HexDisplay;
use sp_runtime::traits::Block as BlockT;
use std::{io::Write, net::SocketAddr};
fn load_spec(
id: &str,
para_id: ParaId,
) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
fn load_spec(id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
Ok(match id {
"dev" => Box::new(chain_spec::development_config(para_id)),
"template-rococo" => Box::new(chain_spec::local_testnet_config(para_id)),
"" | "local" => Box::new(chain_spec::local_testnet_config(para_id)),
"dev" => Box::new(chain_spec::development_config()),
"template-rococo" => Box::new(chain_spec::local_testnet_config()),
"" | "local" => Box::new(chain_spec::local_testnet_config()),
path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
})
}
@@ -40,13 +40,11 @@ impl SubstrateCli for Cli {
}
fn description() -> String {
format!(
"Parachain Collator Template\n\nThe command-line arguments provided first will be \
"Parachain Collator Template\n\nThe command-line arguments provided first will be \
passed to the parachain node, while the arguments provided after -- will be passed \
to the relaychain node.\n\n\
{} [parachain-args] -- [relaychain-args]",
Self::executable_name()
)
to the relay chain node.\n\n\
parachain-collator <parachain-args> -- <relay-chain-args>"
.into()
}
fn author() -> String {
@@ -62,7 +60,7 @@ impl SubstrateCli for Cli {
}
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
load_spec(id, self.run.parachain_id.unwrap_or(2000).into())
load_spec(id)
}
fn native_runtime_version(_: &Box<dyn ChainSpec>) -> &'static RuntimeVersion {
@@ -82,8 +80,8 @@ impl SubstrateCli for RelayChainCli {
fn description() -> String {
"Parachain Collator Template\n\nThe command-line arguments provided first will be \
passed to the parachain node, while the arguments provided after -- will be passed \
to the relaychain node.\n\n\
parachain-collator [parachain-args] -- [relaychain-args]"
to the relay chain node.\n\n\
parachain-collator <parachain-args> -- <relay-chain-args>"
.into()
}
@@ -171,7 +169,7 @@ pub fn run() -> Result<()> {
runner.sync_run(|config| {
let polkadot_cli = RelayChainCli::new(
&config,
[RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()),
[RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()),
);
let polkadot_config = SubstrateCli::create_configuration(
@@ -194,10 +192,9 @@ pub fn run() -> Result<()> {
builder.with_profiling(sc_tracing::TracingReceiver::Log, "");
let _ = builder.init();
let block: Block = generate_genesis_block(&load_spec(
&params.chain.clone().unwrap_or_default(),
params.parachain_id.unwrap_or(2000).into(),
)?)?;
let spec = load_spec(&params.chain.clone().unwrap_or_default())?;
let state_version = Cli::native_runtime_version(&spec).state_version();
let block: Block = generate_genesis_block(&spec, state_version)?;
let raw_header = block.header().encode();
let output_buf = if params.raw {
raw_header
@@ -244,25 +241,44 @@ pub fn run() -> Result<()> {
You can enable it with `--features runtime-benchmarks`."
.into())
},
Some(Subcommand::TryRuntime(cmd)) =>
if cfg!(feature = "try-runtime") {
let runner = cli.create_runner(cmd)?;
// grab the task manager.
let registry = &runner.config().prometheus_config.as_ref().map(|cfg| &cfg.registry);
let task_manager =
TaskManager::new(runner.config().tokio_handle.clone(), *registry)
.map_err(|e| format!("Error: {:?}", e))?;
runner.async_run(|config| {
Ok((cmd.run::<Block, TemplateRuntimeExecutor>(config), task_manager))
})
} else {
Err("Try-runtime must be enabled by `--features try-runtime`.".into())
},
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
runner.run_node_until_exit(|config| async move {
let para_id =
chain_spec::Extensions::try_get(&*config.chain_spec).map(|e| e.para_id);
let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
.map(|e| e.para_id)
.ok_or_else(|| "Could not find parachain ID in chain-spec.")?;
let polkadot_cli = RelayChainCli::new(
&config,
[RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()),
[RelayChainCli::executable_name()].iter().chain(cli.relay_chain_args.iter()),
);
let id = ParaId::from(cli.run.parachain_id.or(para_id).unwrap_or(2000));
let id = ParaId::from(para_id);
let parachain_account =
AccountIdConversion::<polkadot_primitives::v0::AccountId>::into_account(&id);
let block: Block =
generate_genesis_block(&config.chain_spec).map_err(|e| format!("{:?}", e))?;
let state_version =
RelayChainCli::native_runtime_version(&config.chain_spec).state_version();
let block: Block = generate_genesis_block(&config.chain_spec, state_version)
.map_err(|e| format!("{:?}", e))?;
let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode()));
let tokio_handle = config.tokio_handle.clone();
@@ -338,11 +354,24 @@ impl CliConfiguration<Self> for RelayChainCli {
self.base.base.rpc_ws(default_listen_port)
}
fn prometheus_config(&self, default_listen_port: u16) -> Result<Option<PrometheusConfig>> {
self.base.base.prometheus_config(default_listen_port)
fn prometheus_config(
&self,
default_listen_port: u16,
chain_spec: &Box<dyn ChainSpec>,
) -> Result<Option<PrometheusConfig>> {
self.base.base.prometheus_config(default_listen_port, chain_spec)
}
fn init<C: SubstrateCli>(&self) -> Result<()> {
fn init<F>(
&self,
_support_url: &String,
_impl_version: &String,
_logger_hook: F,
_config: &sc_service::Configuration,
) -> Result<()>
where
F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
{
unreachable!("PolkadotCli is never initialized; qed");
}
+1 -1
View File
@@ -1,4 +1,4 @@
//! Substrate Node CLI library.
//! Substrate Parachain Node Template CLI
#![warn(missing_docs)]
+59 -75
View File
@@ -1,7 +1,7 @@
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
// std
use std::sync::Arc;
use std::{sync::Arc, time::Duration};
// Local Runtime Types
use parachain_template_runtime::{
@@ -9,15 +9,15 @@ use parachain_template_runtime::{
};
// Cumulus Imports
use cumulus_client_consensus_aura::{
build_aura_consensus, BuildAuraConsensusParams, SlotProportion,
};
use cumulus_client_consensus_aura::{AuraConsensus, BuildAuraConsensusParams, SlotProportion};
use cumulus_client_consensus_common::ParachainConsensus;
use cumulus_client_network::build_block_announce_validator;
use cumulus_client_network::BlockAnnounceValidator;
use cumulus_client_service::{
prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams,
};
use cumulus_primitives_core::ParaId;
use cumulus_relay_chain_interface::RelayChainInterface;
use cumulus_relay_chain_local::build_relay_chain_interface;
// Substrate Imports
use sc_client_api::ExecutorProvider;
@@ -114,6 +114,7 @@ where
config.wasm_method,
config.default_heap_pages,
config.max_runtime_instances,
config.runtime_cache_size,
);
let (client, backend, keystore_container, task_manager) =
@@ -127,7 +128,7 @@ where
let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
let telemetry = telemetry.map(|(worker, telemetry)| {
task_manager.spawn_handle().spawn("telemetry", worker.run());
task_manager.spawn_handle().spawn("telemetry", None, worker.run());
telemetry
});
@@ -215,7 +216,7 @@ where
Option<&Registry>,
Option<TelemetryHandle>,
&TaskManager,
&polkadot_service::NewFull<polkadot_service::Client>,
Arc<dyn RelayChainInterface>,
Arc<
sc_transaction_pool::FullPool<
Block,
@@ -236,27 +237,23 @@ where
let params = new_partial::<RuntimeApi, Executor, BIQ>(&parachain_config, build_import_queue)?;
let (mut telemetry, telemetry_worker_handle) = params.other;
let relay_chain_full_node =
cumulus_client_service::build_polkadot_full_node(polkadot_config, telemetry_worker_handle)
let client = params.client.clone();
let backend = params.backend.clone();
let mut task_manager = params.task_manager;
let (relay_chain_interface, collator_key) =
build_relay_chain_interface(polkadot_config, telemetry_worker_handle, &mut task_manager)
.map_err(|e| match e {
polkadot_service::Error::Sub(x) => x,
s => format!("{}", s).into(),
})?;
let client = params.client.clone();
let backend = params.backend.clone();
let block_announce_validator = build_block_announce_validator(
relay_chain_full_node.client.clone(),
id,
Box::new(relay_chain_full_node.network.clone()),
relay_chain_full_node.backend.clone(),
);
let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id);
let force_authoring = parachain_config.force_authoring;
let validator = parachain_config.role.is_authority();
let prometheus_registry = parachain_config.prometheus_registry().cloned();
let transaction_pool = params.transaction_pool.clone();
let mut task_manager = params.task_manager;
let import_queue = cumulus_client_service::SharedImportQueue::new(params.import_queue);
let (network, system_rpc_tx, start_network) =
sc_service::build_network(sc_service::BuildNetworkParams {
@@ -265,8 +262,9 @@ where
transaction_pool: transaction_pool.clone(),
spawn_handle: task_manager.spawn_handle(),
import_queue: import_queue.clone(),
on_demand: None,
block_announce_validator_builder: Some(Box::new(|_| block_announce_validator)),
block_announce_validator_builder: Some(Box::new(|_| {
Box::new(block_announce_validator)
})),
warp_sync: None,
})?;
@@ -286,8 +284,6 @@ where
};
sc_service::spawn_tasks(sc_service::SpawnTasksParams {
on_demand: None,
remote_blockchain: None,
rpc_extensions_builder,
client: client.clone(),
transaction_pool: transaction_pool.clone(),
@@ -311,7 +307,7 @@ where
prometheus_registry.as_ref(),
telemetry.as_ref().map(|t| t.handle()),
&task_manager,
&relay_chain_full_node,
relay_chain_interface.clone(),
transaction_pool,
network,
params.keystore_container.sync_keystore(),
@@ -326,10 +322,12 @@ where
announce_block,
client: client.clone(),
task_manager: &mut task_manager,
relay_chain_full_node,
relay_chain_interface,
spawner,
parachain_consensus,
import_queue,
collator_key,
slot_duration: Duration::from_secs(6),
};
start_collator(params).await?;
@@ -339,7 +337,7 @@ where
announce_block,
task_manager: &mut task_manager,
para_id: id,
relay_chain_full_node,
relay_chain_interface,
};
start_full_node(params)?;
@@ -415,7 +413,7 @@ pub async fn start_parachain_node(
prometheus_registry,
telemetry,
task_manager,
relay_chain_node,
relay_chain_interface,
transaction_pool,
sync_oracle,
keystore,
@@ -430,62 +428,48 @@ pub async fn start_parachain_node(
telemetry.clone(),
);
let relay_chain_backend = relay_chain_node.backend.clone();
let relay_chain_client = relay_chain_node.client.clone();
Ok(build_aura_consensus::<
sp_consensus_aura::sr25519::AuthorityPair,
_,
_,
_,
_,
_,
_,
_,
_,
_,
>(BuildAuraConsensusParams {
proposer_factory,
create_inherent_data_providers: move |_, (relay_parent, validation_data)| {
let parachain_inherent =
cumulus_primitives_parachain_inherent::ParachainInherentData::create_at_with_client(
relay_parent,
&relay_chain_client,
&*relay_chain_backend,
&validation_data,
id,
);
async move {
let time = sp_timestamp::InherentDataProvider::from_system_time();
Ok(AuraConsensus::build::<sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _>(
BuildAuraConsensusParams {
proposer_factory,
create_inherent_data_providers: move |_, (relay_parent, validation_data)| {
let parachain_inherent =
cumulus_primitives_parachain_inherent::ParachainInherentData::create_at(
relay_parent,
&relay_chain_interface,
&validation_data,
id,
);
async move {
let time = sp_timestamp::InherentDataProvider::from_system_time();
let slot =
let slot =
sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration(
*time,
slot_duration.slot_duration(),
);
let parachain_inherent = parachain_inherent.ok_or_else(|| {
Box::<dyn std::error::Error + Send + Sync>::from(
"Failed to create parachain inherent",
)
})?;
Ok((time, slot, parachain_inherent))
}
let parachain_inherent = parachain_inherent.ok_or_else(|| {
Box::<dyn std::error::Error + Send + Sync>::from(
"Failed to create parachain inherent",
)
})?;
Ok((time, slot, parachain_inherent))
}
},
block_import: client.clone(),
para_client: client,
backoff_authoring_blocks: Option::<()>::None,
sync_oracle,
keystore,
force_authoring,
slot_duration,
// We got around 500ms for proposing
block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32),
// And a maximum of 750ms if slots are skipped
max_block_proposal_slot_portion: Some(SlotProportion::new(1f32 / 16f32)),
telemetry,
},
block_import: client.clone(),
relay_chain_client: relay_chain_node.client.clone(),
relay_chain_backend: relay_chain_node.backend.clone(),
para_client: client,
backoff_authoring_blocks: Option::<()>::None,
sync_oracle,
keystore,
force_authoring,
slot_duration,
// We got around 500ms for proposing
block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32),
// And a maximum of 750ms if slots are skipped
max_block_proposal_slot_portion: Some(SlotProportion::new(1f32 / 16f32)),
telemetry,
}))
))
},
)
.await
+10 -9
View File
@@ -6,7 +6,7 @@ version = "0.1.0"
license = "Unlicense"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
edition = "2018"
edition = "2021"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
@@ -15,19 +15,19 @@ targets = ["x86_64-unknown-linux-gnu"]
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"], default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
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 }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true , branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
[dev-dependencies]
serde = { version = "1.0.119" }
sp-core = { 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-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
serde = { version = "1.0.132" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
[features]
default = ["std"]
runtime-benchmarks = ["frame-benchmarking"]
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
std = [
"codec/std",
"scale-info/std",
@@ -35,3 +35,4 @@ std = [
"frame-system/std",
"frame-benchmarking/std",
]
try-runtime = [ "frame-support/try-runtime" ]
@@ -3,7 +3,7 @@
use super::*;
#[allow(unused)]
use crate::Module as Template;
use crate::Pallet as Template;
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller};
use frame_system::RawOrigin;
@@ -51,6 +51,7 @@ impl system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_template::Config for Test {
+13 -5
View File
@@ -6,7 +6,7 @@ description = "A new Cumulus FRAME-based Substrate Runtime, ready for hacking to
license = "Unlicense"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/cumulus/"
edition = "2018"
edition = "2021"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
@@ -15,10 +15,11 @@ targets = ["x86_64-unknown-linux-gnu"]
substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
[dependencies]
hex-literal = { version = "0.3.1", optional = true }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"]}
log = { version = "0.4.14", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.119", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
smallvec = "1.6.1"
# Local Dependencies
@@ -41,6 +42,7 @@ sp-version = { git = "https://github.com/paritytech/substrate", default-features
## Substrate FRAME Dependencies
frame-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true , branch = "master" }
frame-try-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true , branch = "master" }
frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
@@ -51,7 +53,6 @@ frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate"
pallet-aura = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-authorship = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
@@ -69,6 +70,7 @@ cumulus-primitives-timestamp = { path = "../../primitives/timestamp", default-fe
cumulus-primitives-utility = { path = "../../primitives/utility", default-features = false }
pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false }
parachain-info = { path = "../../polkadot-parachains/pallets/parachain-info", default-features = false }
cumulus-pallet-session-benchmarking = {path = "../../pallets/session-benchmarking", default-features = false, version = "3.0.0"}
# Polkadot Dependencies
pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "master" }
@@ -106,7 +108,6 @@ std = [
"pallet-authorship/std",
"pallet-balances/std",
"pallet-collator-selection/std",
"pallet-randomness-collective-flip/std",
"pallet-session/std",
"pallet-sudo/std",
"pallet-template/std",
@@ -130,9 +131,10 @@ std = [
]
runtime-benchmarks = [
"hex-literal",
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"frame-benchmarking",
"frame-benchmarking/runtime-benchmarks",
"frame-system-benchmarking",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
@@ -141,4 +143,10 @@ runtime-benchmarks = [
"pallet-template/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"cumulus-pallet-session-benchmarking/runtime-benchmarks",
]
try-runtime = [
"frame-try-runtime",
"frame-executive/try-runtime",
]
+42 -45
View File
@@ -23,7 +23,7 @@ use sp_version::RuntimeVersion;
use frame_support::{
construct_runtime, match_type, parameter_types,
traits::Everything,
traits::{Everything, Nothing},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND},
DispatchClass, IdentityFee, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients,
@@ -33,7 +33,7 @@ use frame_support::{
};
use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureOneOf, EnsureRoot,
EnsureRoot,
};
pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
pub use sp_runtime::{MultiAddress, Perbill, Permill};
@@ -42,7 +42,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill};
pub use sp_runtime::BuildStorage;
// Polkadot Imports
use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough};
use pallet_xcm::XcmPassthrough;
use polkadot_parachain::primitives::Sibling;
use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpdate};
@@ -50,10 +50,10 @@ use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpd
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,
ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SovereignSignedViaLocation,
TakeWeightCredit, UsingComponents,
EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentIsDefault,
RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
UsingComponents,
};
use xcm_executor::{Config, XcmExecutor};
@@ -96,6 +96,7 @@ pub type BlockId = generic::BlockId<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
frame_system::CheckNonZeroSender<Runtime>,
frame_system::CheckSpecVersion<Runtime>,
frame_system::CheckTxVersion<Runtime>,
frame_system::CheckGenesis<Runtime>,
@@ -117,19 +118,9 @@ pub type Executive = frame_executive::Executive<
Block,
frame_system::ChainContext<Runtime>,
Runtime,
AllPallets,
OnRuntimeUpgrade,
AllPalletsWithSystem,
>;
pub struct OnRuntimeUpgrade;
impl frame_support::traits::OnRuntimeUpgrade for OnRuntimeUpgrade {
fn on_runtime_upgrade() -> u64 {
frame_support::migrations::migrate_from_pallet_version_to_storage_version::<
AllPalletsWithSystem,
>(&RocksDbWeight::get())
}
}
/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the
/// node's balance type.
///
@@ -189,6 +180,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 1,
};
/// This determines the average expected block time that we are targeting.
@@ -213,12 +205,9 @@ pub const UNIT: Balance = 1_000_000_000_000;
pub const MILLIUNIT: Balance = 1_000_000_000;
pub const MICROUNIT: Balance = 1_000_000;
/// The existential deposit. Set to 1/10 of the Rococo Relay Chain.
/// The existential deposit. Set to 1/10 of the Connected Relay Chain.
pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT;
// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks.
pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is
/// used to limit the maximal weight of a single extrinsic.
const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5);
@@ -315,6 +304,7 @@ impl frame_system::Config for Runtime {
type SS58Prefix = SS58Prefix;
/// The action to take on a Runtime Upgrade
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -390,14 +380,12 @@ impl cumulus_pallet_parachain_system::Config for Runtime {
type ReservedXcmpWeight = ReservedXcmpWeight;
}
impl pallet_randomness_collective_flip::Config for Runtime {}
impl parachain_info::Config for Runtime {}
impl cumulus_pallet_aura_ext::Config for Runtime {}
parameter_types! {
pub const RocLocation: MultiLocation = MultiLocation::parent();
pub const RelayLocation: MultiLocation = MultiLocation::parent();
pub const RelayNetwork: NetworkId = NetworkId::Any;
pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();
pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();
@@ -420,7 +408,7 @@ pub type LocalAssetTransactor = CurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<RocLocation>,
IsConcrete<RelayLocation>,
// Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
@@ -438,14 +426,11 @@ pub type XcmOriginToTransactDispatchOrigin = (
// foreign chains who want to have a local sovereign account on this chain which they control.
SovereignSignedViaLocation<LocationToAccountId, Origin>,
// Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
// recognised.
// recognized.
RelayChainAsNative<RelayChainOrigin, Origin>,
// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
// recognised.
// recognized.
SiblingParachainAsNative<cumulus_pallet_xcm::Origin, Origin>,
// Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a
// transaction from the Root origin.
ParentAsSuperuser<Origin>,
// Native signed account converter; this just converts an `AccountId32` origin into a normal
// `Origin::Signed` origin of the same 32-byte value.
SignedAccountId32AsNative<RelayNetwork, Origin>,
@@ -482,11 +467,11 @@ impl Config for XcmConfig {
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = XcmOriginToTransactDispatchOrigin;
type IsReserve = NativeAsset;
type IsTeleporter = NativeAsset; // Should be enough to allow teleportation of ROC
type IsTeleporter = (); // Teleporting is disabled.
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
type Trader = UsingComponents<IdentityFee<Balance>, RocLocation, AccountId, Balances, ()>;
type Trader = UsingComponents<IdentityFee<Balance>, RelayLocation, AccountId, Balances, ()>;
type ResponseHandler = PolkadotXcm;
type AssetTrap = PolkadotXcm;
type AssetClaims = PolkadotXcm;
@@ -500,7 +485,7 @@ parameter_types! {
}
/// No local origins on this chain are allowed to dispatch XCM sends/executions.
pub type LocalOriginToLocation = ();
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, RelayNetwork>;
/// The means for routing XCM messages which are not for local execution into the right message
/// queues.
@@ -516,16 +501,19 @@ impl pallet_xcm::Config for Runtime {
type SendXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecuteFilter = Nothing;
// ^ Disable dispatchable execute on the XCM pallet.
// Needs to be `Everything` for local testing.
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Everything;
type XcmReserveTransferFilter = Everything;
type XcmReserveTransferFilter = Nothing;
type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
type LocationInverter = LocationInverter<Ancestry>;
type Origin = Origin;
type Call = Call;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
// ^ Override for AdvertisedXcmVersion default
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
}
@@ -539,6 +527,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime {
type XcmExecutor = XcmExecutor<XcmConfig>;
type ChannelInfo = ParachainSystem;
type VersionWrapper = ();
type ExecuteOverweightOrigin = EnsureRoot<AccountId>;
}
impl cumulus_pallet_dmp_queue::Config for Runtime {
@@ -582,12 +571,8 @@ parameter_types! {
pub const ExecutiveBody: BodyId = BodyId::Executive;
}
// We allow root and the Relay Chain council to execute privileged collator selection operations.
pub type CollatorSelectionUpdateOrigin = EnsureOneOf<
AccountId,
EnsureRoot<AccountId>,
EnsureXcm<IsMajorityOfBody<RocLocation, ExecutiveBody>>,
>;
// We allow root only to execute privileged collator selection operations.
pub type CollatorSelectionUpdateOrigin = EnsureRoot<AccountId>;
impl pallet_collator_selection::Config for Runtime {
type Event = Event;
@@ -622,9 +607,8 @@ construct_runtime!(
ParachainSystem: cumulus_pallet_parachain_system::{
Pallet, Call, Config, Storage, Inherent, Event<T>, ValidateUnsigned,
} = 1,
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage} = 2,
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 3,
ParachainInfo: parachain_info::{Pallet, Storage, Config} = 4,
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 2,
ParachainInfo: parachain_info::{Pallet, Storage, Config} = 3,
// Monetary stuff.
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,
@@ -755,6 +739,18 @@ impl_runtime_apis! {
}
}
#[cfg(feature = "try-runtime")]
impl frame_try_runtime::TryRuntime<Block> for Runtime {
fn on_runtime_upgrade() -> (Weight, Weight) {
log::info!("try-runtime::on_runtime_upgrade parachain-template.");
let weight = Executive::try_runtime_upgrade().unwrap();
(weight, RuntimeBlockWeights::get().max_block)
}
fn execute_block_no_check(block: Block) -> Weight {
Executive::execute_block_no_check(block)
}
}
#[cfg(feature = "runtime-benchmarks")]
impl frame_benchmarking::Benchmark<Block> for Runtime {
@@ -771,6 +767,7 @@ impl_runtime_apis! {
list_benchmark!(list, extra, frame_system, SystemBench::<Runtime>);
list_benchmark!(list, extra, pallet_balances, Balances);
list_benchmark!(list, extra, pallet_session, SessionBench::<Runtime>);
list_benchmark!(list, extra, pallet_timestamp, Timestamp);
list_benchmark!(list, extra, pallet_collator_selection, CollatorSelection);
+22 -10
View File
@@ -1,9 +1,9 @@
[package]
name = "polkadot-collator"
version = "4.0.0"
version = "5.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
edition = "2018"
edition = "2021"
[[bin]]
name = "polkadot-collator"
@@ -14,21 +14,22 @@ futures = { version = "0.3.1", features = ["compat"] }
log = "0.4.8"
codec = { package = "parity-scale-codec", version = "2.3.0" }
structopt = "0.3.3"
serde = { version = "1.0.101", features = ["derive"] }
serde = { version = "1.0.132", features = ["derive"] }
hex-literal = "0.2.1"
async-trait = "0.1.42"
# Parachain runtimes
rococo-parachain-runtime = { package = "rococo-runtime", path = "rococo" }
rococo-parachain-runtime = { path = "rococo-parachain" }
shell-runtime = { path = "shell" }
seedling-runtime = { path = "seedling" }
statemint-runtime = { path = "statemint" }
statemine-runtime = { path = "statemine" }
westmint-runtime = { path = "westmint" }
parachains-common = { path = "parachains-common" }
# Substrate dependencies
frame-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-benchmarking-cli = { git = 'https://github.com/paritytech/substrate', branch = "master" }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -57,6 +58,9 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" }
substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" }
# try-runtime stuff.
try-runtime-cli = { git = "https://github.com/paritytech/substrate", branch = "master" }
# RPC related dependencies
jsonrpc-core = "18.0.0"
sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -72,6 +76,8 @@ cumulus-client-service = { path = "../client/service" }
cumulus-client-network = { path = "../client/network" }
cumulus-primitives-core = { path = "../primitives/core" }
cumulus-primitives-parachain-inherent = { path = "../primitives/parachain-inherent" }
cumulus-relay-chain-interface = { path = "../client/relay-chain-interface" }
cumulus-relay-chain-local = { path = "../client/relay-chain-local" }
# Polkadot dependencies
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" }
@@ -90,8 +96,14 @@ tempfile = "3.2.0"
[features]
default = []
runtime-benchmarks = [
'polkadot-service/runtime-benchmarks',
'statemint-runtime/runtime-benchmarks',
'statemine-runtime/runtime-benchmarks',
'westmint-runtime/runtime-benchmarks',
"polkadot-service/runtime-benchmarks",
"statemint-runtime/runtime-benchmarks",
"statemine-runtime/runtime-benchmarks",
"westmint-runtime/runtime-benchmarks",
]
try-runtime = [
'statemint-runtime/try-runtime',
'statemine-runtime/try-runtime',
'westmint-runtime/try-runtime',
'shell-runtime/try-runtime',
]
@@ -1,13 +1,13 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
name = "parachain-info"
version = "0.1.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+2 -2
View File
@@ -1,13 +1,13 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
name = "cumulus-ping"
version = "0.1.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
@@ -2,63 +2,63 @@
name = "parachains-common"
version = "1.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
description = "Logic which is common to all parachain runtimes"
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
# External dependencies
codec = { package = 'parity-scale-codec', version = '2.3.0', features = ['derive'], default-features = false }
codec = { package = "parity-scale-codec", version = "2.3.0", features = ["derive"], default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
# Substrate dependencies
sp-consensus-aura = { 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-io = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
frame-executive = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
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-assets = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
pallet-authorship = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
pallet-balances = { 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-core = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
sp-consensus-aura = { 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-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
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-asset-tx-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-balances = { 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-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
# Polkadot dependencies
polkadot-runtime-common = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
polkadot-primitives = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
xcm = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
xcm-executor = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "master" }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "master" }
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "master" }
xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "master" }
# Local dependencies
pallet-asset-tx-payment = { path = '../../pallets/asset-tx-payment', default-features = false }
pallet-collator-selection = { path = '../../pallets/collator-selection', default-features = false }
pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false }
[dev-dependencies]
sp-io = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
pallet-authorship = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
pallet-authorship = { git = "https://github.com/paritytech/substrate", default-features = false , branch = "master" }
[build-dependencies]
substrate-wasm-builder = { git = 'https://github.com/paritytech/substrate', branch = "master" }
substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = ["std"]
std = [
'codec/std',
'scale-info/std',
'sp-consensus-aura/std',
'sp-std/std',
'sp-io/std',
'frame-support/std',
'frame-executive/std',
'frame-system/std',
'pallet-asset-tx-payment/std',
'pallet-collator-selection/std',
'pallet-assets/std',
'pallet-authorship/std',
'pallet-balances/std',
'polkadot-runtime-common/std',
'polkadot-primitives/std',
"codec/std",
"scale-info/std",
"sp-consensus-aura/std",
"sp-std/std",
"sp-io/std",
"frame-support/std",
"frame-executive/std",
"frame-system/std",
"pallet-asset-tx-payment/std",
"pallet-collator-selection/std",
"pallet-assets/std",
"pallet-authorship/std",
"pallet-balances/std",
"polkadot-runtime-common/std",
"polkadot-primitives/std",
]
@@ -47,10 +47,10 @@ where
let numeric_amount = amount.peek();
let staking_pot = <pallet_collator_selection::Pallet<R>>::account_id();
<pallet_balances::Pallet<R>>::resolve_creating(&staking_pot, amount);
<frame_system::Pallet<R>>::deposit_event(pallet_balances::Event::Deposit(
staking_pot,
numeric_amount,
));
<frame_system::Pallet<R>>::deposit_event(pallet_balances::Event::Deposit {
who: staking_pot,
amount: numeric_amount,
});
}
}
@@ -84,9 +84,10 @@ where
From<polkadot_primitives::v1::AccountId> + Into<polkadot_primitives::v1::AccountId>,
{
fn handle_credit(credit: CreditOf<AccountIdOf<R>, pallet_assets::Pallet<R>>) {
let author = pallet_authorship::Pallet::<R>::author();
// In case of error: Will drop the result triggering the `OnDrop` of the imbalance.
let _ = pallet_assets::Pallet::<R>::resolve(&author, credit);
if let Some(author) = pallet_authorship::Pallet::<R>::author() {
// In case of error: Will drop the result triggering the `OnDrop` of the imbalance.
let _ = pallet_assets::Pallet::<R>::resolve(&author, credit);
}
}
}
@@ -178,6 +179,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_balances::Config for Test {
@@ -47,7 +47,7 @@ mod types {
pub type Hash = sp_core::H256;
/// Digest item type.
pub type DigestItem = sp_runtime::generic::DigestItem<Hash>;
pub type DigestItem = sp_runtime::generic::DigestItem;
// Aura consensus authority.
pub type AuraId = sp_consensus_aura::sr25519::AuthorityId;
@@ -0,0 +1 @@
0x000000000000000000000000000000000000000000000000000000000000000000c1ef26b567de07159e4ecd415fbbb0340c56a09c4d72c82516d0f3bc2b782c8003170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c11131400
File diff suppressed because one or more lines are too long
+1
View File
@@ -10,6 +10,7 @@
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"ss58Format": 2,
"tokenDecimals": 12,
"tokenSymbol": "KSM"
},
@@ -8,6 +8,7 @@
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"ss58Format": 2,
"tokenDecimals": 12,
"tokenSymbol": "KSM"
},
@@ -1,8 +1,8 @@
[package]
name = 'rococo-runtime'
version = '0.1.0'
name = "rococo-parachain-runtime"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = '2018'
edition = "2021"
description = "Simple runtime used by the rococo parachain(s)"
[dependencies]
@@ -10,7 +10,7 @@ codec = { package = "parity-scale-codec", version = "2.3.0", default-features =
log = { version = "0.4.14", default-features = false }
parachain-info = { path = "../pallets/parachain-info", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
# Substrate dependencies
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
@@ -32,7 +32,6 @@ frame-system = { git = "https://github.com/paritytech/substrate", default-featur
frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-assets = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
@@ -85,7 +84,6 @@ std = [
"frame-system/std",
"pallet-assets/std",
"pallet-balances/std",
"pallet-randomness-collective-flip/std",
"pallet-timestamp/std",
"pallet-sudo/std",
"pallet-transaction-payment/std",
@@ -38,7 +38,7 @@ use sp_version::RuntimeVersion;
// A few exports that help ease life for downstream crates.
pub use frame_support::{
construct_runtime, match_type, parameter_types,
traits::{Everything, IsInVec, Randomness},
traits::{EnsureOneOf, Everything, IsInVec, Randomness},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
DispatchClass, IdentityFee, Weight,
@@ -47,7 +47,7 @@ pub use frame_support::{
};
use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureOneOf, EnsureRoot,
EnsureRoot,
};
pub use pallet_balances::Call as BalancesCall;
pub use pallet_timestamp::Call as TimestampCall;
@@ -60,7 +60,10 @@ use parachains_common::{
impls::{AssetsFrom, NonZeroIssuance},
AssetId,
};
use xcm_builder::{AsPrefixedGeneralIndex, ConvertedConcreteAssetId, FungiblesAdapter};
use xcm_builder::{
AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex,
ConvertedConcreteAssetId, FungiblesAdapter,
};
use xcm_executor::traits::JustTry;
// XCM imports
@@ -94,6 +97,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 0,
};
pub const MILLISECS_PER_BLOCK: u64 = 12000;
@@ -185,13 +189,14 @@ impl frame_system::Config for Runtime {
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type DbWeight = RocksDbWeight;
type BaseCallFilter = frame_support::traits::Everything;
type SystemWeightInfo = ();
type BlockWeights = RuntimeBlockWeights;
type BlockLength = RuntimeBlockLength;
type SS58Prefix = SS58Prefix;
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -229,8 +234,6 @@ impl pallet_balances::Config for Runtime {
type ReserveIdentifier = [u8; 8];
}
impl pallet_randomness_collective_flip::Config for Runtime {}
parameter_types! {
pub const OperationalFeeMultiplier: u8 = 5;
}
@@ -310,7 +313,7 @@ pub type FungiblesTransactor = FungiblesAdapter<
ConvertedConcreteAssetId<
AssetId,
u64,
AsPrefixedGeneralIndex<StatemintLocation, AssetId, JustTry>,
AsPrefixedGeneralIndex<StatemintAssetsPalletLocation, AssetId, JustTry>,
JustTry,
>,
// Convert an XCM MultiLocation into a local account id:
@@ -351,8 +354,8 @@ pub type XcmOriginToTransactDispatchOrigin = (
);
parameter_types! {
// One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.
pub UnitWeightCost: Weight = 1_000_000;
// One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
pub UnitWeightCost: Weight = 1_000_000_000;
// One ROC buys 1 second of weight.
pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), ROC);
pub const MaxInstructions: u32 = 100;
@@ -376,11 +379,19 @@ pub type Barrier = (
AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>,
// ^^^ Parent & its unit plurality gets free execution
AllowUnpaidExecutionFrom<Statemint>,
// Expected responses are OK.
AllowKnownQueryResponses<PolkadotXcm>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<Everything>,
);
parameter_types! {
pub StatemintLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000)));
pub MaxAssetsIntoHolding: u32 = 64;
pub StatemintLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000)));
// ALWAYS ensure that the index in PalletInstance stays up-to-date with
// Statemint's Assets pallet index
pub StatemintAssetsPalletLocation: MultiLocation =
MultiLocation::new(1, X2(Parachain(1000), PalletInstance(50)));
}
pub type Reserves = (NativeAsset, AssetsFrom<StatemintLocation>);
@@ -445,6 +456,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime {
type XcmExecutor = XcmExecutor<XcmConfig>;
type ChannelInfo = ParachainSystem;
type VersionWrapper = ();
type ExecuteOverweightOrigin = EnsureRoot<AccountId>;
}
impl cumulus_pallet_dmp_queue::Config for Runtime {
@@ -462,6 +474,7 @@ impl cumulus_ping::Config for Runtime {
parameter_types! {
pub const AssetDeposit: Balance = 1 * ROC;
pub const AssetAccountDeposit: Balance = 1 * ROC;
pub const ApprovalDeposit: Balance = 100 * MILLIROC;
pub const AssetsStringLimit: u32 = 50;
pub const MetadataDepositBase: Balance = 1 * ROC;
@@ -471,11 +484,8 @@ parameter_types! {
}
/// A majority of the Unit body from Rococo over XCM is our required administration origin.
pub type AdminOrigin = EnsureOneOf<
AccountId,
EnsureRoot<AccountId>,
EnsureXcm<IsMajorityOfBody<RocLocation, UnitBody>>,
>;
pub type AdminOrigin =
EnsureOneOf<EnsureRoot<AccountId>, EnsureXcm<IsMajorityOfBody<RocLocation, UnitBody>>>;
impl pallet_assets::Config for Runtime {
type Event = Event;
@@ -491,6 +501,7 @@ impl pallet_assets::Config for Runtime {
type Freezer = ();
type Extra = ();
type WeightInfo = pallet_assets::weights::SubstrateWeight<Runtime>;
type AssetAccountDeposit = AssetAccountDeposit;
}
impl pallet_aura::Config for Runtime {
@@ -508,7 +519,6 @@ construct_runtime! {
System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>},
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage},
TransactionPayment: pallet_transaction_payment::{Pallet, Storage},
ParachainSystem: cumulus_pallet_parachain_system::{
@@ -557,6 +567,7 @@ pub type SignedBlock = generic::SignedBlock<Block>;
pub type BlockId = generic::BlockId<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
frame_system::CheckNonZeroSender<Runtime>,
frame_system::CheckSpecVersion<Runtime>,
frame_system::CheckGenesis<Runtime>,
frame_system::CheckEra<Runtime>,
@@ -574,9 +585,20 @@ pub type Executive = frame_executive::Executive<
Block,
frame_system::ChainContext<Runtime>,
Runtime,
AllPallets,
AllPalletsWithSystem,
RemoveCollectiveFlip,
>;
pub struct RemoveCollectiveFlip;
impl frame_support::traits::OnRuntimeUpgrade for RemoveCollectiveFlip {
fn on_runtime_upgrade() -> Weight {
use frame_support::storage::migration;
// Remove the storage value `RandomMaterial` from removed pallet `RandomnessCollectiveFlip`
migration::remove_storage_prefix(b"RandomnessCollectiveFlip", b"RandomMaterial", b"");
<Runtime as frame_system::Config>::DbWeight::get().writes(1)
}
}
impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
@@ -1,70 +1,66 @@
[package]
name = "cumulus-test-runtime-upgrade"
version = "0.1.0"
name = 'seedling-runtime'
version = '0.1.0'
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
edition = "2021"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] }
log = { version = "0.4.14", default-features = false }
parachain-info = { path = "../pallets/parachain-info", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
# Substrate dependencies
frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-block-builder = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-offchain = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-offchain = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-block-builder = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
# Cumulus dependencies
cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false }
cumulus-primitives-core = { path = "../../primitives/core", default-features = false }
cumulus-primitives-timestamp = { path = "../../primitives/timestamp", default-features = false }
[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = [ "std", "upgrade" ]
default = [ "std" ]
std = [
"codec/std",
"scale-info/std",
"serde",
"log/std",
"sp-api/std",
"sp-std/std",
"sp-io/std",
"sp-core/std",
"sp-runtime/std",
"sp-version/std",
"sp-offchain/std",
"sp-session/std",
"sp-block-builder/std",
"sp-transaction-pool/std",
"sp-inherents/std",
"frame-support/std",
"frame-executive/std",
"frame-system/std",
"pallet-sudo/std",
"pallet-balances/std",
"parachain-info/std",
"cumulus-pallet-parachain-system/std",
"cumulus-primitives-core/std",
"cumulus-primitives-timestamp/std",
"frame-executive/std",
"frame-support/std",
"frame-system/std",
"pallet-balances/std",
"pallet-randomness-collective-flip/std",
"pallet-sudo/std",
"pallet-timestamp/std",
"pallet-transaction-payment/std",
"serde",
"sp-api/std",
"sp-block-builder/std",
"sp-core/std",
"sp-inherents/std",
"sp-io/std",
"sp-offchain/std",
"sp-runtime/std",
"sp-session/std",
"sp-std/std",
"sp-transaction-pool/std",
"sp-version/std",
]
upgrade = []
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate 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.
// Substrate 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
use substrate_wasm_builder::WasmBuilder;
fn main() {
WasmBuilder::new()
.with_current_project()
.export_heap_base()
.import_memory()
.build()
}
+327
View File
@@ -0,0 +1,327 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus 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.
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
#![cfg_attr(not(feature = "std"), no_std)]
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
#![recursion_limit = "256"]
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use sp_api::impl_runtime_apis;
use sp_core::OpaqueMetadata;
use sp_runtime::{
create_runtime_str, generic,
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult,
};
use sp_std::prelude::*;
#[cfg(feature = "std")]
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
// A few exports that help ease life for downstream crates.
pub use frame_support::{
construct_runtime, match_type, parameter_types,
traits::{Contains, IsInVec, Randomness},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
DispatchClass, IdentityFee, Weight,
},
StorageValue,
};
use frame_system::limits::{BlockLength, BlockWeights};
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
pub use sp_runtime::{Perbill, Permill};
/// This runtime version.
#[sp_version::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("seedling"),
impl_name: create_runtime_str!("seedling"),
authoring_version: 1,
spec_version: 1,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 0,
};
/// The version information used to identify this runtime when compiled natively.
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
}
/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers.
/// This is used to limit the maximal weight of a single extrinsic.
const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);
/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used
/// by Operational extrinsics.
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
/// We allow for .5 seconds of compute with a 12 second average block time.
const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND / 2;
parameter_types! {
pub const BlockHashCount: BlockNumber = 250;
pub const Version: RuntimeVersion = VERSION;
pub RuntimeBlockLength: BlockLength =
BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()
.base_block(BlockExecutionWeight::get())
.for_class(DispatchClass::all(), |weights| {
weights.base_extrinsic = ExtrinsicBaseWeight::get();
})
.for_class(DispatchClass::Normal, |weights| {
weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);
})
.for_class(DispatchClass::Operational, |weights| {
weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
// Operational transactions have some extra reserved space, so that they
// are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.
weights.reserved = Some(
MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT
);
})
.avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
.build_or_panic();
pub const SS58Prefix: u8 = 42;
}
pub struct BaseFilter;
impl Contains<Call> for BaseFilter {
fn contains(c: &Call) -> bool {
// Disallow everything that is not set_validation_data or set_code
match c {
Call::ParachainSystem(cumulus_pallet_parachain_system::Call::set_validation_data {
..
}) => true,
Call::Sudo(pallet_sudo::Call::sudo_unchecked_weight { call: ref x, .. }) => {
matches!(x.as_ref(), &Call::System(frame_system::Call::set_code { .. }))
},
_ => false,
}
}
}
impl frame_system::Config for Runtime {
/// The identifier used to distinguish between accounts.
type AccountId = AccountId;
/// The aggregated dispatch type that is available for extrinsics.
type Call = Call;
/// The lookup mechanism to get account ID from whatever is passed in dispatchers.
type Lookup = AccountIdLookup<AccountId, ()>;
/// The index type for storing how many extrinsics an account has signed.
type Index = Index;
/// The index type for blocks.
type BlockNumber = BlockNumber;
/// The type for hashing blocks and tries.
type Hash = Hash;
/// The hashing algorithm used.
type Hashing = BlakeTwo256;
/// The header type.
type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// The ubiquitous event type.
type Event = Event;
/// The ubiquitous origin type.
type Origin = Origin;
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
type BlockHashCount = BlockHashCount;
/// Runtime version.
type Version = Version;
/// Converts a module to an index of this module in the runtime.
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u128>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = BaseFilter;
type SystemWeightInfo = ();
type BlockWeights = RuntimeBlockWeights;
type BlockLength = RuntimeBlockLength;
type SS58Prefix = SS58Prefix;
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_sudo::Config for Runtime {
type Call = Call;
type Event = Event;
}
impl cumulus_pallet_parachain_system::Config for Runtime {
type Event = Event;
type OnValidationData = ();
type SelfParaId = parachain_info::Pallet<Runtime>;
type OutboundXcmpMessageSource = ();
type DmpMessageHandler = ();
type ReservedDmpWeight = ();
type XcmpMessageHandler = ();
type ReservedXcmpWeight = ();
}
impl parachain_info::Config for Runtime {}
construct_runtime! {
pub enum Runtime where
Block = Block,
NodeBlock = generic::Block<Header, sp_runtime::OpaqueExtrinsic>,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>},
ParachainSystem: cumulus_pallet_parachain_system::{
Pallet, Call, Config, Storage, Inherent, Event<T>, ValidateUnsigned,
},
ParachainInfo: parachain_info::{Pallet, Storage, Config},
}
}
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = sp_runtime::MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as sp_runtime::traits::Verify>::Signer as sp_runtime::traits::IdentifyAccount>::AccountId;
/// Index of a transaction in the chain.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = sp_core::H256;
/// An index to a block.
pub type BlockNumber = u32;
/// The address format for describing accounts.
pub type Address = sp_runtime::MultiAddress<AccountId, ()>;
/// Block header type as expected by this runtime.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Block type as expected by this runtime.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// A Block signed with a Justification
pub type SignedBlock = generic::SignedBlock<Block>;
/// BlockId type as expected by this runtime.
pub type BlockId = generic::BlockId<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
frame_system::CheckSpecVersion<Runtime>,
frame_system::CheckTxVersion<Runtime>,
frame_system::CheckGenesis<Runtime>,
frame_system::CheckEra<Runtime>,
frame_system::CheckNonce<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;
/// Executive: handles dispatch to the various modules.
pub type Executive = frame_executive::Executive<
Runtime,
Block,
frame_system::ChainContext<Runtime>,
Runtime,
AllPalletsWithSystem,
>;
impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
VERSION
}
fn execute_block(block: Block) {
Executive::execute_block(block)
}
fn initialize_block(header: &<Block as BlockT>::Header) {
Executive::initialize_block(header)
}
}
impl sp_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
OpaqueMetadata::new(Runtime::metadata().into())
}
}
impl sp_block_builder::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(
extrinsic: <Block as BlockT>::Extrinsic,
) -> ApplyExtrinsicResult {
Executive::apply_extrinsic(extrinsic)
}
fn finalize_block() -> <Block as BlockT>::Header {
Executive::finalize_block()
}
fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
data.create_extrinsics()
}
fn check_inherents(block: Block, data: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult {
data.check_extrinsics(&block)
}
}
impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(
source: TransactionSource,
tx: <Block as BlockT>::Extrinsic,
block_hash: <Block as BlockT>::Hash,
) -> TransactionValidity {
Executive::validate_transaction(source, tx, block_hash)
}
}
impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(header: &<Block as BlockT>::Header) {
Executive::offchain_worker(header)
}
}
impl sp_session::SessionKeys<Block> for Runtime {
fn decode_session_keys(_: Vec<u8>) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
Some(Vec::new())
}
fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
Vec::new()
}
}
impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {
fn collect_collation_info() -> cumulus_primitives_core::CollationInfo {
ParachainSystem::collect_collation_info()
}
}
}
struct CheckInherents;
impl cumulus_pallet_parachain_system::CheckInherents<Block> for CheckInherents {
fn check_inherents(
_: &Block,
_: &cumulus_pallet_parachain_system::RelayChainStateProof,
) -> sp_inherents::CheckInherentsResult {
sp_inherents::CheckInherentsResult::new()
}
}
cumulus_pallet_parachain_system::register_validate_block! {
Runtime = Runtime,
BlockExecutor = Executive,
CheckInherents = CheckInherents,
}
+11 -4
View File
@@ -1,15 +1,15 @@
[package]
name = 'shell-runtime'
version = '0.1.0'
name = "shell-runtime"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = '2018'
edition = "2021"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] }
log = { version = "0.4.14", default-features = false }
parachain-info = { path = "../pallets/parachain-info", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
serde = { version = "1.0.132", optional = true, features = ["derive"] }
# Substrate dependencies
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
@@ -28,6 +28,9 @@ frame-support = { git = "https://github.com/paritytech/substrate", default-featu
frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
# try-runtime stuff.
frame-try-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
# Cumulus dependencies
cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false }
cumulus-primitives-core = { path = "../../primitives/core", default-features = false }
@@ -72,3 +75,7 @@ std = [
"xcm-builder/std",
"xcm-executor/std",
]
try-runtime = [
"frame-try-runtime",
"frame-executive/try-runtime",
]
+16 -4
View File
@@ -23,12 +23,13 @@
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use codec::{Decode, Encode};
use frame_support::unsigned::TransactionValidityError;
use scale_info::TypeInfo;
use sp_api::impl_runtime_apis;
use sp_core::OpaqueMetadata;
use sp_runtime::{
create_runtime_str, generic,
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, DispatchInfoOf},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult,
};
@@ -70,6 +71,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 0,
};
/// The version information used to identify this runtime when compiled natively.
@@ -150,6 +152,7 @@ impl frame_system::Config for Runtime {
type BlockLength = RuntimeBlockLength;
type SS58Prefix = SS58Prefix;
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -194,8 +197,8 @@ match_type! {
}
parameter_types! {
// One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.
pub UnitWeightCost: Weight = 1_000_000;
// One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
pub UnitWeightCost: Weight = 1_000_000_000;
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
@@ -256,6 +259,15 @@ impl sp_runtime::traits::SignedExtension for DisallowSigned {
) -> sp_std::result::Result<(), sp_runtime::transaction_validity::TransactionValidityError> {
Ok(())
}
fn pre_dispatch(
self,
who: &Self::AccountId,
call: &Self::Call,
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(self.validate(who, call, info, len).map(|_| ())?)
}
fn validate(
&self,
_who: &Self::AccountId,
@@ -301,7 +313,7 @@ pub type Executive = frame_executive::Executive<
Block,
frame_system::ChainContext<Runtime>,
Runtime,
AllPallets,
AllPalletsWithSystem,
>;
impl_runtime_apis! {
+102 -64
View File
@@ -30,6 +30,10 @@ pub type ChainSpec =
/// Specialized `ChainSpec` for the shell parachain runtime.
pub type ShellChainSpec = sc_service::GenericChainSpec<shell_runtime::GenesisConfig, Extensions>;
/// Specialized `ChainSpec` for the seedling parachain runtime.
pub type SeedlingChainSpec =
sc_service::GenericChainSpec<seedling_runtime::GenesisConfig, Extensions>;
/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
@@ -64,7 +68,7 @@ where
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
pub fn get_chain_spec(id: ParaId) -> ChainSpec {
pub fn get_chain_spec() -> ChainSpec {
ChainSpec::from_genesis(
"Local Testnet",
"local_testnet",
@@ -87,32 +91,51 @@ pub fn get_chain_spec(id: ParaId) -> ChainSpec {
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
None,
Extensions { relay_chain: "westend".into(), para_id: id.into() },
Extensions { relay_chain: "westend".into(), para_id: 1000 },
)
}
pub fn get_shell_chain_spec(id: ParaId) -> ShellChainSpec {
pub fn get_shell_chain_spec() -> ShellChainSpec {
ShellChainSpec::from_genesis(
"Shell Local Testnet",
"shell_local_testnet",
ChainType::Local,
move || shell_testnet_genesis(id),
vec![],
move || shell_testnet_genesis(1000.into()),
Vec::new(),
None,
None,
None,
Extensions { relay_chain: "westend".into(), para_id: id.into() },
Extensions { relay_chain: "westend".into(), para_id: 1000 },
)
}
pub fn staging_test_net(id: ParaId) -> ChainSpec {
pub fn get_seedling_chain_spec() -> SeedlingChainSpec {
SeedlingChainSpec::from_genesis(
"Seedling Local Testnet",
"seedling_local_testnet",
ChainType::Local,
move || {
seedling_testnet_genesis(
get_account_id_from_seed::<sr25519::Public>("Alice"),
2000.into(),
)
},
Vec::new(),
None,
None,
None,
Extensions { relay_chain: "westend".into(), para_id: 2000 },
)
}
pub fn staging_test_net() -> ChainSpec {
ChainSpec::from_genesis(
"Staging Testnet",
"staging_testnet",
@@ -131,14 +154,14 @@ pub fn staging_test_net(id: ParaId) -> ChainSpec {
vec![
hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into()
],
id,
1000.into(),
)
},
Vec::new(),
None,
None,
None,
Extensions { relay_chain: "westend".into(), para_id: id.into() },
Extensions { relay_chain: "westend".into(), para_id: 1000 },
)
}
@@ -153,12 +176,11 @@ fn testnet_genesis(
code: rococo_parachain_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
},
balances: rococo_parachain_runtime::BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
},
sudo: rococo_parachain_runtime::SudoConfig { key: root_key },
sudo: rococo_parachain_runtime::SudoConfig { key: Some(root_key) },
parachain_info: rococo_parachain_runtime::ParachainInfoConfig { parachain_id: id },
aura: rococo_parachain_runtime::AuraConfig { authorities: initial_authorities },
aura_ext: Default::default(),
@@ -172,13 +194,28 @@ fn shell_testnet_genesis(parachain_id: ParaId) -> shell_runtime::GenesisConfig {
code: shell_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
},
parachain_info: shell_runtime::ParachainInfoConfig { parachain_id },
parachain_system: Default::default(),
}
}
fn seedling_testnet_genesis(
root_key: AccountId,
parachain_id: ParaId,
) -> seedling_runtime::GenesisConfig {
seedling_runtime::GenesisConfig {
system: seedling_runtime::SystemConfig {
code: seedling_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
},
sudo: seedling_runtime::SudoConfig { key: Some(root_key) },
parachain_info: seedling_runtime::ParachainInfoConfig { parachain_id },
parachain_system: Default::default(),
}
}
use parachains_common::Balance as StatemintBalance;
/// Specialized `ChainSpec` for the normal parachain runtime.
@@ -194,7 +231,7 @@ const STATEMINE_ED: StatemintBalance = statemine_runtime::constants::currency::E
const WESTMINT_ED: StatemintBalance = westmint_runtime::constants::currency::EXISTENTIAL_DEPOSIT;
/// Helper function to generate a crypto pair from seed
pub fn get_pair_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
pub fn get_public_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
@@ -204,7 +241,7 @@ pub fn get_pair_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair
///
/// This function's return type must always match the session keys of the chain in tuple format.
pub fn get_collator_keys_from_seed(seed: &str) -> AuraId {
get_pair_from_seed::<AuraId>(seed)
get_public_from_seed::<AuraId>(seed)
}
/// Generate the session keys from individual elements.
@@ -228,7 +265,7 @@ pub fn westmint_session_keys(keys: AuraId) -> westmint_runtime::SessionKeys {
westmint_runtime::SessionKeys { aura: keys }
}
pub fn statemint_development_config(id: ParaId) -> StatemintChainSpec {
pub fn statemint_development_config() -> StatemintChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "DOT".into());
properties.insert("tokenDecimals".into(), 10.into());
@@ -252,18 +289,18 @@ pub fn statemint_development_config(id: ParaId) -> StatemintChainSpec {
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "polkadot-dev".into(), para_id: id.into() },
Extensions { relay_chain: "polkadot-dev".into(), para_id: 1000 },
)
}
pub fn statemint_local_config(id: ParaId) -> StatemintChainSpec {
pub fn statemint_local_config() -> StatemintChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "DOT".into());
properties.insert("tokenDecimals".into(), 10.into());
@@ -301,14 +338,14 @@ pub fn statemint_local_config(id: ParaId) -> StatemintChainSpec {
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "polkadot-local".into(), para_id: id.into() },
Extensions { relay_chain: "polkadot-local".into(), para_id: 1000 },
)
}
@@ -322,7 +359,6 @@ fn statemint_genesis(
code: statemint_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
},
balances: statemint_runtime::BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, STATEMINT_ED * 4096)).collect(),
@@ -335,12 +371,11 @@ fn statemint_genesis(
},
session: statemint_runtime::SessionConfig {
keys: invulnerables
.iter()
.cloned()
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
acc.clone(), // validator id
acc, // validator id
statemint_session_keys(aura), // session keys
)
})
@@ -354,8 +389,9 @@ fn statemint_genesis(
}
}
pub fn statemine_development_config(id: ParaId) -> StatemineChainSpec {
pub fn statemine_development_config() -> StatemineChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("ss58Format".into(), 2.into());
properties.insert("tokenSymbol".into(), "KSM".into());
properties.insert("tokenDecimals".into(), 12.into());
@@ -378,19 +414,20 @@ pub fn statemine_development_config(id: ParaId) -> StatemineChainSpec {
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "kusama-dev".into(), para_id: id.into() },
Extensions { relay_chain: "kusama-dev".into(), para_id: 1000 },
)
}
pub fn statemine_local_config(id: ParaId) -> StatemineChainSpec {
pub fn statemine_local_config() -> StatemineChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("ss58Format".into(), 2.into());
properties.insert("tokenSymbol".into(), "KSM".into());
properties.insert("tokenDecimals".into(), 12.into());
@@ -427,19 +464,20 @@ pub fn statemine_local_config(id: ParaId) -> StatemineChainSpec {
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "kusama-local".into(), para_id: id.into() },
Extensions { relay_chain: "kusama-local".into(), para_id: 1000 },
)
}
pub fn statemine_config(id: ParaId) -> StatemineChainSpec {
pub fn statemine_config() -> StatemineChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("ss58Format".into(), 2.into());
properties.insert("tokenSymbol".into(), "KSM".into());
properties.insert("tokenDecimals".into(), 12.into());
@@ -478,15 +516,15 @@ pub fn statemine_config(id: ParaId) -> StatemineChainSpec {
.unchecked_into(),
),
],
vec![],
id,
Vec::new(),
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "kusama".into(), para_id: id.into() },
Extensions { relay_chain: "kusama".into(), para_id: 1000 },
)
}
@@ -500,10 +538,13 @@ fn statemine_genesis(
code: statemine_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
},
balances: statemine_runtime::BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, STATEMINE_ED * 4096)).collect(),
balances: endowed_accounts
.iter()
.cloned()
.map(|k| (k, STATEMINE_ED * 524_288))
.collect(),
},
parachain_info: statemine_runtime::ParachainInfoConfig { parachain_id: id },
collator_selection: statemine_runtime::CollatorSelectionConfig {
@@ -513,12 +554,11 @@ fn statemine_genesis(
},
session: statemine_runtime::SessionConfig {
keys: invulnerables
.iter()
.cloned()
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
acc.clone(), // validator id
acc, // validator id
statemine_session_keys(aura), // session keys
)
})
@@ -530,7 +570,7 @@ fn statemine_genesis(
}
}
pub fn westmint_development_config(id: ParaId) -> WestmintChainSpec {
pub fn westmint_development_config() -> WestmintChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "WND".into());
properties.insert("tokenDecimals".into(), 12.into());
@@ -555,18 +595,18 @@ pub fn westmint_development_config(id: ParaId) -> WestmintChainSpec {
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "westend".into(), para_id: id.into() },
Extensions { relay_chain: "westend".into(), para_id: 1000 },
)
}
pub fn westmint_local_config(id: ParaId) -> WestmintChainSpec {
pub fn westmint_local_config() -> WestmintChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "WND".into());
properties.insert("tokenDecimals".into(), 12.into());
@@ -605,18 +645,18 @@ pub fn westmint_local_config(id: ParaId) -> WestmintChainSpec {
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "westend-local".into(), para_id: id.into() },
Extensions { relay_chain: "westend-local".into(), para_id: 1000 },
)
}
pub fn westmint_config(id: ParaId) -> WestmintChainSpec {
pub fn westmint_config() -> WestmintChainSpec {
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "WND".into());
properties.insert("tokenDecimals".into(), 12.into());
@@ -656,17 +696,17 @@ pub fn westmint_config(id: ParaId) -> WestmintChainSpec {
.unchecked_into(),
),
],
vec![],
Vec::new(),
// re-use the Westend sudo key
hex!("6648d7f3382690650c681aba1b993cd11e54deb4df21a3a18c3e2177de9f7342").into(),
id,
1000.into(),
)
},
vec![],
Vec::new(),
None,
None,
Some(properties),
Extensions { relay_chain: "westend".into(), para_id: id.into() },
Extensions { relay_chain: "westend".into(), para_id: 1000 },
)
}
@@ -681,12 +721,11 @@ fn westmint_genesis(
code: westmint_runtime::WASM_BINARY
.expect("WASM binary was not build, please build it!")
.to_vec(),
changes_trie_config: Default::default(),
},
balances: westmint_runtime::BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, WESTMINT_ED * 4096)).collect(),
},
sudo: westmint_runtime::SudoConfig { key: root_key },
sudo: westmint_runtime::SudoConfig { key: Some(root_key) },
parachain_info: westmint_runtime::ParachainInfoConfig { parachain_id: id },
collator_selection: westmint_runtime::CollatorSelectionConfig {
invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(),
@@ -695,12 +734,11 @@ fn westmint_genesis(
},
session: westmint_runtime::SessionConfig {
keys: invulnerables
.iter()
.cloned()
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
acc.clone(), // validator id
acc, // validator id
westmint_session_keys(aura), // session keys
)
})

Some files were not shown because too many files have changed in this diff Show More