Skip to content

[AHM] PoC for D-Day governance rescue feature on CollectivesWestend#8141

Draft
bkontur wants to merge 32 commits intomasterfrom
bko-ahm-dday-governance-poc
Draft

[AHM] PoC for D-Day governance rescue feature on CollectivesWestend#8141
bkontur wants to merge 32 commits intomasterfrom
bko-ahm-dday-governance-poc

Conversation

@bkontur
Copy link
Contributor

@bkontur bkontur commented Apr 2, 2025

Relates to: #5588 (comment)
Relates to: #7591
Relates to: @kianenigma's initial demo

TL;DR

This PR includes several key features:

  • CollectivesWestend D-Day referenda setup: Enables voting by AssetHub accounts (multi-key) with AssetHub proofs (+ showcases a unit test for voting with real proofs downloaded from AssetHubWestend).
  • SkipCheckIf transaction extension wrapper: Allows defining conditions under which the inner transaction extension execution can be skipped (e.g., in CollectivesWestend transaction extensions, skipping CheckNonce when valid proof).

Motivation and Context

Currently, governance on the relay chain has the ability to trigger Paras::force_set_current_code(Root, para_id, new_code), for example, to rescue a stalled parachain (although there may be other reasons as well).

A major thing is approaching: the AssetHub migration (AHM). One key aspect of this migration is the transfer of relay chain governance to AssetHub. This means that governance will no longer exist on the relay chain but will instead be fully migrated to AssetHub.

D-Day (Doomsday) governance serves as a backup plan or a last resort in case the AssetHub parachain stalls - that is, it stops producing blocks. In such a scenario, AssetHub’s governance would be unable to trigger Paras::force_set_current_code(..) on itself.

Proposed Solution

  • CollectivesWestend has configured a new pallet_bridge_proof_root_store:
    • Stores the Relay Chain block number and storage_root with OnSystemEvent
    • (This pallet is just cherry-picked from other Bridges PR for other use-case)
  • CollectivesWestend has configured a pallet_referenda instance (DDayReferendaInstance):
    • Only rank3+ fellows can submit and start referenda with a specific call, e.g.,
      pallet_xcm::send(Location::parent(), Xcm(Transact(Paras::authorize_force_set_current_code_hash(1000, fixed_code))))
  • CollectivesWestend has configured a new pallet_dday_voting: (copied from conviction-voting and adjusted)
    • Voting is allowed only if the AssetHub state root is present.
    • Voting via pallet_dday_voting::Call::vote(signed_user, proof) requires a valid AssetHub proof containing:
      • Account balance
      • Total and inactive issuance
    • The signed_user represents an (AssetHub) account signed with a secret key, and the accountId is used as part of the proof key.
    • The proof can be generated for a stalled AssetHub block using the RPC:
      state.getReadProof(keys, at)
    • The proof is validated against the stored AssetHub state root. A dedicated runtime API could also be added to simplify UI integration.
    • Total issuance/inactive issuance is updated in Tally to calculate support(..) (see also Questionable / Weak Points).

Questionable / Weak Points

  • DDay conviction voting requires knowledge of AssetHub’s "total" issuance (total - inactive):
    • Voting works only if AssetHub is stalled, meaning we have a single state root/block number.
    • Total and inactive issuance are part of the proofs, and for the same block number, they shouldn't change.
  • PoC does not expect/require an AssetHub account to have a balance on the Collectives chain:
    • The main issue arises with CheckNonce (verifying an account’s providers/sufficients) and ChargeTransactionPayment.
    • The current fix involves adding the SkipCheckIf transaction extension wrapper. If a DDayVoting::vote call is valid and includes valid proofs (only when AssetHub is stalled), then CheckNonce and ChargeTransactionPayment are skipped. (In other words, if AssetHub is not stalled, nothing is skipped.)
    • Uncertainty: Is this approach sufficient, or could it lead to potential free spamming? Does it create a vulnerability?
  • We could avoid RelayChain proofs, but then we need to read para head from RelayChainStateProof_** (see Follow-ups).
    • (This feature will be prioritized, as it will also optimize Bridges.)

Testing

  • Unit test demonstrating proof validation using real data downloaded from AssetHubWestend at a specific block:
    • fn asset_hub_account_prover_works() / fn verify_proof_root_works()
  • Unit test verifying DDayReferenda / DDayVoting by proof-based voting:
    • The vote is submitted as a real extrinsic to pass all transaction extensions.
    • The user does not have an account or balance on the Collectives chain.
    • fn dday_referenda_and_voting_works()

TODO

  • Review, decide, and confirm if this PR is the right approach; check questionable/weak points:
    • DDay conviction voting and active issuance?
    • Is SkipCheckIf acceptable? (If not then what alternative approach?)
  • Complete the PR:
    • Fix all code TODOs
    • Benchmarking and tests
    • Runtime API for pallet_dday_detection::LastKnownHead or pallet_dday_detection::IsStalled::stalled_head()

Follow-ups

Read para head from RelayChainStateProof:

Force setting the current code from a parachain to the relay chain:

@bkontur bkontur added T1-FRAME This PR/Issue is related to core FRAME, the framework. T14-system_parachains This PR/Issue is related to system parachains. labels Apr 2, 2025
@bkontur bkontur requested a review from a team as a code owner April 2, 2025 22:46
@bkontur
Copy link
Contributor Author

bkontur commented Apr 2, 2025

/cmd fmt

@bkontur
Copy link
Contributor Author

bkontur commented May 1, 2025

/cmd fmt

@bkontur bkontur force-pushed the bko-ahm-dday-governance-poc branch 2 times, most recently from c641d3f to 23eaf5c Compare May 9, 2025 21:38
github-merge-queue bot pushed a commit that referenced this pull request May 29, 2025
…7592)

Closes: #7574
Relates to: #7591

## Motivation

This feature is useful when triggering a `Paras` pallet call from a
different chain than the one where the `Paras` pallet is deployed. For
example, we may want to send `Paras::force_set_current_code(para, code)`
from the Collectives and/or AssetHub to the relay chain (because the
relaychain governance will be migrated to the AssetHub as a part of
AHM).

The primary reason for this approach is to avoid transferring the entire
`new_code` Wasm blob between chains. Instead, we authorize the
`code_hash` using `root` via `fn
authorize_force_set_current_code_hash(new_authorization, expire_at)`.
This authorization can later be applied by anyone using
`Paras::apply_authorized_force_set_current_code(para, new_code)`. If
`expire_at` is reached without the authorization being used, it is
automatically removed.

## Usage

This feature is intended for use in two scenarios:

- The D-Day scenario, where we can restart AssetHub from Collectives —
see the PoC: #8141
- Using `force_set_current_code` for any parachain from migrated
governance to AssetHub (AHM)

## TODO
- [x] ~cover also `add_trusted_validation_code` or
`force_schedule_code_upgrade` - see comment bellow:
#7592 (comment)
no see other
[comment](#7592 (comment))


## Open questions

- [ ] ~Do we need something like `poke_authorized_code_hash`? E.g. in
case that we authorize code hash, but nobody would apply it and the
parachain starts working with old/other_new code? Is this possible?~
- [ ] Do we need something similar for `frame_system` pallet and
`set_code` / `set_code_without_checks`?
- [ ] Can we achieve the same with `pallet-whitelist`?
- [ ] Do we have other extrinsics over chains which has `code`
attribute?
- [x] Do we need to add `validate_unsigned` for `apply_authorized_code`?

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
pgherveou pushed a commit that referenced this pull request Jun 11, 2025
…7592)

Closes: #7574
Relates to: #7591

## Motivation

This feature is useful when triggering a `Paras` pallet call from a
different chain than the one where the `Paras` pallet is deployed. For
example, we may want to send `Paras::force_set_current_code(para, code)`
from the Collectives and/or AssetHub to the relay chain (because the
relaychain governance will be migrated to the AssetHub as a part of
AHM).

The primary reason for this approach is to avoid transferring the entire
`new_code` Wasm blob between chains. Instead, we authorize the
`code_hash` using `root` via `fn
authorize_force_set_current_code_hash(new_authorization, expire_at)`.
This authorization can later be applied by anyone using
`Paras::apply_authorized_force_set_current_code(para, new_code)`. If
`expire_at` is reached without the authorization being used, it is
automatically removed.

## Usage

This feature is intended for use in two scenarios:

- The D-Day scenario, where we can restart AssetHub from Collectives —
see the PoC: #8141
- Using `force_set_current_code` for any parachain from migrated
governance to AssetHub (AHM)

## TODO
- [x] ~cover also `add_trusted_validation_code` or
`force_schedule_code_upgrade` - see comment bellow:
#7592 (comment)
no see other
[comment](#7592 (comment))


## Open questions

- [ ] ~Do we need something like `poke_authorized_code_hash`? E.g. in
case that we authorize code hash, but nobody would apply it and the
parachain starts working with old/other_new code? Is this possible?~
- [ ] Do we need something similar for `frame_system` pallet and
`set_code` / `set_code_without_checks`?
- [ ] Can we achieve the same with `pallet-whitelist`?
- [ ] Do we have other extrinsics over chains which has `code`
attribute?
- [x] Do we need to add `validate_unsigned` for `apply_authorized_code`?

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
@redzsina redzsina moved this from In progress to Backlog in Security Audit (PRs) - SRLabs Jun 30, 2025
…ame`

WIP: squash

WIP: squash to primitives

WIP: squash to voting
bkontur and others added 21 commits July 24, 2025 11:19
…o user does not need balance on Collectives)
…trate/frame`" (just reuse existing one from bridges to bring down PR diff noice)

This reverts commit c88b0cc4e639ccb0120626e8ee3d9a06922f859b.

# Conflicts:
#	substrate/frame/proofs/primitives/Cargo.toml
#	substrate/frame/proofs/primitives/src/lib.rs
#8326
#8325

Add proof root sync (`pallet-bridge-proof-root-sync`) as an ring buffer with on_idle callback
Add `OnNewHead` to `pallet-bridge-parachains`
@bkontur bkontur force-pushed the bko-ahm-dday-governance-poc branch from 23eaf5c to 01f3aff Compare August 1, 2025 23:59
@bkontur bkontur marked this pull request as draft August 2, 2025 00:00
@paritytech-workflow-stopper
Copy link

All GitHub workflows were cancelled due to failure one of the required jobs.
Failed workflow url: https://github.com/paritytech/polkadot-sdk/actions/runs/16687494536
Failed job name: fmt

@MN0B
Copy link

MN0B commented Aug 13, 2025

@bkontur is there an ETA for this ?

alvicsam pushed a commit that referenced this pull request Oct 17, 2025
…7592)

Closes: #7574
Relates to: #7591

## Motivation

This feature is useful when triggering a `Paras` pallet call from a
different chain than the one where the `Paras` pallet is deployed. For
example, we may want to send `Paras::force_set_current_code(para, code)`
from the Collectives and/or AssetHub to the relay chain (because the
relaychain governance will be migrated to the AssetHub as a part of
AHM).

The primary reason for this approach is to avoid transferring the entire
`new_code` Wasm blob between chains. Instead, we authorize the
`code_hash` using `root` via `fn
authorize_force_set_current_code_hash(new_authorization, expire_at)`.
This authorization can later be applied by anyone using
`Paras::apply_authorized_force_set_current_code(para, new_code)`. If
`expire_at` is reached without the authorization being used, it is
automatically removed.

## Usage

This feature is intended for use in two scenarios:

- The D-Day scenario, where we can restart AssetHub from Collectives —
see the PoC: #8141
- Using `force_set_current_code` for any parachain from migrated
governance to AssetHub (AHM)

## TODO
- [x] ~cover also `add_trusted_validation_code` or
`force_schedule_code_upgrade` - see comment bellow:
#7592 (comment)
no see other
[comment](#7592 (comment))


## Open questions

- [ ] ~Do we need something like `poke_authorized_code_hash`? E.g. in
case that we authorize code hash, but nobody would apply it and the
parachain starts working with old/other_new code? Is this possible?~
- [ ] Do we need something similar for `frame_system` pallet and
`set_code` / `set_code_without_checks`?
- [ ] Can we achieve the same with `pallet-whitelist`?
- [ ] Do we have other extrinsics over chains which has `code`
attribute?
- [x] Do we need to add `validate_unsigned` for `apply_authorized_code`?

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T1-FRAME This PR/Issue is related to core FRAME, the framework. T14-system_parachains This PR/Issue is related to system parachains.

Projects

Status: In Progress
Status: In review
Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants