1#![cfg_attr(not(feature = "std"), no_std)]
19#![allow(clippy::all)]
20#![deny(missing_docs)]
21
22mod tests;
23
24extern crate alloc;
25use polkadot_sdk::*;
26
27use alloc::collections::BTreeMap;
28use anyhow::anyhow;
29use codec::DecodeAll;
30use finality_grandpa::Chain;
31use grandpa_verifier_primitives::{
32 justification::{find_scheduled_change, AncestryChain, GrandpaJustification},
33 parachain_header_storage_key, ConsensusState, FinalityProof, ParachainHeadersWithFinalityProof,
34};
35use sp_core::H256;
36use sp_runtime::traits::{BlakeTwo256, Header};
37use sp_std::prelude::*;
38use sp_trie::StorageProof;
39use substrate_state_machine::read_proof_check;
40
41pub fn verify_grandpa_finality_proof<H>(
44 mut consensus_state: ConsensusState,
45 finality_proof: FinalityProof<H>,
46) -> Result<(ConsensusState, H, Vec<H256>, AncestryChain<H>), anyhow::Error>
47where
48 H: Header<Hash = H256, Number = u32>,
49 H::Number: finality_grandpa::BlockNumberOps + Into<u32>,
50{
51 let headers = AncestryChain::<H>::new(&finality_proof.unknown_headers);
53
54 let target = finality_proof
55 .unknown_headers
56 .iter()
57 .max_by_key(|h| *h.number())
58 .ok_or_else(|| anyhow!("Unknown headers can't be empty!"))?;
59
60 if target.hash() != finality_proof.block {
62 Err(anyhow!("Latest finalized block should be highest block in unknown_headers"))?;
63 }
64
65 let justification =
66 GrandpaJustification::<H>::decode_all(&mut &finality_proof.justification[..])
67 .map_err(|e| anyhow!("Failed to decode justification {:?}", e))?;
68
69 if justification.commit.target_hash != finality_proof.block {
70 Err(anyhow!("Justification target hash and finality proof block hash mismatch"))?;
71 }
72
73 let from = consensus_state.latest_hash;
74
75 let base = finality_proof
76 .unknown_headers
77 .iter()
78 .min_by_key(|h| *h.number())
79 .ok_or_else(|| anyhow!("Unknown headers can't be empty!"))?;
80
81 if base.number() < &consensus_state.latest_height {
82 headers.ancestry(base.hash(), consensus_state.latest_hash).map_err(|_| {
83 anyhow!(
84 "[verify_grandpa_finality_proof] Invalid ancestry (base -> latest relay block)!"
85 )
86 })?;
87 }
88
89 let finalized = headers
90 .ancestry(from, target.hash())
91 .map_err(|_| anyhow!("[verify_grandpa_finality_proof] Invalid ancestry!"))?;
92
93 justification.verify(consensus_state.current_set_id, &consensus_state.current_authorities)?;
95
96 consensus_state.latest_hash = target.hash();
98 consensus_state.latest_height = (*target.number()).into();
99 if let Some(scheduled_change) = find_scheduled_change::<H>(&target) {
100 consensus_state.current_set_id += 1;
101 consensus_state.current_authorities = scheduled_change.next_authorities;
102 }
103
104 Ok((consensus_state, target.clone(), finalized, headers))
105}
106pub fn verify_parachain_headers_with_grandpa_finality_proof<H>(
113 consensus_state: ConsensusState,
114 proof: ParachainHeadersWithFinalityProof<H>,
115) -> Result<(ConsensusState, BTreeMap<u32, Vec<H>>), anyhow::Error>
116where
117 H: Header<Hash = H256, Number = u32>,
118 H::Number: finality_grandpa::BlockNumberOps + Into<u32>,
119{
120 let ParachainHeadersWithFinalityProof { finality_proof, parachain_headers } = proof;
121
122 let (consensus_state, _, mut finalized_hashes, headers) =
123 verify_grandpa_finality_proof(consensus_state, finality_proof)?;
124 finalized_hashes.sort();
125 let mut verified_parachain_headers: BTreeMap<u32, Vec<H>> = BTreeMap::new();
127 for (hash, proof) in parachain_headers {
128 if finalized_hashes.binary_search(&hash).is_err() {
129 continue;
131 }
132 let relay_chain_header =
133 headers.header(&hash).expect("Headers have been checked by AncestryChain; qed");
134 let state_proof = proof.state_proof;
135 let mut keys = BTreeMap::new();
136 for para_id in proof.para_ids {
137 let key = parachain_header_storage_key(para_id);
138
139 keys.insert(key.0, para_id);
140 }
141
142 let proof = StorageProof::new(state_proof);
143
144 let mut result = read_proof_check::<BlakeTwo256, _>(
146 relay_chain_header.state_root(),
147 proof,
148 keys.keys().map(|key| key.as_slice()),
149 )
150 .map_err(|err| anyhow!("error verifying parachain header state proof: {err:?}"))?;
151 for (key, para_id) in keys {
152 let header = result
153 .remove(&key)
154 .flatten()
155 .ok_or_else(|| anyhow!("Invalid proof, parachain header not found"))?;
156 let parachain_header =
157 H::decode(&mut &header[..]).map_err(|e| anyhow!("error decoding header: {e:?}"))?;
158 verified_parachain_headers.entry(para_id).or_default().push(parachain_header);
159 }
160 }
161
162 Ok((consensus_state, verified_parachain_headers))
163}