Skip to content

Commit a462207

Browse files
authored
Introduce submit_finality_proof_ex call to bridges GRANDPA pallet (#3225)
backport of paritytech/parity-bridges-common#2821 (see detailed description there)
1 parent bc2e5e1 commit a462207

File tree

10 files changed

+550
-103
lines changed

10 files changed

+550
-103
lines changed

bridges/bin/runtime-common/src/refund_relayer_extension.rs

Lines changed: 266 additions & 2 deletions
Large diffs are not rendered by default.

bridges/modules/grandpa/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ There are two main things in GRANDPA that help building light clients:
3838

3939
## Pallet Operations
4040

41-
The main entrypoint of the pallet is the `submit_finality_proof` call. It has two arguments - the finalized
42-
headers and associated GRANDPA justification. The call simply verifies the justification using current
43-
validators set and checks if header is better than the previous best header. If both checks are passed, the
44-
header (only its useful fields) is inserted into the runtime storage and may be used by other pallets to verify
45-
storage proofs.
41+
The main entrypoint of the pallet is the `submit_finality_proof_ex` call. It has three arguments - the finalized
42+
headers, associated GRANDPA justification and ID of the authority set that has generated this justification. The
43+
call simply verifies the justification using current validators set and checks if header is better than the
44+
previous best header. If both checks are passed, the header (only its useful fields) is inserted into the runtime
45+
storage and may be used by other pallets to verify storage proofs.
4646

4747
The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is
4848
required for the pallet operations, submitting such header is free. So if you're ok with session-length

bridges/modules/grandpa/src/benchmarking.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
//! Benchmarks for the GRANDPA Pallet.
1818
//!
19-
//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof`, so these benchmarks are
20-
//! based around that. There are to main factors which affect finality proof verification:
19+
//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof_ex`. Our benchmarks
20+
//! are based around `submit_finality_proof`, though - from weight PoV they are the same calls.
21+
//! There are to main factors which affect finality proof verification:
2122
//!
2223
//! 1. The number of `votes-ancestries` in the justification
2324
//! 2. The number of `pre-commits` in the justification

bridges/modules/grandpa/src/call_ext.rs

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
1616

17-
use crate::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet};
17+
use crate::{
18+
weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error,
19+
Pallet,
20+
};
1821
use bp_header_chain::{
1922
justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size,
2023
ChainWithGrandpa, GrandpaConsensusLogReader,
2124
};
2225
use bp_runtime::{BlockNumberOf, OwnedBridgeModule};
2326
use codec::Encode;
2427
use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight};
28+
use sp_consensus_grandpa::SetId;
2529
use sp_runtime::{
2630
traits::{Header, Zero},
2731
transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
@@ -33,6 +37,9 @@ use sp_runtime::{
3337
pub struct SubmitFinalityProofInfo<N> {
3438
/// Number of the finality target.
3539
pub block_number: N,
40+
/// An identifier of the validators set that has signed the submitted justification.
41+
/// It might be `None` if deprecated version of the `submit_finality_proof` is used.
42+
pub current_set_id: Option<SetId>,
3643
/// Extra weight that we assume is included in the call.
3744
///
3845
/// We have some assumptions about headers and justifications of the bridged chain.
@@ -61,9 +68,11 @@ pub struct SubmitFinalityProofHelper<T: Config<I>, I: 'static> {
6168

6269
impl<T: Config<I>, I: 'static> SubmitFinalityProofHelper<T, I> {
6370
/// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best
64-
/// one we know.
71+
/// one we know. Additionally, checks if `current_set_id` matches the current authority set
72+
/// id, if specified.
6573
pub fn check_obsolete(
6674
finality_target: BlockNumberOf<T::BridgedChain>,
75+
current_set_id: Option<SetId>,
6776
) -> Result<(), Error<T, I>> {
6877
let best_finalized = crate::BestFinalized::<T, I>::get().ok_or_else(|| {
6978
log::trace!(
@@ -85,6 +94,20 @@ impl<T: Config<I>, I: 'static> SubmitFinalityProofHelper<T, I> {
8594
return Err(Error::<T, I>::OldHeader)
8695
}
8796

97+
if let Some(current_set_id) = current_set_id {
98+
let actual_set_id = <CurrentAuthoritySet<T, I>>::get().set_id;
99+
if current_set_id != actual_set_id {
100+
log::trace!(
101+
target: crate::LOG_TARGET,
102+
"Cannot finalize header signed by unknown authority set: bundled {:?}, best {:?}",
103+
current_set_id,
104+
actual_set_id,
105+
);
106+
107+
return Err(Error::<T, I>::InvalidAuthoritySetId)
108+
}
109+
}
110+
88111
Ok(())
89112
}
90113

@@ -111,6 +134,18 @@ pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
111134
return Some(submit_finality_proof_info_from_args::<T, I>(
112135
finality_target,
113136
justification,
137+
None,
138+
))
139+
} else if let Some(crate::Call::<T, I>::submit_finality_proof_ex {
140+
finality_target,
141+
justification,
142+
current_set_id,
143+
}) = self.is_sub_type()
144+
{
145+
return Some(submit_finality_proof_info_from_args::<T, I>(
146+
finality_target,
147+
justification,
148+
Some(*current_set_id),
114149
))
115150
}
116151

@@ -133,7 +168,10 @@ pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
133168
return InvalidTransaction::Call.into()
134169
}
135170

136-
match SubmitFinalityProofHelper::<T, I>::check_obsolete(finality_target.block_number) {
171+
match SubmitFinalityProofHelper::<T, I>::check_obsolete(
172+
finality_target.block_number,
173+
finality_target.current_set_id,
174+
) {
137175
Ok(_) => Ok(ValidTransaction::default()),
138176
Err(Error::<T, I>::OldHeader) => InvalidTransaction::Stale.into(),
139177
Err(_) => InvalidTransaction::Call.into(),
@@ -150,6 +188,7 @@ impl<T: Config<I>, I: 'static> CallSubType<T, I> for T::RuntimeCall where
150188
pub(crate) fn submit_finality_proof_info_from_args<T: Config<I>, I: 'static>(
151189
finality_target: &BridgedHeader<T, I>,
152190
justification: &GrandpaJustification<BridgedHeader<T, I>>,
191+
current_set_id: Option<SetId>,
153192
) -> SubmitFinalityProofInfo<BridgedBlockNumber<T, I>> {
154193
let block_number = *finality_target.number();
155194

@@ -191,28 +230,32 @@ pub(crate) fn submit_finality_proof_info_from_args<T: Config<I>, I: 'static>(
191230
);
192231
let extra_size = actual_call_size.saturating_sub(max_expected_call_size);
193232

194-
SubmitFinalityProofInfo { block_number, extra_weight, extra_size }
233+
SubmitFinalityProofInfo { block_number, current_set_id, extra_weight, extra_size }
195234
}
196235

197236
#[cfg(test)]
198237
mod tests {
199238
use crate::{
200239
call_ext::CallSubType,
201240
mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime},
202-
BestFinalized, Config, PalletOperatingMode, WeightInfo,
241+
BestFinalized, Config, CurrentAuthoritySet, PalletOperatingMode, StoredAuthoritySet,
242+
SubmitFinalityProofInfo, WeightInfo,
203243
};
204244
use bp_header_chain::ChainWithGrandpa;
205245
use bp_runtime::{BasicOperatingMode, HeaderId};
206246
use bp_test_utils::{
207247
make_default_justification, make_justification_for_header, JustificationGeneratorParams,
248+
TEST_GRANDPA_SET_ID,
208249
};
209250
use frame_support::weights::Weight;
210251
use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion};
211252

212253
fn validate_block_submit(num: TestNumber) -> bool {
213-
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof {
254+
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
214255
finality_target: Box::new(test_header(num)),
215256
justification: make_default_justification(&test_header(num)),
257+
// not initialized => zero
258+
current_set_id: 0,
216259
};
217260
RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
218261
bridge_grandpa_call,
@@ -256,6 +299,18 @@ mod tests {
256299
});
257300
}
258301

302+
#[test]
303+
fn extension_rejects_new_header_if_set_id_is_invalid() {
304+
run_test(|| {
305+
// when set id is different from the passed one => tx is rejected
306+
sync_to_header_10();
307+
let next_set = StoredAuthoritySet::<TestRuntime, ()>::try_new(vec![], 0x42).unwrap();
308+
CurrentAuthoritySet::<TestRuntime, ()>::put(next_set);
309+
310+
assert!(!validate_block_submit(15));
311+
});
312+
}
313+
259314
#[test]
260315
fn extension_accepts_new_header() {
261316
run_test(|| {
@@ -266,6 +321,42 @@ mod tests {
266321
});
267322
}
268323

324+
#[test]
325+
fn submit_finality_proof_info_is_parsed() {
326+
// when `submit_finality_proof` is used, `current_set_id` is set to `None`
327+
let deprecated_call =
328+
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof {
329+
finality_target: Box::new(test_header(42)),
330+
justification: make_default_justification(&test_header(42)),
331+
});
332+
assert_eq!(
333+
deprecated_call.submit_finality_proof_info(),
334+
Some(SubmitFinalityProofInfo {
335+
block_number: 42,
336+
current_set_id: None,
337+
extra_weight: Weight::zero(),
338+
extra_size: 0,
339+
})
340+
);
341+
342+
// when `submit_finality_proof_ex` is used, `current_set_id` is set to `Some`
343+
let deprecated_call =
344+
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
345+
finality_target: Box::new(test_header(42)),
346+
justification: make_default_justification(&test_header(42)),
347+
current_set_id: 777,
348+
});
349+
assert_eq!(
350+
deprecated_call.submit_finality_proof_info(),
351+
Some(SubmitFinalityProofInfo {
352+
block_number: 42,
353+
current_set_id: Some(777),
354+
extra_weight: Weight::zero(),
355+
extra_size: 0,
356+
})
357+
);
358+
}
359+
269360
#[test]
270361
fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() {
271362
// when call arguments are below our limit => no refund
@@ -275,9 +366,10 @@ mod tests {
275366
..Default::default()
276367
};
277368
let small_justification = make_justification_for_header(justification_params);
278-
let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
369+
let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
279370
finality_target: Box::new(small_finality_target),
280371
justification: small_justification,
372+
current_set_id: TEST_GRANDPA_SET_ID,
281373
});
282374
assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0);
283375

@@ -291,9 +383,10 @@ mod tests {
291383
..Default::default()
292384
};
293385
let large_justification = make_justification_for_header(justification_params);
294-
let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
386+
let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
295387
finality_target: Box::new(large_finality_target),
296388
justification: large_justification,
389+
current_set_id: TEST_GRANDPA_SET_ID,
297390
});
298391
assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0);
299392
}
@@ -309,9 +402,10 @@ mod tests {
309402

310403
// when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY` headers => no refund
311404
let justification = make_justification_for_header(justification_params.clone());
312-
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
405+
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
313406
finality_target: Box::new(finality_target.clone()),
314407
justification,
408+
current_set_id: TEST_GRANDPA_SET_ID,
315409
});
316410
assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero());
317411

@@ -322,9 +416,10 @@ mod tests {
322416
justification.commit.precommits.len().saturated_into(),
323417
justification.votes_ancestries.len().saturated_into(),
324418
);
325-
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
419+
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
326420
finality_target: Box::new(finality_target),
327421
justification,
422+
current_set_id: TEST_GRANDPA_SET_ID,
328423
});
329424
assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight);
330425
}

0 commit comments

Comments
 (0)