Skip to content

Relayer: Cross-chain equivocation fishing #1493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 132 commits into
base: main
Choose a base branch
from

Conversation

Lederstrumpf
Copy link
Contributor

@Lederstrumpf Lederstrumpf commented Jun 10, 2025

Implements cross-chain equivocation fishing by the relayer: fishes for both future block & fork equivocations and reports them.

Monitoring for submit_init calls on Ethereum:

  1. If the commitment is to a block with height greater than what is finalized on the canonical relay chain, reports an equivocation via Beefy.report_future_block_voting.
  2. If the commitment's mmr payload diverges from the mmr payload of the canonical relay chain block at height commitment.BlockNumber, reports an equivocation via Beefy.report_fork_voting.

Uses the mmr_generateAncestryProof method introduced in paritytech/polkadot-sdk#9295, so please review using the SDK PR's branch rhmb/add-generate-ancestry-proof-mmr-rpc-call.

To test with smoketest, first instantiate local testnet:

pushd $SNOWBRIDGE_ROOT
nix develop .
pushd ./web/packages/test/ && ./scripts/start-services.sh

Then run the smoketest malicious_payload, which crafts a malicious payload that is signed by at least one validator.

pushd $SNOWBRIDGE_ROOT
pushd ./smoketest
./make-bindings.sh
# either run only equivocation tests
cargo test --test malicious_payload -- --nocapture
# or run full test suite
./run-tests.sh

List of PRs this one depends on:

  1. Add mmr_generateAncestryProof rpc call paritytech/polkadot-sdk#9295
  2. add generateAncestryProofResponse go-substrate-rpc-client#68

alistair-singh and others added 30 commits December 7, 2024 05:02
1. subscribes to NewTicket events
2. get the tx hash from a NewTicket event and retrieves the tx call data
from the Ethereum client
3. queries the relay chain for the canonical MMR payload and compares
these two results (currently only in log)
Flake lock file updates:

• Updated input 'flake-utils':
    'github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a?narHash=sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ%3D' (2024-03-11)
  → 'github:numtide/flake-utils/11707dc2f618dd54ca8739b309ec4fc024de578b?narHash=sha256-l0KFg5HjrsfsO/JpG%2Br7fRrqm12kzFHyUHqHCVpMMbI%3D' (2024-11-13)
• Updated input 'foundry':
    'github:shazow/foundry.nix/d84c83b1c1722c8742b3d2d84c9386814d75384e?narHash=sha256-wEDJdvwRZF2ErQ33nQ0Lqn/48XrPbaadv56/bM2MSZU%3D' (2024-08-03)
  → 'github:shazow/foundry.nix/33a209625b9e31227a5f11417e95a3ac7264d811?narHash=sha256-aLWyhJx2cO/M3/QLoDBpsObFfjC9e/VEN6HtaI0U6IA%3D' (2025-02-04)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/8a3354191c0d7144db9756a74755672387b702ba?narHash=sha256-Grh5PF0%2BgootJfOJFenTTxDTYPidA3V28dqJ/WV7iis%3D' (2024-08-18)
  → 'github:NixOS/nixpkgs/c6e957d81b96751a3d5967a0fd73694f303cc914?narHash=sha256-nLivjIygCiqLp5QcL7l56Tca/elVqM9FG1hGd9ZSsrg%3D' (2025-02-03)
pinned version from 2024-09-03
subxt metadata --url http://127.0.0.1:9944  --pallets Beefy > westend_relaychain_metadata.scale
subxt metadata --url http://127.0.0.1:9944  --pallets Beefy --runtime-apis '' > westend_relaychain_metadata.scale
relay chain state is irrelevant for crafting malicious payload
@Lederstrumpf Lederstrumpf marked this pull request as ready for review July 21, 2025 22:19
utility module is available on penpal on latest polkadot-sdk master, so
no need for the prior availability handling.
Comment on lines +243 to +244
github.com/lederstrumpf/go-substrate-rpc-client/v4 v4.2.0-alpha.1 h1:95S1KXEfRQxr6M/U4LMTjE5MMTYKY61PodtLUp0+Psc=
github.com/lederstrumpf/go-substrate-rpc-client/v4 v4.2.0-alpha.1/go.mod h1:x8Svd6jMXFQ+OMNsXX6Y8FlSZShBwGnsCMEXiJj3WtE=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we’re using a forked gsrpc repo — is that for encoding/decoding the new AncestryProof types? If possible, could you also link the upstream PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the upstream PR to your gsrpc repo is this one: Snowfork/go-substrate-rpc-client#68

I've just added an explicit list of PRs this one depends on to this PR's description

@Lederstrumpf Lederstrumpf force-pushed the rob/relayer-fishing branch from 7647ad2 to d11db21 Compare July 25, 2025 08:50
Lederstrumpf and others added 5 commits July 25, 2025 11:24
For the proof to be accepted, it's crucial that the payload (MMR root)
contained in the header matches the ancestry proof's best MMR root. If
the best block happened to get updated between both calls, this may not
be the case. Ergo: use the block hash of the header provided in the
equivocation proof for the ancestry proof generation call.

Co-authored-by: Ron <yrong1997@gmail.com>
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/c6e957d81b96751a3d5967a0fd73694f303cc914?narHash=sha256-nLivjIygCiqLp5QcL7l56Tca/elVqM9FG1hGd9ZSsrg%3D' (2025-02-03)
  → 'github:NixOS/nixpkgs/fc02ee70efb805d3b2865908a13ddd4474557ecf?narHash=sha256-i%2BCQV2rPmP8wHxj0aq4siYyohHwVlsh40kV89f3nw1s%3D' (2025-07-23)
@yrong
Copy link
Contributor

yrong commented Jul 28, 2025

@Lederstrumpf Awesome! I've tested this PR with a local setup, and the malicious_payload smoke test worked as expected - thanks for the contribution!

However, I noticed one issue: due to some of the refactoring in this PR, it seems the HRMP channels are sometimes not being built. I’ve proposed a potential fix and also included a few other minor improvements in a follow-up PR here: Lederstrumpf#1.

Please take a look and let me know if it makes sense.

Comment on lines +79 to +84
// TODO: for slashing, we may want to
// 1. scan older blocks as well if the latest BEEFY commitment stored on Ethereum doesn't match the commitment at that block # on the relay chain
// 2. potentially scan older blocks even if latest BEEFY commitment is sound (honest relayers may have saved the day, but the adversaries should still be slashed)
// 3. scan older tickets as well, since they may have optimistically tried their luck on the subsampling without a `submitFinal` call
// in all cases, this scan should not go past the slashability horizon, i.e. only be performed where the validators are still bonded
// this may also have to query the relay chain for any reported equivocations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we may need a seperate scanning process. Another reason is that the fisherman relay may not always work reliably - if it gets stuck for any reason, the adversary could potentially escape slashing during that time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants