Skip to content

Commit 93cb268

Browse files
[runtime] Restrict nexus beefy consensus proofs to SP1 (#900)
Co-authored-by: David Salami <wizdave97@gmail.com>
1 parent fb5002f commit 93cb268

7 files changed

Lines changed: 57 additions & 13 deletions

File tree

modules/consensus/beefy/verifier/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ pub enum Error {
9494
/// The ismp host this client runs on isn't itself a parachain (Polkadot or Kusama).
9595
#[error("Host state machine should be a parachain")]
9696
HostStateMachineNotParachain,
97+
/// The host is the coprocessor; the BEEFY client only seeds the coprocessor's own state
98+
/// machine commitment and must not serve a state machine client for proof verification.
99+
#[error("Host state machine is the coprocessor")]
100+
HostStateMachineIsCoprocessor,
97101
/// An MMR fraud proof failed to SCALE-decode.
98102
#[error("Cannot decode MMR proof: {0}")]
99103
DecodeMmrProof(String),

modules/ismp/clients/beefy/src/consensus.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ where
223223
}
224224

225225
fn state_machine(&self, id: StateMachine) -> Result<Box<dyn StateMachineClient>, Error> {
226+
// On the coprocessor the BEEFY client only seeds the coprocessor's own state machine
227+
// commitment; it must never be used to verify state proofs for incoming messages.
228+
let host = H::default();
229+
if Some(host.host_state_machine()) == host.allowed_proxy() {
230+
Err(BeefyError::HostStateMachineIsCoprocessor)?
231+
}
232+
226233
let para_id = match id {
227234
StateMachine::Polkadot(id) | StateMachine::Kusama(id) => id,
228235
_ => Err(BeefyError::UnsupportedStateMachine(id))?,

modules/pallets/beefy-consensus-proofs/src/lib.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ pub mod pallet {
7272
};
7373
use frame_system::pallet_prelude::*;
7474
use ismp::{
75-
consensus::{ConsensusStateId, StateMachineHeight, StateMachineId},
75+
consensus::{ConsensusStateId, StateCommitment, StateMachineHeight, StateMachineId},
7676
handlers,
7777
host::IsmpHost,
78-
messaging::{ConsensusMessage as IsmpConsensusMessage, Message},
78+
messaging::{ConsensusMessage as IsmpConsensusMessage, Message, StateCommitmentHeight},
7979
};
8080
use ismp_abi::ecdsa_beefy::BeefyConsensusState as SolBeefyConsensusState;
8181
use primitive_types::H256;
@@ -227,6 +227,8 @@ pub mod pallet {
227227
UnknownProofType,
228228
/// ABI decoding or conversion failed.
229229
AbiDecodeFailed,
230+
/// The submitted proof is not in canonical ABI form (e.g. trailing padding).
231+
MalformedProof,
230232
/// The BEEFY verifier rejected the proof.
231233
VerificationFailed,
232234
/// Rotation proof did not rotate to `NextAuthoritySetId`.
@@ -300,7 +302,12 @@ pub mod pallet {
300302
let current_set_id = state.current_authorities.id;
301303
let next_set_id = state.next_authorities.id;
302304
let latest_beefy_height = state.latest_beefy_height;
305+
let host = pallet_ismp::Pallet::<T>::default();
303306

307+
// Seed an initial commitment for the host state machine at the current block height.
308+
let height = sp_runtime::SaturatedConversion::saturated_into::<u64>(
309+
frame_system::Pallet::<T>::block_number(),
310+
);
304311
pallet_ismp::Pallet::<T>::create_consensus_client(
305312
frame_system::RawOrigin::Root.into(),
306313
ismp::messaging::CreateConsensusState {
@@ -309,7 +316,20 @@ pub mod pallet {
309316
consensus_state_id: ismp_beefy::BEEFY_CONSENSUS_ID,
310317
unbonding_period: T::UnbondingPeriod::get(),
311318
challenge_periods: Default::default(),
312-
state_machine_commitments: Default::default(),
319+
state_machine_commitments: vec![(
320+
StateMachineId {
321+
consensus_state_id: ismp_beefy::BEEFY_CONSENSUS_ID,
322+
state_id: host.host_state_machine(),
323+
},
324+
StateCommitmentHeight {
325+
height,
326+
commitment: StateCommitment {
327+
timestamp: host.timestamp().as_secs(),
328+
overlay_root: None,
329+
state_root: H256::zero(),
330+
},
331+
},
332+
)],
313333
},
314334
)
315335
.map_err(|e| {
@@ -459,6 +479,14 @@ pub mod pallet {
459479
canonical_proof.extend_from_slice(&canonical_payload);
460480
let proof_hash: H256 = sp_io::hashing::keccak_256(&canonical_proof).into();
461481

482+
// Reject any submission that isn't already in canonical form. If the raw input
483+
// hashes differently from its canonical re-encoding it carries non-canonical
484+
// bytes (trailing padding, alternate encodings), so it is malformed.
485+
let submitted_hash: H256 = sp_io::hashing::keccak_256(&proof).into();
486+
if submitted_hash != proof_hash {
487+
Err(Error::<T>::MalformedProof)?
488+
}
489+
462490
// Read the pre-proof consensus state before `verify_and_apply` mutates it.
463491
// Used to seed `ProofContext` for the first-proof path.
464492
let host = pallet_ismp::Pallet::<T>::default();

modules/pallets/relayer/src/accumulate.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use frame_support::{dispatch::DispatchResult, ensure};
3232
use ismp::{
3333
handlers::validate_state_machine,
3434
host::{IsmpHost, StateMachine},
35-
messaging::{Keccak256, Proof},
35+
messaging::Proof,
3636
};
3737
use pallet_ismp::child_trie::RequestCommitments;
3838
use polkadot_sdk::*;
@@ -179,6 +179,10 @@ where
179179
/// needs the `fee` field at offset 0, so for EVM sources the offset-0 slot is derived here
180180
/// directly rather than reusing the membership key.
181181
///
182+
/// The raw (unhashed) slot is returned for EVM sources: the fee path verifies through
183+
/// `verify_state_proof`, which hashes each key internally to derive the trie slot hash.
184+
/// Pre-hashing here would double-hash and miss the value.
185+
///
182186
/// Substrate sources store the whole `RequestMetadata` (fee included) under a single key, so
183187
/// `commitment_state_trie_key` already points at the fee for them.
184188
///
@@ -192,12 +196,13 @@ where
192196
commitments
193197
.iter()
194198
.map(|commitment| {
195-
let slot = derive_unhashed_map_key_with_offset::<<T as Config>::IsmpHost>(
199+
derive_unhashed_map_key_with_offset::<<T as Config>::IsmpHost>(
196200
commitment.0.to_vec(),
197201
REQUEST_COMMITMENTS_SLOT,
198202
0,
199-
);
200-
<T as Config>::IsmpHost::keccak256(&slot.0).0.to_vec()
203+
)
204+
.0
205+
.to_vec()
201206
})
202207
.collect()
203208
} else {

parachain/runtimes/gargantua/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
249249
spec_name: Cow::Borrowed("gargantua"),
250250
impl_name: Cow::Borrowed("gargantua"),
251251
authoring_version: 1,
252-
spec_version: 7_100,
252+
spec_version: 7_300,
253253
impl_version: 0,
254254
apis: RUNTIME_API_VERSIONS,
255255
transaction_version: 1,

parachain/runtimes/nexus/src/ismp.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
use crate::{
1717
alloc::{boxed::Box, string::ToString},
1818
weights, AccountId, Balance, Balances, Ismp, IsmpParachain, Mmr, ParachainInfo,
19-
ReputationAsset, Runtime, RuntimeEvent, Timestamp, TreasuryPalletId, EXISTENTIAL_DEPOSIT,
19+
ReputationAsset, Runtime, RuntimeEvent, Timestamp, TreasuryAccount, TreasuryPalletId,
20+
EXISTENTIAL_DEPOSIT,
2021
};
2122
use anyhow::anyhow;
2223
use evm_state_machine::SubstrateEvmStateMachine;
@@ -25,7 +26,7 @@ use frame_support::{
2526
parameter_types,
2627
traits::AsEnsureOriginWithArg,
2728
};
28-
use frame_system::EnsureRoot;
29+
use frame_system::{EnsureRoot, EnsureRootWithSuccess};
2930
use ismp::{
3031
consensus::StateMachineClient,
3132
error::Error,
@@ -264,7 +265,7 @@ impl pallet_assets::Config for Runtime {
264265
type AssetId = H256;
265266
type AssetIdParameter = H256;
266267
type Currency = Balances;
267-
type CreateOrigin = AsEnsureOriginWithArg<frame_system::EnsureSigned<AccountId32>>;
268+
type CreateOrigin = AsEnsureOriginWithArg<EnsureRootWithSuccess<AccountId32, TreasuryAccount>>;
268269
type ForceOrigin = EnsureRoot<AccountId32>;
269270
type AssetDeposit = AssetDeposit;
270271
type AssetAccountDeposit = AssetAccountDeposit;

parachain/runtimes/nexus/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
235235
spec_name: Cow::Borrowed("nexus"),
236236
impl_name: Cow::Borrowed("nexus"),
237237
authoring_version: 1,
238-
spec_version: 7_000,
238+
spec_version: 7_100,
239239
impl_version: 0,
240240
apis: RUNTIME_API_VERSIONS,
241241
transaction_version: 1,
@@ -924,7 +924,6 @@ parameter_types! {
924924

925925
parameter_types! {
926926
pub const AllowedBeefyProofTypes: &'static [u8] = &[
927-
pallet_beefy_consensus_proofs::types::PROOF_TYPE_NAIVE,
928927
pallet_beefy_consensus_proofs::types::PROOF_TYPE_SP1,
929928
];
930929
}

0 commit comments

Comments
 (0)