Skip to content

Commit

Permalink
Migrate Finality Verifier Pallet to FRAME v2 (#669)
Browse files Browse the repository at this point in the history
* Get pallet compiling with FRAME v2

* Get tests compiling

* Stop printing metadata in tests

* Remove more metadata related code

* Remove unecessary storage attribute
  • Loading branch information
HCastano authored and bkchr committed Apr 10, 2024
1 parent ea5d866 commit be59072
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 56 deletions.
115 changes: 62 additions & 53 deletions bridges/modules/finality-verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,73 +35,65 @@
use bp_header_chain::{justification::verify_justification, AncestryChecker, HeaderChain};
use bp_runtime::{Chain, HeaderOf};
use finality_grandpa::voter_set::VoterSet;
use frame_support::{decl_error, decl_module, decl_storage, dispatch::DispatchResult, ensure, traits::Get};
use frame_support::{ensure, traits::Get};
use frame_system::ensure_signed;
use sp_runtime::traits::Header as HeaderT;

#[cfg(test)]
mod mock;

/// Header of the bridged chain.
pub(crate) type BridgedHeader<T> = HeaderOf<<T as Config>::BridgedChain>;

/// The module configuration trait.
pub trait Config: frame_system::Config {
/// The chain we are bridging to here.
type BridgedChain: Chain;
/// The pallet which we will use as our underlying storage mechanism.
type HeaderChain: HeaderChain<<Self::BridgedChain as Chain>::Header>;
/// The type through which we will verify that a given header is related to the last
/// finalized header in our storage pallet.
type AncestryChecker: AncestryChecker<
<Self::BridgedChain as Chain>::Header,
Vec<<Self::BridgedChain as Chain>::Header>,
>;
/// The maximum length of headers we can have in a single ancestry proof. This prevents
/// unbounded iteration when verifying proofs.
type MaxHeadersInSingleProof: Get<u8>;
}

decl_storage! {
trait Store for Module<T: Config> as FinalityVerifier {}
}

decl_error! {
pub enum Error for Module<T: Config> {
/// The given justification is invalid for the given header.
InvalidJustification,
/// The given ancestry proof is unable to verify that the child and ancestor headers are
/// related.
InvalidAncestryProof,
/// The authority set from the underlying header chain is invalid.
InvalidAuthoritySet,
/// Failed to write a header to the underlying header chain.
FailedToWriteHeader,
/// Failed to write finality proof to the underlying header chain.
FailedToWriteFinalityProof,
/// The given ancestry proof is too large to be verified in a single transaction.
OversizedAncestryProof,
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

/// Header of the bridged chain.
pub(crate) type BridgedHeader<T> = HeaderOf<<T as Config>::BridgedChain>;

#[pallet::config]
pub trait Config: frame_system::Config {
/// The chain we are bridging to here.
type BridgedChain: Chain;

/// The pallet which we will use as our underlying storage mechanism.
type HeaderChain: HeaderChain<<Self::BridgedChain as Chain>::Header>;

/// The type through which we will verify that a given header is related to the last
/// finalized header in our storage pallet.
type AncestryChecker: AncestryChecker<
<Self::BridgedChain as Chain>::Header,
Vec<<Self::BridgedChain as Chain>::Header>,
>;

/// The maximum length of headers we can have in a single ancestry proof. This prevents
/// unbounded iteration when verifying proofs.
#[pallet::constant]
type MaxHeadersInSingleProof: Get<u8>;
}
}

decl_module! {
pub struct Module<T: Config> for enum Call where origin: T::Origin {
type Error = Error<T>;
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Verify a header is finalized according to the given finality proof.
///
/// Will use the underlying storage pallet to fetch information about the current
/// authorities and best finalized header in order to verify that the header is finalized.
///
/// If successful in verification, it will write the headers to the underlying storage
/// pallet as well as import the valid finality proof.
#[weight = 0]
#[pallet::weight(0)]
pub fn submit_finality_proof(
origin,
origin: OriginFor<T>,
finality_target: BridgedHeader<T>,
justification: Vec<u8>,
ancestry_proof: Vec<BridgedHeader<T>>,
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;

ensure!(
Expand All @@ -110,15 +102,14 @@ decl_module! {
);

let authority_set = T::HeaderChain::authority_set();
let voter_set =
VoterSet::new(authority_set.authorities).ok_or(<Error<T>>::InvalidAuthoritySet)?;
let voter_set = VoterSet::new(authority_set.authorities).ok_or(<Error<T>>::InvalidAuthoritySet)?;
let set_id = authority_set.set_id;

verify_justification::<BridgedHeader<T>>(
(finality_target.hash(), *finality_target.number()),
set_id,
voter_set,
&justification
&justification,
)
.map_err(|_| <Error<T>>::InvalidJustification)?;

Expand All @@ -134,24 +125,42 @@ decl_module! {
with_transaction(|| {
for header in ancestry_proof {
if T::HeaderChain::import_header(header).is_err() {
return TransactionOutcome::Rollback(Err(<Error<T>>::FailedToWriteHeader))
return TransactionOutcome::Rollback(Err(<Error<T>>::FailedToWriteHeader));
}
}

if T::HeaderChain::import_finality_proof(finality_target, justification).is_err() {
return TransactionOutcome::Rollback(Err(<Error<T>>::FailedToWriteFinalityProof))
return TransactionOutcome::Rollback(Err(<Error<T>>::FailedToWriteFinalityProof));
}

TransactionOutcome::Commit(Ok(()))
})?;

Ok(())
Ok(().into())
}
}

#[pallet::error]
pub enum Error<T> {
/// The given justification is invalid for the given header.
InvalidJustification,
/// The given ancestry proof is unable to verify that the child and ancestor headers are
/// related.
InvalidAncestryProof,
/// The authority set from the underlying header chain is invalid.
InvalidAuthoritySet,
/// Failed to write a header to the underlying header chain.
FailedToWriteHeader,
/// Failed to write finality proof to the underlying header chain.
FailedToWriteFinalityProof,
/// The given ancestry proof is too large to be verified in a single transaction.
OversizedAncestryProof,
}
}

#[cfg(test)]
mod tests {
use super::pallet::*;
use super::*;
use crate::mock::{run_test, test_header, Origin, TestRuntime};
use bp_test_utils::{authority_list, make_justification_for_header};
Expand Down
6 changes: 3 additions & 3 deletions bridges/modules/finality-verifier/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// 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/>.

use crate::{BridgedHeader, Config};
use crate::pallet::{BridgedHeader, Config};
use bp_runtime::{BlockNumberOf, Chain};
use frame_support::{impl_outer_origin, parameter_types, weights::Weight};
use sp_runtime::{
Expand Down Expand Up @@ -74,7 +74,7 @@ parameter_types! {
pub const MaxHeadersInSingleProof: u8 = 5;
}

impl crate::Config for TestRuntime {
impl crate::pallet::Config for TestRuntime {
type BridgedChain = TestBridgedChain;
type HeaderChain = pallet_substrate_bridge::Module<TestRuntime>;
type AncestryChecker = Checker<<Self::BridgedChain as Chain>::Header, Vec<<Self::BridgedChain as Chain>::Header>>;
Expand All @@ -94,7 +94,7 @@ impl Chain for TestBridgedChain {
#[derive(Debug)]
pub struct Checker<H, P>(std::marker::PhantomData<(H, P)>);

impl<H> crate::AncestryChecker<H, Vec<H>> for Checker<H, Vec<H>> {
impl<H> bp_header_chain::AncestryChecker<H, Vec<H>> for Checker<H, Vec<H>> {
fn are_ancestors(_ancestor: &H, _child: &H, proof: &Vec<H>) -> bool {
!proof.is_empty()
}
Expand Down

0 comments on commit be59072

Please sign in to comment.