Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ members = [
"modules/ismp/clients/polygon",
"modules/ismp/clients/tendermint",
"modules/pallets/collator-manager",
"modules/ismp/clients/beefy",

# cryptography
"modules/consensus/sync-committee/prover",
"modules/consensus/sync-committee/verifier",
"modules/consensus/sync-committee/primitives",
"modules/consensus/beefy/primitives",
"modules/consensus/beefy/prover",
"modules/consensus/beefy/verifier",
"modules/consensus/geth-primitives",
"modules/consensus/bsc/verifier",
"modules/consensus/bsc/prover",
Expand Down Expand Up @@ -205,6 +207,7 @@ reqwest-middleware = "0.2.4"
reqwest = { version="0.11.14", features=["json"]}
prost = { version = "0.13.0", default-features = false }
impl-trait-for-tuples = "0.2.3"
k256 = { version = "0.13.3", default-features = false, features = ["ecdsa"] }

# arkworks
ark-ec = { version = "0.4.2", default-features = false }
Expand Down Expand Up @@ -249,6 +252,7 @@ subxt-utils = { path = "modules/utils/subxt", default-features = false }
# consensus provers & verifiers
beefy-verifier-primitives = { path = "./modules/consensus/beefy/primitives", default-features = false }
beefy-prover = { path = "./modules/consensus/beefy/prover" }
beefy-verifier = { path = "./modules/consensus/beefy/verifier" }
bsc-prover = { path = "./modules/consensus/bsc/prover" }
bsc-verifier = { path = "./modules/consensus/bsc/verifier", default-features = false }
geth-primitives = { path = "./modules/consensus/geth-primitives", default-features = false }
Expand Down Expand Up @@ -290,6 +294,7 @@ pallet-state-coprocessor = { path = "modules/pallets/state-coprocessor", default
pallet-intents-coprocessor = { path = "modules/pallets/intents-coprocessor", default-features = false }
pallet-token-gateway-inspector = { path = "modules/pallets/token-gateway-inspector", default-features = false }
pallet-bridge-airdrop = { path = "modules/pallets/bridge-drop", default-features = false }
ismp-beefy = { path = "modules/ismp/clients/beefy", default-features = false}

# merkle trees
ethereum-triedb = { version = "0.1.1", path = "./modules/trees/ethereum", default-features = false }
Expand Down Expand Up @@ -386,4 +391,6 @@ features = ["derive"]
version = "0.5.0"
default-features = false

[patch.crates-io]
[workspace.dependencies.rs_merkle]
version = "1.2.0"
default-features = false
63 changes: 59 additions & 4 deletions modules/consensus/beefy/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,27 @@ pub struct PartialMmrLeaf {
pub beefy_next_authority_set: BeefyAuthoritySet<H256>,
}

#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq)]
#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq, Encode, Decode)]
/// Parachain header and metadata needed for merkle inclusion proof
pub struct ParachainHeader {
/// scale encoded parachain header
pub header: Vec<u8>,
/// leaf index for parachain heads proof
pub index: usize,
pub index: u32,
/// ParaId for parachain
pub para_id: u32,
}

#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq)]
#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq, Encode, Decode)]
/// Parachain proofs definition
pub struct ParachainProof {
/// List of parachains we have a proof for
pub parachains: Vec<ParachainHeader>,

/// Proof for parachain header inclusion in the parachain headers root
pub proof: Vec<Vec<(usize, [u8; 32])>>,
pub proof: Vec<[u8; 32]>,
/// Total leaves count for the proof
pub total_leaves: u32,
}

#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq)]
Expand All @@ -119,6 +121,59 @@ pub struct ConsensusMessage {
pub mmr: MmrProof,
}

/// Represents a node in a Merkle proof, containing a hash and its index at a specific layer.
#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct Node {
/// The positional index of the node in its layer of the Merkle tree.
pub index: u32,
/// The hash of the node.
pub hash: H256,
}

/// Represents a canonical BEEFY Merkle Mountain Range (MMR) leaf.
///
/// This struct contains the essential data about a finalized block that is committed to the MMR.
#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct BeefyMmrLeaf {
/// The version of the MMR leaf format.
pub version: MmrLeafVersion,
/// A tuple containing the block number and hash of the parent block.
pub parent_block_and_hash: (u32, H256),
/// The authority set that will be active in the next BEEFY session.
pub beefy_next_authority_set: BeefyAuthoritySet<H256>,
/// The k-index of the leaf, used in MMR calculations.
pub k_index: u32,
/// The sequential index of this leaf in the MMR.
pub leaf_index: u32,
/// An extra data field
pub extra: H256,
}

/// Represents the proof components for verifying the relay chain's consensus state
#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct RelaychainProof {
/// Signed commitment
pub signed_commitment: SignedCommitment,
/// Latest leaf added to mmr
pub latest_mmr_leaf: BeefyMmrLeaf,
/// Proof for the latest mmr leaf
pub mmr_proof: Vec<H256>,
/// Proof for authorities in current/next session
pub proof: Vec<H256>,
}

/// Represents a complete BEEFY consensus proof.
///
/// This proof contains all the necessary data to verify a BEEFY finality proof from the relay chain
/// and to prove the inclusion of specific parachain headers within that finalized block.
#[derive(sp_std::fmt::Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct BeefyConsensusProof {
/// The proof items for the relay chain consensus
pub relay: RelaychainProof,
/// The proof items for parachain headers
pub parachain: ParachainProof,
}

#[cfg(feature = "std")]
#[derive(Clone, serde::Serialize, serde::Deserialize)]
/// finality proof
Expand Down
6 changes: 4 additions & 2 deletions modules/consensus/beefy/prover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl<R: Config, P: Config> Prover<R, P> {
(
ParachainHeader {
header: heads[index].1.clone(),
index,
index: index as u32,
para_id: heads[index].0,
},
index,
Expand All @@ -251,7 +251,9 @@ impl<R: Config, P: Config> Prover<R, P> {
let leaves = heads.iter().map(|pair| keccak_256(&pair.encode())).collect::<Vec<_>>();
let proof = util::merkle_proof(&leaves, &indices);

let parachain = ParachainProof { parachains, proof };
let proof: Vec<[u8; 32]> = proof.into_iter().flatten().map(|(_, hash)| hash).collect();

let parachain = ParachainProof { parachains, proof, total_leaves: leaves.len() as u32 };

Ok(ConsensusMessage { mmr, parachain })
}
Expand Down
55 changes: 55 additions & 0 deletions modules/consensus/beefy/verifier/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[package]
name = "beefy-verifier"
version = "0.1.0"
edition = "2024"
authors = ["Polytope Labs <hello@polytope.technology>"]
description = "Verifier for the BEEFY consensus client"
publish = false

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
log = { workspace = true }
anyhow = { workspace = true, default-features = false }
primitive-types = { workspace = true, features = ["codec"] }
codec = { workspace = true, features = ["derive"], default-features = true }
beefy-verifier-primitives = { workspace = true }
ismp = { workspace = true, default-features = false }
merkle-mountain-range = {workspace = true, default-features = false}
rs_merkle = {workspace = true}
k256 = { workspace = true }
beefy-prover = { workspace = true}
subxt = { workspace = true, default-features = true }
subxt-core = { workspace = true, default-features = true }
subxt-utils = { workspace = true, default-features = true }
futures = { workspace = true }
tokio = { workspace = true }
thiserror = { workspace = true }

[dependencies.polkadot-sdk]
workspace = true
features = [
"sp-core",
"sp-io",
"sp-runtime"
]

[dev-dependencies]


[features]
default = ["std"]
std = [
"log/std",
"anyhow/std",
"codec/std",
"ismp/std",
"primitive-types/std",
"beefy-verifier-primitives/std",
"polkadot-sdk/std",
"merkle-mountain-range/std",
"rs_merkle/std",
"k256/std"

]
26 changes: 26 additions & 0 deletions modules/consensus/beefy/verifier/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Stale height: trusted height {trusted_height} >= current_height {current_height}")]
StaleHeight { trusted_height: u32, current_height: u32 },
#[error("Super majority of signatures required")]
SuperMajorityRequired,
#[error("Unkown authority set id {id}")]
UnknownAuthoritySet { id: u64 },
#[error("MMR root hash is missing from commitment payload")]
MmrRootHashMissing,
#[error("Invalid MMR root hash length: expected 32, found {len}")]
InvalidMmrRootHashLength { len: usize },
#[error("Invalid signature recovery ID")]
InvalidRecoveryId,
#[error("Invalid signature format")]
InvalidSignatureFormat,
#[error("Failed to recover public key from signature")]
FailedToRecoverPublicKey,
#[error("Invalid authorities proof")]
InvalidAuthoritiesProof,
#[error("MMR verification failed during calculation: {0}")]
MmrVerificationFailed(String),
#[error("Invalid MMR proof: calculated root does not match provided root")]
InvalidMmrProof,
}
Loading