diff --git a/Cargo.lock b/Cargo.lock
index 2c13fcc0e32df250ec7ed1efb4bcb2da5a72e2b0..d787d2fe08c25b52ffe47db5ffa7ea8d1b404b8e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2602,6 +2602,15 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "ckb-merkle-mountain-range"
+version = "0.6.0"
+source = "git+https://github.com/paritytech/merkle-mountain-range.git?branch=master#537f0e3f67c5adf7afff0800bbb81f02f17570a1"
+dependencies = [
+ "cfg-if",
+ "itertools 0.10.5",
+]
+
 [[package]]
 name = "clang-sys"
 version = "1.6.1"
@@ -9759,7 +9768,7 @@ dependencies = [
  "bp-beefy",
  "bp-runtime",
  "bp-test-utils",
- "ckb-merkle-mountain-range",
+ "ckb-merkle-mountain-range 0.5.2",
  "frame-support",
  "frame-system",
  "log",
@@ -19545,7 +19554,7 @@ name = "sp-mmr-primitives"
 version = "26.0.0"
 dependencies = [
  "array-bytes",
- "ckb-merkle-mountain-range",
+ "ckb-merkle-mountain-range 0.6.0",
  "log",
  "parity-scale-codec",
  "scale-info",
diff --git a/bridges/primitives/beefy/src/lib.rs b/bridges/primitives/beefy/src/lib.rs
index 0441781e79a66f785b985047ad56da70c0f13d49..2494706818ef14c11a8193983164e65a510a80e6 100644
--- a/bridges/primitives/beefy/src/lib.rs
+++ b/bridges/primitives/beefy/src/lib.rs
@@ -22,7 +22,7 @@
 pub use binary_merkle_tree::merkle_root;
 pub use pallet_beefy_mmr::BeefyEcdsaToEthereum;
 pub use pallet_mmr::{
-	primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof},
+	primitives::{DataOrHash as MmrDataOrHash, LeafProof as MmrProof},
 	verify_leaves_proof as verify_mmr_leaves_proof,
 };
 pub use sp_consensus_beefy::{
diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs
index 5c889552a6ae4b7708c0afb7511447fb518f8a9a..03c4836020d98cdd15719619856db0be38dae66b 100644
--- a/polkadot/node/service/src/fake_runtime_api.rs
+++ b/polkadot/node/service/src/fake_runtime_api.rs
@@ -272,11 +272,11 @@ sp_api::impl_runtime_apis! {
 		fn generate_proof(
 			_: Vec<BlockNumber>,
 			_: Option<BlockNumber>,
-		) -> Result<(Vec<sp_mmr_primitives::EncodableOpaqueLeaf>, sp_mmr_primitives::Proof<Hash>), sp_mmr_primitives::Error> {
+		) -> Result<(Vec<sp_mmr_primitives::EncodableOpaqueLeaf>, sp_mmr_primitives::LeafProof<Hash>), sp_mmr_primitives::Error> {
 			unimplemented!()
 		}
 
-		fn verify_proof(_: Vec<sp_mmr_primitives::EncodableOpaqueLeaf>, _: sp_mmr_primitives::Proof<Hash>)
+		fn verify_proof(_: Vec<sp_mmr_primitives::EncodableOpaqueLeaf>, _: sp_mmr_primitives::LeafProof<Hash>)
 			-> Result<(), sp_mmr_primitives::Error>
 		{
 			unimplemented!()
@@ -285,7 +285,7 @@ sp_api::impl_runtime_apis! {
 		fn verify_proof_stateless(
 			_: Hash,
 			_: Vec<sp_mmr_primitives::EncodableOpaqueLeaf>,
-			_: sp_mmr_primitives::Proof<Hash>
+			_: sp_mmr_primitives::LeafProof<Hash>
 		) -> Result<(), sp_mmr_primitives::Error> {
 			unimplemented!()
 		}
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index 22e6183e59460899e6eceb938d93222798ee3a34..c22d5c39b23303178e739f5a1f672ae8b762048a 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -2134,7 +2134,7 @@ sp_api::impl_runtime_apis! {
 		fn generate_proof(
 			block_numbers: Vec<BlockNumber>,
 			best_known_block_number: Option<BlockNumber>,
-		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<mmr::Hash>), mmr::Error> {
+		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::LeafProof<mmr::Hash>), mmr::Error> {
 			Mmr::generate_proof(block_numbers, best_known_block_number).map(
 				|(leaves, proof)| {
 					(
@@ -2148,7 +2148,7 @@ sp_api::impl_runtime_apis! {
 			)
 		}
 
-		fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::Proof<mmr::Hash>)
+		fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::LeafProof<mmr::Hash>)
 			-> Result<(), mmr::Error>
 		{
 			let leaves = leaves.into_iter().map(|leaf|
@@ -2161,7 +2161,7 @@ sp_api::impl_runtime_apis! {
 		fn verify_proof_stateless(
 			root: mmr::Hash,
 			leaves: Vec<mmr::EncodableOpaqueLeaf>,
-			proof: mmr::Proof<mmr::Hash>
+			proof: mmr::LeafProof<mmr::Hash>
 		) -> Result<(), mmr::Error> {
 			let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect();
 			pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(root, nodes, proof)
diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs
index 0509ba382b2e8b262a391af97dae42cb24291c23..9eb0fcca6678b234ab2b744e3fbf2f6f837cbdbe 100644
--- a/polkadot/runtime/test-runtime/src/lib.rs
+++ b/polkadot/runtime/test-runtime/src/lib.rs
@@ -1044,11 +1044,11 @@ sp_api::impl_runtime_apis! {
 		fn generate_proof(
 			_block_numbers: Vec<BlockNumber>,
 			_best_known_block_number: Option<BlockNumber>,
-		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<Hash>), mmr::Error> {
+		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::LeafProof<Hash>), mmr::Error> {
 			Err(mmr::Error::PalletNotIncluded)
 		}
 
-		fn verify_proof(_leaves: Vec<mmr::EncodableOpaqueLeaf>, _proof: mmr::Proof<Hash>)
+		fn verify_proof(_leaves: Vec<mmr::EncodableOpaqueLeaf>, _proof: mmr::LeafProof<Hash>)
 			-> Result<(), mmr::Error>
 		{
 			Err(mmr::Error::PalletNotIncluded)
@@ -1057,7 +1057,7 @@ sp_api::impl_runtime_apis! {
 		fn verify_proof_stateless(
 			_root: Hash,
 			_leaves: Vec<mmr::EncodableOpaqueLeaf>,
-			_proof: mmr::Proof<Hash>
+			_proof: mmr::LeafProof<Hash>
 		) -> Result<(), mmr::Error> {
 			Err(mmr::Error::PalletNotIncluded)
 		}
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index cae12ab49c0242903a99442bea01b02bcecb532c..b62c6d08201c4c2d3a3ba879e9f7e2c6546e51c2 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -2013,7 +2013,7 @@ sp_api::impl_runtime_apis! {
 		fn generate_proof(
 			block_numbers: Vec<BlockNumber>,
 			best_known_block_number: Option<BlockNumber>,
-		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<mmr::Hash>), mmr::Error> {
+		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::LeafProof<mmr::Hash>), mmr::Error> {
 			Mmr::generate_proof(block_numbers, best_known_block_number).map(
 				|(leaves, proof)| {
 					(
@@ -2027,7 +2027,7 @@ sp_api::impl_runtime_apis! {
 			)
 		}
 
-		fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::Proof<mmr::Hash>)
+		fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::LeafProof<mmr::Hash>)
 			-> Result<(), mmr::Error>
 		{
 			let leaves = leaves.into_iter().map(|leaf|
@@ -2040,7 +2040,7 @@ sp_api::impl_runtime_apis! {
 		fn verify_proof_stateless(
 			root: mmr::Hash,
 			leaves: Vec<mmr::EncodableOpaqueLeaf>,
-			proof: mmr::Proof<mmr::Hash>
+			proof: mmr::LeafProof<mmr::Hash>
 		) -> Result<(), mmr::Error> {
 			let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect();
 			pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(root, nodes, proof)
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 18b0d0c31a4df97010149c12c1e12b3c2d4d5119..017ee9100f9e5ff8124b2446cd5d9333b93a64da 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -3094,7 +3094,7 @@ impl_runtime_apis! {
 		fn generate_proof(
 			block_numbers: Vec<BlockNumber>,
 			best_known_block_number: Option<BlockNumber>,
-		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<mmr::Hash>), mmr::Error> {
+		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::LeafProof<mmr::Hash>), mmr::Error> {
 			Mmr::generate_proof(block_numbers, best_known_block_number).map(
 				|(leaves, proof)| {
 					(
@@ -3108,7 +3108,7 @@ impl_runtime_apis! {
 			)
 		}
 
-		fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::Proof<mmr::Hash>)
+		fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::LeafProof<mmr::Hash>)
 			-> Result<(), mmr::Error>
 		{
 			let leaves = leaves.into_iter().map(|leaf|
@@ -3121,7 +3121,7 @@ impl_runtime_apis! {
 		fn verify_proof_stateless(
 			root: mmr::Hash,
 			leaves: Vec<mmr::EncodableOpaqueLeaf>,
-			proof: mmr::Proof<mmr::Hash>
+			proof: mmr::LeafProof<mmr::Hash>
 		) -> Result<(), mmr::Error> {
 			let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect();
 			pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(root, nodes, proof)
diff --git a/substrate/client/merkle-mountain-range/rpc/src/lib.rs b/substrate/client/merkle-mountain-range/rpc/src/lib.rs
index b4da9848de54cbc10ee549c3bd8cf1b033559722..41e73a5b8d75d6940c669786533e423fcc81b5ac 100644
--- a/substrate/client/merkle-mountain-range/rpc/src/lib.rs
+++ b/substrate/client/merkle-mountain-range/rpc/src/lib.rs
@@ -36,7 +36,7 @@ use sp_core::{
 	offchain::{storage::OffchainDb, OffchainDbExt, OffchainStorage},
 	Bytes,
 };
-use sp_mmr_primitives::{Error as MmrError, Proof};
+use sp_mmr_primitives::{Error as MmrError, LeafProof};
 use sp_runtime::traits::{Block as BlockT, NumberFor};
 
 pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi;
@@ -52,17 +52,17 @@ pub struct LeavesProof<BlockHash> {
 	pub block_hash: BlockHash,
 	/// SCALE-encoded vector of `LeafData`.
 	pub leaves: Bytes,
-	/// SCALE-encoded proof data. See [sp_mmr_primitives::Proof].
+	/// SCALE-encoded proof data. See [sp_mmr_primitives::LeafProof].
 	pub proof: Bytes,
 }
 
 impl<BlockHash> LeavesProof<BlockHash> {
 	/// Create new `LeavesProof` from a given vector of `Leaf` and a
-	/// [sp_mmr_primitives::Proof].
+	/// [sp_mmr_primitives::LeafProof].
 	pub fn new<Leaf, MmrHash>(
 		block_hash: BlockHash,
 		leaves: Vec<Leaf>,
-		proof: Proof<MmrHash>,
+		proof: LeafProof<MmrHash>,
 	) -> Self
 	where
 		Leaf: Encode,
@@ -258,7 +258,7 @@ mod tests {
 	fn should_serialize_leaf_proof() {
 		// given
 		let leaf = vec![1_u8, 2, 3, 4];
-		let proof = Proof {
+		let proof = LeafProof {
 			leaf_indices: vec![1],
 			leaf_count: 9,
 			items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
@@ -281,7 +281,7 @@ mod tests {
 		// given
 		let leaf_a = vec![1_u8, 2, 3, 4];
 		let leaf_b = vec![2_u8, 2, 3, 4];
-		let proof = Proof {
+		let proof = LeafProof {
 			leaf_indices: vec![1, 2],
 			leaf_count: 9,
 			items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
@@ -306,7 +306,7 @@ mod tests {
 			block_hash: H256::repeat_byte(0),
 			leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()),
 			proof: Bytes(
-				Proof {
+				LeafProof {
 					leaf_indices: vec![1],
 					leaf_count: 9,
 					items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
@@ -333,7 +333,7 @@ mod tests {
 			block_hash: H256::repeat_byte(0),
 			leaves: Bytes(vec![vec![1_u8, 2, 3, 4], vec![2_u8, 2, 3, 4]].encode()),
 			proof: Bytes(
-				Proof {
+				LeafProof {
 					leaf_indices: vec![1, 2],
 					leaf_count: 9,
 					items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
diff --git a/substrate/client/merkle-mountain-range/src/test_utils.rs b/substrate/client/merkle-mountain-range/src/test_utils.rs
index 5775b4cfe67cd49878508521ec72ba9af413a187..fcf9fa25b593c86eb780d8b563e6ecc55ecb50a7 100644
--- a/substrate/client/merkle-mountain-range/src/test_utils.rs
+++ b/substrate/client/merkle-mountain-range/src/test_utils.rs
@@ -309,11 +309,11 @@ sp_api::mock_impl_runtime_apis! {
 			&self,
 			_block_numbers: Vec<u64>,
 			_best_known_block_number: Option<u64>,
-		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<MmrHash>), mmr::Error> {
+		) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::LeafProof<MmrHash>), mmr::Error> {
 			Err(mmr::Error::PalletNotIncluded)
 		}
 
-		fn verify_proof(_leaves: Vec<mmr::EncodableOpaqueLeaf>, _proof: mmr::Proof<MmrHash>)
+		fn verify_proof(_leaves: Vec<mmr::EncodableOpaqueLeaf>, _proof: mmr::LeafProof<MmrHash>)
 			-> Result<(), mmr::Error>
 		{
 			Err(mmr::Error::PalletNotIncluded)
@@ -322,7 +322,7 @@ sp_api::mock_impl_runtime_apis! {
 		fn verify_proof_stateless(
 			_root: MmrHash,
 			_leaves: Vec<mmr::EncodableOpaqueLeaf>,
-			_proof: mmr::Proof<MmrHash>
+			_proof: mmr::LeafProof<MmrHash>
 		) -> Result<(), mmr::Error> {
 			Err(mmr::Error::PalletNotIncluded)
 		}
diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs
index e2b40974579e8376723f466198d74755603285d0..a86443f2e0114120cac8b14e85de08ee183eda93 100644
--- a/substrate/frame/merkle-mountain-range/src/lib.rs
+++ b/substrate/frame/merkle-mountain-range/src/lib.rs
@@ -260,15 +260,15 @@ pub mod pallet {
 
 /// Stateless MMR proof verification for batch of leaves.
 ///
-/// This function can be used to verify received MMR [primitives::Proof] (`proof`)
+/// This function can be used to verify received MMR [primitives::LeafProof] (`proof`)
 /// for given leaves set (`leaves`) against a known MMR root hash (`root`).
 /// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
 /// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
-/// [primitives::Proof].
+/// [primitives::LeafProof].
 pub fn verify_leaves_proof<H, L>(
 	root: H::Output,
 	leaves: Vec<mmr::Node<H, L>>,
-	proof: primitives::Proof<H::Output>,
+	proof: primitives::LeafProof<H::Output>,
 ) -> Result<(), primitives::Error>
 where
 	H: traits::Hash,
@@ -342,7 +342,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	pub fn generate_proof(
 		block_numbers: Vec<BlockNumberFor<T>>,
 		best_known_block_number: Option<BlockNumberFor<T>>,
-	) -> Result<(Vec<LeafOf<T, I>>, primitives::Proof<HashOf<T, I>>), primitives::Error> {
+	) -> Result<(Vec<LeafOf<T, I>>, primitives::LeafProof<HashOf<T, I>>), primitives::Error> {
 		// check whether best_known_block_number provided, else use current best block
 		let best_known_block_number =
 			best_known_block_number.unwrap_or_else(|| <frame_system::Pallet<T>>::block_number());
@@ -362,11 +362,6 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		mmr.generate_proof(leaf_indices)
 	}
 
-	/// Return the on-chain MMR root hash.
-	pub fn mmr_root() -> HashOf<T, I> {
-		RootHash::<T, I>::get()
-	}
-
 	/// Verify MMR proof for given `leaves`.
 	///
 	/// This method is safe to use within the runtime code.
@@ -375,7 +370,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	/// or the proof is invalid.
 	pub fn verify_leaves(
 		leaves: Vec<LeafOf<T, I>>,
-		proof: primitives::Proof<HashOf<T, I>>,
+		proof: primitives::LeafProof<HashOf<T, I>>,
 	) -> Result<(), primitives::Error> {
 		if proof.leaf_count > NumberOfLeaves::<T, I>::get() ||
 			proof.leaf_count == 0 ||
@@ -393,4 +388,37 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 			Err(primitives::Error::Verify.log_debug("The proof is incorrect."))
 		}
 	}
+
+	pub fn generate_ancestry_proof(
+		prev_block_number: BlockNumberFor<T>,
+		best_known_block_number: Option<BlockNumberFor<T>>,
+	) -> Result<primitives::AncestryProof<HashOf<T, I>>, Error> {
+		// check whether best_known_block_number provided, else use current best block
+		let best_known_block_number =
+			best_known_block_number.unwrap_or_else(|| <frame_system::Pallet<T>>::block_number());
+
+		let leaf_count = Self::block_num_to_leaf_index(best_known_block_number)?.saturating_add(1);
+		let prev_leaf_count = Self::block_num_to_leaf_index(prev_block_number)?.saturating_add(1);
+
+		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
+		mmr.generate_ancestry_proof(prev_leaf_count)
+	}
+
+	pub fn verify_ancestry_proof(
+		ancestry_proof: primitives::AncestryProof<HashOf<T, I>>,
+	) -> Result<(), Error> {
+		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> =
+			mmr::Mmr::new(ancestry_proof.leaf_count);
+		let is_valid = mmr.verify_ancestry_proof(ancestry_proof)?;
+		if is_valid {
+			Ok(())
+		} else {
+			Err(Error::Verify.log_debug("The ancestry proof is incorrect."))
+		}
+	}
+
+	/// Return the on-chain MMR root hash.
+	pub fn mmr_root() -> HashOf<T, I> {
+		RootHash::<T, I>::get()
+	}
 }
diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs
index aeb3e7ea66414a062a238da3f84795b53da4f68b..5efc172d1e93f1d7a56e3b36da20f93c6e1df1f9 100644
--- a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs
+++ b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs
@@ -23,17 +23,17 @@ use crate::{
 	primitives::{self, Error, NodeIndex},
 	Config, HashOf, HashingOf,
 };
-use sp_mmr_primitives::{mmr_lib, utils::NodesUtils};
+use sp_mmr_primitives::{mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, LeafIndex};
 use sp_std::prelude::*;
 
 /// Stateless verification of the proof for a batch of leaves.
 /// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
 /// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
-/// [primitives::Proof]
+/// [primitives::LeafProof]
 pub fn verify_leaves_proof<H, L>(
 	root: H::Output,
 	leaves: Vec<Node<H, L>>,
-	proof: primitives::Proof<H::Output>,
+	proof: primitives::LeafProof<H::Output>,
 ) -> Result<bool, Error>
 where
 	H: sp_runtime::traits::Hash,
@@ -69,7 +69,8 @@ where
 	T: Config<I>,
 	I: 'static,
 	L: primitives::FullLeaf,
-	Storage<StorageType, T, I, L>: mmr_lib::MMRStore<NodeOf<T, I, L>>,
+	Storage<StorageType, T, I, L>:
+		MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
 {
 	mmr: mmr_lib::MMR<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>, Storage<StorageType, T, I, L>>,
 	leaves: NodeIndex,
@@ -80,7 +81,8 @@ where
 	T: Config<I>,
 	I: 'static,
 	L: primitives::FullLeaf,
-	Storage<StorageType, T, I, L>: mmr_lib::MMRStore<NodeOf<T, I, L>>,
+	Storage<StorageType, T, I, L>:
+		MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
 {
 	/// Create a pointer to an existing MMR with given number of leaves.
 	pub fn new(leaves: NodeIndex) -> Self {
@@ -91,11 +93,11 @@ where
 	/// Verify proof for a set of leaves.
 	/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have
 	/// the same position in both the `leaves` vector and the `leaf_indices` vector contained in the
-	/// [primitives::Proof]
+	/// [primitives::LeafProof]
 	pub fn verify_leaves_proof(
 		&self,
 		leaves: Vec<L>,
-		proof: primitives::Proof<HashOf<T, I>>,
+		proof: primitives::LeafProof<HashOf<T, I>>,
 	) -> Result<bool, Error> {
 		let p = mmr_lib::MerkleProof::<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>>::new(
 			self.mmr.mmr_size(),
@@ -117,6 +119,44 @@ where
 			.map_err(|e| Error::Verify.log_debug(e))
 	}
 
+	pub fn verify_ancestry_proof(
+		&self,
+		ancestry_proof: primitives::AncestryProof<HashOf<T, I>>,
+	) -> Result<bool, Error> {
+		let prev_peaks_proof =
+			mmr_lib::NodeMerkleProof::<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>>::new(
+				self.mmr.mmr_size(),
+				ancestry_proof
+					.items
+					.into_iter()
+					.map(|(index, hash)| (index, Node::Hash(hash)))
+					.collect(),
+			);
+
+		let raw_ancestry_proof = mmr_lib::AncestryProof::<
+			NodeOf<T, I, L>,
+			Hasher<HashingOf<T, I>, L>,
+		> {
+			prev_peaks: ancestry_proof
+				.prev_peaks
+				.into_iter()
+				.map(|hash| Node::Hash(hash))
+				.collect(),
+			prev_size: mmr_lib::helper::leaf_index_to_mmr_size(ancestry_proof.prev_leaf_count - 1),
+			proof: prev_peaks_proof,
+		};
+
+		let prev_root = mmr_lib::ancestry_proof::bagging_peaks_hashes::<
+			NodeOf<T, I, L>,
+			Hasher<HashingOf<T, I>, L>,
+		>(raw_ancestry_proof.prev_peaks.clone())
+		.map_err(|e| Error::Verify.log_debug(e))?;
+		let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
+		raw_ancestry_proof
+			.verify_ancestor(root, prev_root)
+			.map_err(|e| Error::Verify.log_debug(e))
+	}
+
 	/// Return the internal size of the MMR (number of nodes).
 	#[cfg(test)]
 	pub fn size(&self) -> NodeIndex {
@@ -145,7 +185,7 @@ where
 
 	/// Commit the changes to underlying storage, return current number of leaves and
 	/// calculate the new MMR's root hash.
-	pub fn finalize(self) -> Result<(NodeIndex, HashOf<T, I>), Error> {
+	pub fn finalize(mut self) -> Result<(NodeIndex, HashOf<T, I>), Error> {
 		let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
 		self.mmr.commit().map_err(|e| Error::Commit.log_error(e))?;
 		Ok((self.leaves, root.hash()))
@@ -166,7 +206,7 @@ where
 	pub fn generate_proof(
 		&self,
 		leaf_indices: Vec<NodeIndex>,
-	) -> Result<(Vec<L>, primitives::Proof<HashOf<T, I>>), Error> {
+	) -> Result<(Vec<L>, primitives::LeafProof<HashOf<T, I>>), Error> {
 		let positions = leaf_indices
 			.iter()
 			.map(|index| mmr_lib::leaf_index_to_pos(*index))
@@ -174,7 +214,7 @@ where
 		let store = <Storage<OffchainStorage, T, I, L>>::default();
 		let leaves = positions
 			.iter()
-			.map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) {
+			.map(|pos| match store.get_elem(*pos) {
 				Ok(Some(Node::Data(leaf))) => Ok(leaf),
 				e => Err(Error::LeafNotFound.log_debug(e)),
 			})
@@ -184,11 +224,34 @@ where
 		self.mmr
 			.gen_proof(positions)
 			.map_err(|e| Error::GenerateProof.log_error(e))
-			.map(|p| primitives::Proof {
+			.map(|p| primitives::LeafProof {
 				leaf_indices,
 				leaf_count,
 				items: p.proof_items().iter().map(|x| x.hash()).collect(),
 			})
 			.map(|p| (leaves, p))
 	}
+
+	pub fn generate_ancestry_proof(
+		&self,
+		prev_leaf_count: LeafIndex,
+	) -> Result<primitives::AncestryProof<HashOf<T, I>>, Error> {
+		let prev_mmr_size = NodesUtils::new(prev_leaf_count).size();
+		let raw_ancestry_proof = self
+			.mmr
+			.gen_ancestry_proof(prev_mmr_size)
+			.map_err(|e| Error::GenerateProof.log_error(e))?;
+
+		Ok(primitives::AncestryProof {
+			prev_peaks: raw_ancestry_proof.prev_peaks.into_iter().map(|p| p.hash()).collect(),
+			prev_leaf_count,
+			leaf_count: self.leaves,
+			items: raw_ancestry_proof
+				.proof
+				.proof_items()
+				.iter()
+				.map(|(index, item)| (*index, item.hash()))
+				.collect(),
+		})
+	}
 }
diff --git a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs
index f2acc35a137ffb46a39396762eafe30d7296e017..6848b8f1b9906b85bfc7c3b9ca9d5a52b4ddaed8 100644
--- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs
+++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs
@@ -60,7 +60,7 @@ impl<StorageType, T, I, L> Default for Storage<StorageType, T, I, L> {
 	}
 }
 
-impl<T, I, L> mmr_lib::MMRStore<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
+impl<T, I, L> mmr_lib::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
 where
 	T: Config<I>,
 	I: 'static,
@@ -98,13 +98,20 @@ where
 		Ok(sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &temp_key)
 			.and_then(|v| codec::Decode::decode(&mut &*v).ok()))
 	}
+}
 
+impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
+where
+	T: Config<I>,
+	I: 'static,
+	L: primitives::FullLeaf + codec::Decode,
+{
 	fn append(&mut self, _: NodeIndex, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
 		panic!("MMR must not be altered in the off-chain context.")
 	}
 }
 
-impl<T, I, L> mmr_lib::MMRStore<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
+impl<T, I, L> mmr_lib::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
 where
 	T: Config<I>,
 	I: 'static,
@@ -113,7 +120,14 @@ where
 	fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
 		Ok(Nodes::<T, I>::get(pos).map(Node::Hash))
 	}
+}
 
+impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
+where
+	T: Config<I>,
+	I: 'static,
+	L: primitives::FullLeaf,
+{
 	fn append(&mut self, pos: NodeIndex, elems: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
 		if elems.is_empty() {
 			return Ok(())
diff --git a/substrate/frame/merkle-mountain-range/src/tests.rs b/substrate/frame/merkle-mountain-range/src/tests.rs
index 88de7511c9f280b6996419f07ac1f5c458d8a395..f8cfcb4e2c286f949207826990ba715485247ca2 100644
--- a/substrate/frame/merkle-mountain-range/src/tests.rs
+++ b/substrate/frame/merkle-mountain-range/src/tests.rs
@@ -22,7 +22,7 @@ use sp_core::{
 	offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt},
 	H256,
 };
-use sp_mmr_primitives::{mmr_lib::helper, utils, Compact, Proof};
+use sp_mmr_primitives::{mmr_lib::helper, utils, Compact, LeafProof};
 use sp_runtime::BuildStorage;
 
 pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
@@ -283,7 +283,7 @@ fn should_generate_proofs_correctly() {
 			proofs[0],
 			(
 				vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![0],
 					leaf_count: 7,
 					items: vec![
@@ -298,7 +298,7 @@ fn should_generate_proofs_correctly() {
 			historical_proofs[0][0],
 			(
 				vec![Compact::new(((0, H256::repeat_byte(1)).into(), LeafData::new(1).into(),))],
-				Proof { leaf_indices: vec![0], leaf_count: 1, items: vec![] }
+				LeafProof { leaf_indices: vec![0], leaf_count: 1, items: vec![] }
 			)
 		);
 
@@ -314,7 +314,7 @@ fn should_generate_proofs_correctly() {
 			proofs[2],
 			(
 				vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![2],
 					leaf_count: 7,
 					items: vec![
@@ -334,7 +334,7 @@ fn should_generate_proofs_correctly() {
 			historical_proofs[2][0],
 			(
 				vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![2],
 					leaf_count: 3,
 					items: vec![hex(
@@ -354,7 +354,7 @@ fn should_generate_proofs_correctly() {
 			historical_proofs[2][2],
 			(
 				vec![Compact::new(((2, H256::repeat_byte(3)).into(), LeafData::new(3).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![2],
 					leaf_count: 5,
 					items: vec![
@@ -372,7 +372,7 @@ fn should_generate_proofs_correctly() {
 			(
 				// NOTE: the leaf index is equivalent to the block number(in this case 5) - 1
 				vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![4],
 					leaf_count: 7,
 					items: vec![
@@ -387,7 +387,7 @@ fn should_generate_proofs_correctly() {
 			historical_proofs[4][0],
 			(
 				vec![Compact::new(((4, H256::repeat_byte(5)).into(), LeafData::new(5).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![4],
 					leaf_count: 5,
 					items: vec![hex(
@@ -402,7 +402,7 @@ fn should_generate_proofs_correctly() {
 			proofs[6],
 			(
 				vec![Compact::new(((6, H256::repeat_byte(7)).into(), LeafData::new(7).into(),))],
-				Proof {
+				LeafProof {
 					leaf_indices: vec![6],
 					leaf_count: 7,
 					items: vec![
@@ -433,7 +433,7 @@ fn should_generate_batch_proof_correctly() {
 		// then
 		assert_eq!(
 			proof,
-			Proof {
+			LeafProof {
 				// the leaf indices are equivalent to the above specified block numbers - 1.
 				leaf_indices: vec![0, 4, 5],
 				leaf_count: 7,
@@ -451,7 +451,7 @@ fn should_generate_batch_proof_correctly() {
 		// then
 		assert_eq!(
 			historical_proof,
-			Proof {
+			LeafProof {
 				leaf_indices: vec![0, 4, 5],
 				leaf_count: 6,
 				items: vec![
@@ -516,43 +516,40 @@ fn should_verify() {
 	});
 }
 
-#[test]
-fn should_verify_batch_proofs() {
-	fn generate_and_verify_batch_proof(
-		ext: &mut sp_io::TestExternalities,
-		block_numbers: &Vec<u64>,
-		blocks_to_add: usize,
-	) {
-		let (leaves, proof) = ext.execute_with(|| {
-			crate::Pallet::<Test>::generate_proof(block_numbers.to_vec(), None).unwrap()
-		});
+fn generate_and_verify_batch_proof(
+	ext: &mut sp_io::TestExternalities,
+	block_numbers: &Vec<u64>,
+	blocks_to_add: usize,
+) {
+	let (leaves, proof) = ext.execute_with(|| {
+		crate::Pallet::<Test>::generate_proof(block_numbers.to_vec(), None).unwrap()
+	});
 
-		let max_block_number = ext.execute_with(|| frame_system::Pallet::<Test>::block_number());
-		let min_block_number = block_numbers.iter().max().unwrap();
+	let max_block_number = ext.execute_with(|| frame_system::Pallet::<Test>::block_number());
+	let min_block_number = block_numbers.iter().max().unwrap();
 
-		// generate all possible historical proofs for the given blocks
-		let historical_proofs = (*min_block_number..=max_block_number)
-			.map(|best_block| {
-				ext.execute_with(|| {
-					crate::Pallet::<Test>::generate_proof(block_numbers.to_vec(), Some(best_block))
-						.unwrap()
-				})
+	// generate all possible historical proofs for the given blocks
+	let historical_proofs = (*min_block_number..=max_block_number)
+		.map(|best_block| {
+			ext.execute_with(|| {
+				crate::Pallet::<Test>::generate_proof(block_numbers.to_vec(), Some(best_block))
+					.unwrap()
 			})
-			.collect::<Vec<_>>();
-
-		ext.execute_with(|| {
-			add_blocks(blocks_to_add);
-			// then
-			assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proof), Ok(()));
-			historical_proofs.iter().for_each(|(leaves, proof)| {
-				assert_eq!(
-					crate::Pallet::<Test>::verify_leaves(leaves.clone(), proof.clone()),
-					Ok(())
-				);
-			});
 		})
-	}
+		.collect::<Vec<_>>();
+
+	ext.execute_with(|| {
+		add_blocks(blocks_to_add);
+		// then
+		assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves, proof), Ok(()));
+		historical_proofs.iter().for_each(|(leaves, proof)| {
+			assert_eq!(crate::Pallet::<Test>::verify_leaves(leaves.clone(), proof.clone()), Ok(()));
+		});
+	})
+}
 
+#[test]
+fn should_verify_batch_proofs() {
 	let _ = env_logger::try_init();
 
 	use itertools::Itertools;
@@ -790,3 +787,24 @@ fn does_not_panic_when_generating_historical_proofs() {
 		);
 	});
 }
+
+#[test]
+fn generating_and_verifying_ancestry_proofs_works_correctly() {
+	let _ = env_logger::try_init();
+	let mut ext = new_test_ext();
+	ext.execute_with(|| add_blocks(500));
+	ext.persist_offchain_overlay();
+	register_offchain_ext(&mut ext);
+
+	ext.execute_with(|| {
+		// Check that generating and verifying ancestry proofs works correctly
+		// for each previous block
+		for prev_block_number in 1..501 {
+			let proof = Pallet::<Test>::generate_ancestry_proof(prev_block_number, None).unwrap();
+			Pallet::<Test>::verify_ancestry_proof(proof).unwrap();
+		}
+
+		// Check that we can't generate ancestry proofs for a future block.
+		assert_eq!(Pallet::<Test>::generate_ancestry_proof(501, None), Err(Error::GenerateProof));
+	});
+}
diff --git a/substrate/primitives/merkle-mountain-range/Cargo.toml b/substrate/primitives/merkle-mountain-range/Cargo.toml
index b97cef138ed4a2c83ff921bc0cef702c97070d49..65d8bd79e5afc4bec9c11c38338c9f400866c686 100644
--- a/substrate/primitives/merkle-mountain-range/Cargo.toml
+++ b/substrate/primitives/merkle-mountain-range/Cargo.toml
@@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
 scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
 log = { workspace = true }
-mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false }
+mmr-lib = { package = "ckb-merkle-mountain-range", git = "https://github.com/paritytech/merkle-mountain-range.git", branch = "master", default-features = false }
 serde = { features = ["alloc", "derive"], optional = true, workspace = true }
 sp-api = { path = "../api", default-features = false }
 sp-core = { path = "../core", default-features = false }
diff --git a/substrate/primitives/merkle-mountain-range/src/lib.rs b/substrate/primitives/merkle-mountain-range/src/lib.rs
index c76d66bb08ea700bd6bcc1272d56310bda51881f..3740047e027829eb471a61244b460cdd58fe8cd5 100644
--- a/substrate/primitives/merkle-mountain-range/src/lib.rs
+++ b/substrate/primitives/merkle-mountain-range/src/lib.rs
@@ -352,15 +352,29 @@ impl_leaf_data_for_tuple!(A:0, B:1, C:2, D:3, E:4);
 
 /// An MMR proof data for a group of leaves.
 #[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
-pub struct Proof<Hash> {
+pub struct LeafProof<Hash> {
 	/// The indices of the leaves the proof is for.
 	pub leaf_indices: Vec<LeafIndex>,
 	/// Number of leaves in MMR, when the proof was generated.
 	pub leaf_count: NodeIndex,
-	/// Proof elements (hashes of siblings of inner nodes on the path to the leaf).
+	/// Proof elements (hashes of siblings of inner nodes on the path to the leafs).
 	pub items: Vec<Hash>,
 }
 
+/// An MMR ancestry proof for a prior mmr root.
+#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
+pub struct AncestryProof<Hash> {
+	/// Peaks of the ancestor's mmr
+	pub prev_peaks: Vec<Hash>,
+	/// Number of leaves in the ancestor's MMR.
+	pub prev_leaf_count: u64,
+	/// Number of leaves in MMR, when the proof was generated.
+	pub leaf_count: NodeIndex,
+	/// Proof elements
+	/// (positions and hashes of siblings of inner nodes on the path to the previous peaks).
+	pub items: Vec<(u64, Hash)>,
+}
+
 /// Merkle Mountain Range operation error.
 #[cfg_attr(feature = "std", derive(thiserror::Error))]
 #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo)]
@@ -437,14 +451,14 @@ sp_api::decl_runtime_apis! {
 		fn generate_proof(
 			block_numbers: Vec<BlockNumber>,
 			best_known_block_number: Option<BlockNumber>
-		) -> Result<(Vec<EncodableOpaqueLeaf>, Proof<Hash>), Error>;
+		) -> Result<(Vec<EncodableOpaqueLeaf>, LeafProof<Hash>), Error>;
 
 		/// Verify MMR proof against on-chain MMR for a batch of leaves.
 		///
 		/// Note this function will use on-chain MMR root hash and check if the proof matches the hash.
 		/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
-		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [Proof]
-		fn verify_proof(leaves: Vec<EncodableOpaqueLeaf>, proof: Proof<Hash>) -> Result<(), Error>;
+		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof]
+		fn verify_proof(leaves: Vec<EncodableOpaqueLeaf>, proof: LeafProof<Hash>) -> Result<(), Error>;
 
 		/// Verify MMR proof against given root hash for a batch of leaves.
 		///
@@ -452,8 +466,8 @@ sp_api::decl_runtime_apis! {
 		/// proof is verified against given MMR root hash.
 		///
 		/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
-		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [Proof]
-		fn verify_proof_stateless(root: Hash, leaves: Vec<EncodableOpaqueLeaf>, proof: Proof<Hash>)
+		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof]
+		fn verify_proof_stateless(root: Hash, leaves: Vec<EncodableOpaqueLeaf>, proof: LeafProof<Hash>)
 			-> Result<(), Error>;
 	}
 }
@@ -472,12 +486,12 @@ mod tests {
 
 	type Test = DataOrHash<Keccak256, String>;
 	type TestCompact = Compact<Keccak256, (Test, Test)>;
-	type TestProof = Proof<<Keccak256 as traits::Hash>::Output>;
+	type TestProof = LeafProof<<Keccak256 as traits::Hash>::Output>;
 
 	#[test]
 	fn should_encode_decode_proof() {
 		// given
-		let proof: TestProof = Proof {
+		let proof: TestProof = LeafProof {
 			leaf_indices: vec![5],
 			leaf_count: 10,
 			items: vec![