Skip to content

Commit

Permalink
remove duplicate parachain heads exension (paritytech#1444)
Browse files Browse the repository at this point in the history
* remove duplicate parachain heads exension

* fix benchmarks compilation

* actually fix it
  • Loading branch information
svyatonik authored and serban300 committed Apr 8, 2024
1 parent 45e0614 commit d1a112c
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 35 deletions.
12 changes: 11 additions & 1 deletion bridges/bin/millau/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,11 @@ pallet_bridge_grandpa::declare_bridge_reject_obsolete_grandpa_header! {
Call::BridgeWestendGrandpa => WestendGrandpaInstance
}

pallet_bridge_parachains::declare_bridge_reject_obsolete_parachain_header! {
Runtime,
Call::BridgeRialtoParachains => WithRialtoParachainsInstance
}

/// The address format for describing accounts.
pub type Address = AccountId;
/// Block header type as expected by this runtime.
Expand All @@ -586,6 +591,7 @@ pub type SignedExtra = (
frame_system::CheckWeight<Runtime>,
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
BridgeRejectObsoleteGrandpaHeader,
BridgeRejectObsoleteParachainHeader,
);
/// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
Expand Down Expand Up @@ -994,7 +1000,11 @@ impl_runtime_apis! {
parachains: &[bp_polkadot_core::parachains::ParaId],
parachain_head_size: u32,
proof_size: bp_runtime::StorageProofSize,
) -> (pallet_bridge_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof) {
) -> (
pallet_bridge_parachains::RelayBlockNumber,
pallet_bridge_parachains::RelayBlockHash,
bp_polkadot_core::parachains::ParaHeadsProof,
) {
bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof::<Runtime, WithRialtoParachainsInstance>(
parachains,
parachain_head_size,
Expand Down
11 changes: 6 additions & 5 deletions bridges/bin/runtime-common/src/messages_benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ where
// finally - prepare storage proof and update environment
let (state_root, storage_proof) =
prepare_messages_storage_proof::<B, BHH>(&params, message_payload);
let bridged_header_hash = insert_header_to_grandpa_pallet::<R, FI>(state_root);
let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(state_root);

(
FromBridgedChainMessagesProof {
Expand Down Expand Up @@ -132,7 +132,7 @@ where
let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect();

// finally insert header with given state root to our storage
let bridged_header_hash = insert_header_to_grandpa_pallet::<R, FI>(root);
let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::<R, FI>(root);

FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: bridged_header_hash.into(),
Expand Down Expand Up @@ -207,22 +207,23 @@ where
/// Insert header to the bridge GRANDPA pallet.
pub(crate) fn insert_header_to_grandpa_pallet<R, GI>(
state_root: bp_runtime::HashOf<R::BridgedChain>,
) -> bp_runtime::HashOf<R::BridgedChain>
) -> (bp_runtime::BlockNumberOf<R::BridgedChain>, bp_runtime::HashOf<R::BridgedChain>)
where
R: pallet_bridge_grandpa::Config<GI>,
GI: 'static,
R::BridgedChain: bp_runtime::Chain,
{
let bridged_block_number = Zero::zero();
let bridged_header = bp_runtime::HeaderOf::<R::BridgedChain>::new(
Zero::zero(),
bridged_block_number,
Default::default(),
state_root,
Default::default(),
Default::default(),
);
let bridged_header_hash = bridged_header.hash();
pallet_bridge_grandpa::initialize_for_benchmarks::<R, GI>(bridged_header);
bridged_header_hash
(bridged_block_number, bridged_header_hash)
}

/// Populate trie with dummy keys+values until trie has at least given size.
Expand Down
10 changes: 5 additions & 5 deletions bridges/bin/runtime-common/src/parachains_benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use bp_polkadot_core::parachains::{ParaHead, ParaHeadsProof, ParaId};
use bp_runtime::StorageProofSize;
use codec::Encode;
use frame_support::traits::Get;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher};
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use sp_std::prelude::*;
use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut};

Expand All @@ -37,13 +37,13 @@ pub fn prepare_parachain_heads_proof<R, PI>(
parachains: &[ParaId],
parachain_head_size: u32,
size: StorageProofSize,
) -> (RelayBlockHash, ParaHeadsProof)
) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof)
where
R: pallet_bridge_parachains::Config<PI>
+ pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>,
PI: 'static,
<R as pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash>,
bp_runtime::Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash>,
{
let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]);

Expand Down Expand Up @@ -73,8 +73,8 @@ where
.expect("record_all_keys should not fail in benchmarks");
let proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect();

let relay_block_hash =
let (relay_block_number, relay_block_hash) =
insert_header_to_grandpa_pallet::<R, R::BridgesGrandpaPalletInstance>(state_root);

(relay_block_hash, ParaHeadsProof(proof))
(relay_block_number, relay_block_hash, ParaHeadsProof(proof))
}
5 changes: 5 additions & 0 deletions bridges/modules/grandpa/src/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
/// Call::BridgeWestendGrandpa => WestendGrandpaInstance,
/// }
/// ```
///
/// The goal of this extension is to avoid "mining" transactions that provide
/// outdated bridged chain headers. Without that extension, even honest relayers
/// may lose their funds if there are multiple relays running and submitting the
/// same information.
#[macro_export]
macro_rules! declare_bridge_reject_obsolete_grandpa_header {
($runtime:ident, $($call:path => $instance:ty),*) => {
Expand Down
19 changes: 10 additions & 9 deletions bridges/modules/parachains/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub trait Config<I: 'static>: crate::Config<I> {
parachains: &[ParaId],
parachain_head_size: u32,
proof_size: StorageProofSize,
) -> (RelayBlockHash, ParaHeadsProof);
) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof);
}

benchmarks_instance_pallet! {
Expand All @@ -57,12 +57,13 @@ benchmarks_instance_pallet! {

let sender = account("sender", 0, 0);
let parachains = (1..=p).map(ParaId).collect::<Vec<_>>();
let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
let (relay_block_number, relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
&parachains,
DEFAULT_PARACHAIN_HEAD_SIZE,
StorageProofSize::Minimal(0),
);
}: submit_parachain_heads(RawOrigin::Signed(sender), relay_block_hash, parachains.clone(), parachain_heads_proof)
let at_relay_block = (relay_block_number, relay_block_hash);
}: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains.clone(), parachain_heads_proof)
verify {
for parachain in parachains {
assert!(crate::Pallet::<T, I>::best_parachain_head(parachain).is_some());
Expand All @@ -73,13 +74,13 @@ benchmarks_instance_pallet! {
submit_parachain_heads_with_1kb_proof {
let sender = account("sender", 0, 0);
let parachains = vec![ParaId(1)];
let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
let (relay_block_number, relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
&parachains,
DEFAULT_PARACHAIN_HEAD_SIZE,
StorageProofSize::HasExtraNodes(1024),
);

}: submit_parachain_heads(RawOrigin::Signed(sender), relay_block_hash, parachains.clone(), parachain_heads_proof)
let at_relay_block = (relay_block_number, relay_block_hash);
}: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains.clone(), parachain_heads_proof)
verify {
for parachain in parachains {
assert!(crate::Pallet::<T, I>::best_parachain_head(parachain).is_some());
Expand All @@ -90,13 +91,13 @@ benchmarks_instance_pallet! {
submit_parachain_heads_with_16kb_proof {
let sender = account("sender", 0, 0);
let parachains = vec![ParaId(1)];
let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
let (relay_block_number, relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
&parachains,
DEFAULT_PARACHAIN_HEAD_SIZE,
StorageProofSize::HasExtraNodes(16 * 1024),
);

}: submit_parachain_heads(RawOrigin::Signed(sender), relay_block_hash, parachains.clone(), parachain_heads_proof)
let at_relay_block = (relay_block_number, relay_block_hash);
}: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains.clone(), parachain_heads_proof)
verify {
for parachain in parachains {
assert!(crate::Pallet::<T, I>::best_parachain_head(parachain).is_some());
Expand Down
195 changes: 195 additions & 0 deletions bridges/modules/parachains/src/extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common 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.

// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.

/// Declares a runtime-specific `BridgeRejectObsoleteParachainHeader` signed extension.
///
/// ## Example
///
/// ```nocompile
/// pallet_bridge_grandpa::declare_bridge_reject_obsolete_parachain_header!{
/// Runtime,
/// Call::BridgeRialtoParachains => RialtoGrandpaInstance,
/// Call::BridgeWestendParachains => WestendGrandpaInstance,
/// }
/// ```
///
/// The goal of this extension is to avoid "mining" transactions that provide
/// outdated bridged parachain heads. Without that extension, even honest relayers
/// may lose their funds if there are multiple relays running and submitting the
/// same information.
///
/// This extension only works with transactions that are updating single parachain
/// head. We can't use unbounded validation - it may take too long and either break
/// block production, or "eat" significant portion of block production time literally
/// for nothing. In addition, the single-parachain-head-per-transaction is how the
/// pallet will be used in our environment.
#[macro_export]
macro_rules! declare_bridge_reject_obsolete_parachain_header {
($runtime:ident, $($call:path => $instance:ty),*) => {
/// Transaction-with-obsolete-bridged-parachain-header check that will reject transaction if
/// it submits obsolete bridged parachain header.
#[derive(Clone, codec::Decode, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)]
pub struct BridgeRejectObsoleteParachainHeader;

impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteParachainHeader {
const IDENTIFIER: &'static str = "BridgeRejectObsoleteParachainHeader";
type AccountId = <$runtime as frame_system::Config>::AccountId;
type Call = <$runtime as frame_system::Config>::Call;
type AdditionalSigned = ();
type Pre = ();

fn additional_signed(&self) -> sp_std::result::Result<
(),
sp_runtime::transaction_validity::TransactionValidityError,
> {
Ok(())
}

fn validate(
&self,
_who: &Self::AccountId,
call: &Self::Call,
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
_len: usize,
) -> sp_runtime::transaction_validity::TransactionValidity {
match *call {
$(
$call($crate::Call::<$runtime, $instance>::submit_parachain_heads {
ref at_relay_block,
ref parachains,
..
}) if parachains.len() == 1 => {
let parachain = parachains.get(0).expect("verified by match condition; qed");

let bundled_relay_block_number = at_relay_block.0;

let best_parachain_head = $crate::BestParaHeads::<$runtime, $instance>::get(parachain);
match best_parachain_head {
Some(best_parachain_head) if best_parachain_head.at_relay_block_number
>= bundled_relay_block_number =>
sp_runtime::transaction_validity::InvalidTransaction::Stale.into(),
_ => Ok(sp_runtime::transaction_validity::ValidTransaction::default()),
}
},
)*
_ => Ok(sp_runtime::transaction_validity::ValidTransaction::default()),
}
}

fn pre_dispatch(
self,
who: &Self::AccountId,
call: &Self::Call,
info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
len: usize,
) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
self.validate(who, call, info, len).map(drop)
}

fn post_dispatch(
_maybe_pre: Option<Self::Pre>,
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
_post_info: &sp_runtime::traits::PostDispatchInfoOf<Self::Call>,
_len: usize,
_result: &sp_runtime::DispatchResult,
) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> {
Ok(())
}
}
};
}

#[cfg(test)]
mod tests {
use crate::{
mock::{run_test, Call, TestRuntime},
BestParaHead, BestParaHeads, RelayBlockNumber,
};
use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
use frame_support::weights::{DispatchClass, DispatchInfo, Pays};
use sp_runtime::traits::SignedExtension;

declare_bridge_reject_obsolete_parachain_header! {
TestRuntime,
Call::Parachains => ()
}

fn validate_submit_parachain_heads(num: RelayBlockNumber, parachains: Vec<ParaId>) -> bool {
BridgeRejectObsoleteParachainHeader
.validate(
&42,
&Call::Parachains(crate::Call::<TestRuntime, ()>::submit_parachain_heads {
at_relay_block: (num, Default::default()),
parachains,
parachain_heads_proof: ParaHeadsProof(Vec::new()),
}),
&DispatchInfo { weight: 0, class: DispatchClass::Operational, pays_fee: Pays::Yes },
0,
)
.is_ok()
}

fn sync_to_relay_header_10() {
BestParaHeads::<TestRuntime, ()>::insert(
ParaId(1),
BestParaHead {
at_relay_block_number: 10,
head_hash: Default::default(),
next_imported_hash_position: 0,
},
);
}

#[test]
fn extension_rejects_obsolete_header() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#5 => tx is
// rejected
sync_to_relay_header_10();
assert!(!validate_submit_parachain_heads(5, vec![ParaId(1)]));
});
}

#[test]
fn extension_rejects_same_header() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#10 => tx is
// rejected
sync_to_relay_header_10();
assert!(!validate_submit_parachain_heads(10, vec![ParaId(1)]));
});
}

#[test]
fn extension_accepts_new_header() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#15 => tx is
// accepted
sync_to_relay_header_10();
assert!(validate_submit_parachain_heads(15, vec![ParaId(1)]));
});
}

#[test]
fn extension_accepts_if_more_than_one_parachain_is_submitted() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#5, but another
// parachain head is also supplied => tx is accepted
sync_to_relay_header_10();
assert!(validate_submit_parachain_heads(5, vec![ParaId(1), ParaId(2)]));
});
}
}
Loading

0 comments on commit d1a112c

Please sign in to comment.