mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 02:57:57 +00:00
Add submit_finality_proof_ex call to the GRANDPA pallet (#2821)
* depreacte submit_finality_proof and introduce submit_finality_proof_ex instead * propagate changes to the rest of runtime crates * tests for new call * suppress deprecation warning * revert changes to benchmarks to avoid unnecessary compilation issues when integrating to other repos
This commit is contained in:
committed by
Bastian Köcher
parent
a7255831ca
commit
4d53c846e7
@@ -195,6 +195,19 @@ impl CallInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns mutable reference to pre-dispatch `finality_target` sent to the
|
||||
/// `SubmitFinalityProof` call.
|
||||
#[cfg(test)]
|
||||
fn submit_finality_proof_info_mut(
|
||||
&mut self,
|
||||
) -> Option<&mut SubmitFinalityProofInfo<RelayBlockNumber>> {
|
||||
match *self {
|
||||
Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info),
|
||||
Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pre-dispatch `SubmitParachainHeadsInfo`.
|
||||
fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> {
|
||||
match self {
|
||||
@@ -844,7 +857,7 @@ mod tests {
|
||||
use bp_parachains::{BestParaHeadHash, ParaInfo};
|
||||
use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId};
|
||||
use bp_runtime::{BasicOperatingMode, HeaderId};
|
||||
use bp_test_utils::{make_default_justification, test_keyring};
|
||||
use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID};
|
||||
use frame_support::{
|
||||
assert_storage_noop, parameter_types,
|
||||
traits::{fungible::Mutate, ReservableCurrency},
|
||||
@@ -929,7 +942,7 @@ mod tests {
|
||||
let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect();
|
||||
let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default());
|
||||
pallet_bridge_grandpa::CurrentAuthoritySet::<TestRuntime>::put(
|
||||
StoredAuthoritySet::try_new(authorities, 0).unwrap(),
|
||||
StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(),
|
||||
);
|
||||
pallet_bridge_grandpa::BestFinalized::<TestRuntime>::put(best_relay_header);
|
||||
|
||||
@@ -977,6 +990,23 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall {
|
||||
let relay_header = BridgedChainHeader::new(
|
||||
relay_header_number,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let relay_justification = make_default_justification(&relay_header);
|
||||
|
||||
RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof_ex {
|
||||
finality_target: Box::new(relay_header),
|
||||
justification: relay_justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_parachain_head_call(
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
) -> RuntimeCall {
|
||||
@@ -1059,6 +1089,18 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn relay_finality_and_delivery_batch_call_ex(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
best_message: MessageNonce,
|
||||
) -> RuntimeCall {
|
||||
RuntimeCall::Utility(UtilityCall::batch_all {
|
||||
calls: vec![
|
||||
submit_relay_header_call_ex(relay_header_number),
|
||||
message_delivery_call(best_message),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn relay_finality_and_confirmation_batch_call(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
best_message: MessageNonce,
|
||||
@@ -1071,6 +1113,18 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn relay_finality_and_confirmation_batch_call_ex(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
best_message: MessageNonce,
|
||||
) -> RuntimeCall {
|
||||
RuntimeCall::Utility(UtilityCall::batch_all {
|
||||
calls: vec![
|
||||
submit_relay_header_call_ex(relay_header_number),
|
||||
message_confirmation_call(best_message),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn all_finality_and_delivery_batch_call(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
@@ -1085,6 +1139,20 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn all_finality_and_delivery_batch_call_ex(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
best_message: MessageNonce,
|
||||
) -> RuntimeCall {
|
||||
RuntimeCall::Utility(UtilityCall::batch_all {
|
||||
calls: vec![
|
||||
submit_relay_header_call_ex(relay_header_number),
|
||||
submit_parachain_head_call(parachain_head_at_relay_header_number),
|
||||
message_delivery_call(best_message),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn all_finality_and_confirmation_batch_call(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
@@ -1099,12 +1167,27 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn all_finality_and_confirmation_batch_call_ex(
|
||||
relay_header_number: RelayBlockNumber,
|
||||
parachain_head_at_relay_header_number: RelayBlockNumber,
|
||||
best_message: MessageNonce,
|
||||
) -> RuntimeCall {
|
||||
RuntimeCall::Utility(UtilityCall::batch_all {
|
||||
calls: vec![
|
||||
submit_relay_header_call_ex(relay_header_number),
|
||||
submit_parachain_head_call(parachain_head_at_relay_header_number),
|
||||
message_confirmation_call(best_message),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn all_finality_pre_dispatch_data() -> PreDispatchData<ThisChainAccountId> {
|
||||
PreDispatchData {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
call_info: CallInfo::AllFinalityAndMsgs(
|
||||
SubmitFinalityProofInfo {
|
||||
block_number: 200,
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
},
|
||||
@@ -1128,12 +1211,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn all_finality_pre_dispatch_data_ex() -> PreDispatchData<ThisChainAccountId> {
|
||||
let mut data = all_finality_pre_dispatch_data();
|
||||
data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id =
|
||||
Some(TEST_GRANDPA_SET_ID);
|
||||
data
|
||||
}
|
||||
|
||||
fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData<ThisChainAccountId> {
|
||||
PreDispatchData {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
call_info: CallInfo::AllFinalityAndMsgs(
|
||||
SubmitFinalityProofInfo {
|
||||
block_number: 200,
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
},
|
||||
@@ -1153,12 +1244,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData<ThisChainAccountId> {
|
||||
let mut data = all_finality_confirmation_pre_dispatch_data();
|
||||
data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id =
|
||||
Some(TEST_GRANDPA_SET_ID);
|
||||
data
|
||||
}
|
||||
|
||||
fn relay_finality_pre_dispatch_data() -> PreDispatchData<ThisChainAccountId> {
|
||||
PreDispatchData {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
call_info: CallInfo::RelayFinalityAndMsgs(
|
||||
SubmitFinalityProofInfo {
|
||||
block_number: 200,
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
},
|
||||
@@ -1177,12 +1276,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData<ThisChainAccountId> {
|
||||
let mut data = relay_finality_pre_dispatch_data();
|
||||
data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id =
|
||||
Some(TEST_GRANDPA_SET_ID);
|
||||
data
|
||||
}
|
||||
|
||||
fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData<ThisChainAccountId> {
|
||||
PreDispatchData {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
call_info: CallInfo::RelayFinalityAndMsgs(
|
||||
SubmitFinalityProofInfo {
|
||||
block_number: 200,
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
},
|
||||
@@ -1197,6 +1304,13 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData<ThisChainAccountId> {
|
||||
let mut data = relay_finality_confirmation_pre_dispatch_data();
|
||||
data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id =
|
||||
Some(TEST_GRANDPA_SET_ID);
|
||||
data
|
||||
}
|
||||
|
||||
fn parachain_finality_pre_dispatch_data() -> PreDispatchData<ThisChainAccountId> {
|
||||
PreDispatchData {
|
||||
relayer: relayer_account_at_this_chain(),
|
||||
@@ -1393,6 +1507,10 @@ mod tests {
|
||||
run_validate(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
// message confirmation validation is passing
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(message_confirmation_call(200)),
|
||||
@@ -1410,6 +1528,12 @@ mod tests {
|
||||
)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex(
|
||||
200, 200, 200
|
||||
)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1500,12 +1624,24 @@ mod tests {
|
||||
run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_delivery_batch_call_ex(
|
||||
200, 200, 200
|
||||
)),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call(
|
||||
200, 200, 200
|
||||
)),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex(
|
||||
200, 200, 200
|
||||
)),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1518,11 +1654,19 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(100, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call(100, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call_ex(100, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1535,10 +1679,18 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(101, 100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call(101, 100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call_ex(101, 100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)),
|
||||
@@ -1560,19 +1712,35 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_confirmation_batch_call(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_validate(all_finality_and_confirmation_batch_call_ex(200, 200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)),
|
||||
@@ -1609,10 +1777,18 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1631,10 +1807,18 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)),
|
||||
@@ -1662,10 +1846,18 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)),
|
||||
@@ -1696,10 +1888,18 @@ mod tests {
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)),
|
||||
Ok(Some(all_finality_pre_dispatch_data())),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)),
|
||||
Ok(Some(all_finality_pre_dispatch_data_ex())),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)),
|
||||
Ok(Some(all_finality_confirmation_pre_dispatch_data())),
|
||||
);
|
||||
assert_eq!(
|
||||
run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)),
|
||||
Ok(Some(all_finality_confirmation_pre_dispatch_data_ex())),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2126,6 +2326,12 @@ mod tests {
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
assert_eq!(
|
||||
TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&all_finality_and_delivery_batch_call_ex(200, 200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// relay + parachain + message confirmation calls batch is ignored
|
||||
assert_eq!(
|
||||
@@ -2134,6 +2340,12 @@ mod tests {
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
assert_eq!(
|
||||
TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&all_finality_and_confirmation_batch_call_ex(200, 200, 200)
|
||||
),
|
||||
Ok(None),
|
||||
);
|
||||
|
||||
// parachain + message delivery call batch is ignored
|
||||
assert_eq!(
|
||||
@@ -2158,6 +2370,12 @@ mod tests {
|
||||
),
|
||||
Ok(Some(relay_finality_pre_dispatch_data().call_info)),
|
||||
);
|
||||
assert_eq!(
|
||||
TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&relay_finality_and_delivery_batch_call_ex(200, 200)
|
||||
),
|
||||
Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)),
|
||||
);
|
||||
|
||||
// relay + message confirmation call batch is accepted
|
||||
assert_eq!(
|
||||
@@ -2166,6 +2384,12 @@ mod tests {
|
||||
),
|
||||
Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)),
|
||||
);
|
||||
assert_eq!(
|
||||
TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call(
|
||||
&relay_finality_and_confirmation_batch_call_ex(200, 200)
|
||||
),
|
||||
Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)),
|
||||
);
|
||||
|
||||
// message delivery call batch is accepted
|
||||
assert_eq!(
|
||||
@@ -2194,11 +2418,19 @@ mod tests {
|
||||
run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_delivery_batch_call(100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(100, 200)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2211,19 +2443,35 @@ mod tests {
|
||||
run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 100)),
|
||||
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(message_delivery_call(100)),
|
||||
@@ -2254,19 +2502,35 @@ mod tests {
|
||||
run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 200)),
|
||||
Ok(Some(relay_finality_pre_dispatch_data()),)
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 200)),
|
||||
Ok(Some(relay_finality_pre_dispatch_data_ex()),)
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 200)),
|
||||
Ok(Some(relay_finality_confirmation_pre_dispatch_data())),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 200)),
|
||||
Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex())),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 200)),
|
||||
Ok(Default::default()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_grandpa_pre_dispatch(message_delivery_call(200)),
|
||||
|
||||
@@ -38,11 +38,11 @@ There are two main things in GRANDPA that help building light clients:
|
||||
|
||||
## Pallet Operations
|
||||
|
||||
The main entrypoint of the pallet is the `submit_finality_proof` call. It has two arguments - the finalized
|
||||
headers and associated GRANDPA justification. The call simply verifies the justification using current
|
||||
validators set and checks if header is better than the previous best header. If both checks are passed, the
|
||||
header (only its useful fields) is inserted into the runtime storage and may be used by other pallets to verify
|
||||
storage proofs.
|
||||
The main entrypoint of the pallet is the `submit_finality_proof_ex` call. It has three arguments - the finalized
|
||||
headers, associated GRANDPA justification and ID of the authority set that has generated this justification. The
|
||||
call simply verifies the justification using current validators set and checks if header is better than the
|
||||
previous best header. If both checks are passed, the header (only its useful fields) is inserted into the runtime
|
||||
storage and may be used by other pallets to verify storage proofs.
|
||||
|
||||
The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is
|
||||
required for the pallet operations, submitting such header is free. So if you're ok with session-length
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
//! Benchmarks for the GRANDPA Pallet.
|
||||
//!
|
||||
//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof`, so these benchmarks are
|
||||
//! based around that. There are to main factors which affect finality proof verification:
|
||||
//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof_ex`. Our benchmarks
|
||||
//! are based around `submit_finality_proof`, though - from weight PoV they are the same calls.
|
||||
//! There are to main factors which affect finality proof verification:
|
||||
//!
|
||||
//! 1. The number of `votes-ancestries` in the justification
|
||||
//! 2. The number of `pre-commits` in the justification
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
// 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::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet};
|
||||
use crate::{
|
||||
weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error,
|
||||
Pallet,
|
||||
};
|
||||
use bp_header_chain::{
|
||||
justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size,
|
||||
ChainWithGrandpa, GrandpaConsensusLogReader,
|
||||
@@ -22,6 +25,7 @@ use bp_header_chain::{
|
||||
use bp_runtime::{BlockNumberOf, OwnedBridgeModule};
|
||||
use codec::Encode;
|
||||
use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight};
|
||||
use sp_consensus_grandpa::SetId;
|
||||
use sp_runtime::{
|
||||
traits::{Header, Zero},
|
||||
transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},
|
||||
@@ -33,6 +37,9 @@ use sp_runtime::{
|
||||
pub struct SubmitFinalityProofInfo<N> {
|
||||
/// Number of the finality target.
|
||||
pub block_number: N,
|
||||
/// An identifier of the validators set that has signed the submitted justification.
|
||||
/// It might be `None` if deprecated version of the `submit_finality_proof` is used.
|
||||
pub current_set_id: Option<SetId>,
|
||||
/// Extra weight that we assume is included in the call.
|
||||
///
|
||||
/// We have some assumptions about headers and justifications of the bridged chain.
|
||||
@@ -61,9 +68,11 @@ pub struct SubmitFinalityProofHelper<T: Config<I>, I: 'static> {
|
||||
|
||||
impl<T: Config<I>, I: 'static> SubmitFinalityProofHelper<T, I> {
|
||||
/// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best
|
||||
/// one we know.
|
||||
/// one we know. Additionally, checks if `current_set_id` matches the current authority set
|
||||
/// id, if specified.
|
||||
pub fn check_obsolete(
|
||||
finality_target: BlockNumberOf<T::BridgedChain>,
|
||||
current_set_id: Option<SetId>,
|
||||
) -> Result<(), Error<T, I>> {
|
||||
let best_finalized = crate::BestFinalized::<T, I>::get().ok_or_else(|| {
|
||||
log::trace!(
|
||||
@@ -85,6 +94,20 @@ impl<T: Config<I>, I: 'static> SubmitFinalityProofHelper<T, I> {
|
||||
return Err(Error::<T, I>::OldHeader)
|
||||
}
|
||||
|
||||
if let Some(current_set_id) = current_set_id {
|
||||
let actual_set_id = <CurrentAuthoritySet<T, I>>::get().set_id;
|
||||
if current_set_id != actual_set_id {
|
||||
log::trace!(
|
||||
target: crate::LOG_TARGET,
|
||||
"Cannot finalize header signed by unknown authority set: bundled {:?}, best {:?}",
|
||||
current_set_id,
|
||||
actual_set_id,
|
||||
);
|
||||
|
||||
return Err(Error::<T, I>::InvalidAuthoritySetId)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -111,6 +134,18 @@ pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
|
||||
return Some(submit_finality_proof_info_from_args::<T, I>(
|
||||
finality_target,
|
||||
justification,
|
||||
None,
|
||||
))
|
||||
} else if let Some(crate::Call::<T, I>::submit_finality_proof_ex {
|
||||
finality_target,
|
||||
justification,
|
||||
current_set_id,
|
||||
}) = self.is_sub_type()
|
||||
{
|
||||
return Some(submit_finality_proof_info_from_args::<T, I>(
|
||||
finality_target,
|
||||
justification,
|
||||
Some(*current_set_id),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -133,7 +168,10 @@ pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
|
||||
return InvalidTransaction::Call.into()
|
||||
}
|
||||
|
||||
match SubmitFinalityProofHelper::<T, I>::check_obsolete(finality_target.block_number) {
|
||||
match SubmitFinalityProofHelper::<T, I>::check_obsolete(
|
||||
finality_target.block_number,
|
||||
finality_target.current_set_id,
|
||||
) {
|
||||
Ok(_) => Ok(ValidTransaction::default()),
|
||||
Err(Error::<T, I>::OldHeader) => InvalidTransaction::Stale.into(),
|
||||
Err(_) => InvalidTransaction::Call.into(),
|
||||
@@ -150,6 +188,7 @@ impl<T: Config<I>, I: 'static> CallSubType<T, I> for T::RuntimeCall where
|
||||
pub(crate) fn submit_finality_proof_info_from_args<T: Config<I>, I: 'static>(
|
||||
finality_target: &BridgedHeader<T, I>,
|
||||
justification: &GrandpaJustification<BridgedHeader<T, I>>,
|
||||
current_set_id: Option<SetId>,
|
||||
) -> SubmitFinalityProofInfo<BridgedBlockNumber<T, I>> {
|
||||
let block_number = *finality_target.number();
|
||||
|
||||
@@ -191,7 +230,7 @@ pub(crate) fn submit_finality_proof_info_from_args<T: Config<I>, I: 'static>(
|
||||
);
|
||||
let extra_size = actual_call_size.saturating_sub(max_expected_call_size);
|
||||
|
||||
SubmitFinalityProofInfo { block_number, extra_weight, extra_size }
|
||||
SubmitFinalityProofInfo { block_number, current_set_id, extra_weight, extra_size }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -199,20 +238,24 @@ mod tests {
|
||||
use crate::{
|
||||
call_ext::CallSubType,
|
||||
mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime},
|
||||
BestFinalized, Config, PalletOperatingMode, WeightInfo,
|
||||
BestFinalized, Config, CurrentAuthoritySet, PalletOperatingMode, StoredAuthoritySet,
|
||||
SubmitFinalityProofInfo, WeightInfo,
|
||||
};
|
||||
use bp_header_chain::ChainWithGrandpa;
|
||||
use bp_runtime::{BasicOperatingMode, HeaderId};
|
||||
use bp_test_utils::{
|
||||
make_default_justification, make_justification_for_header, JustificationGeneratorParams,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
};
|
||||
use frame_support::weights::Weight;
|
||||
use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion};
|
||||
|
||||
fn validate_block_submit(num: TestNumber) -> bool {
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof {
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(num)),
|
||||
justification: make_default_justification(&test_header(num)),
|
||||
// not initialized => zero
|
||||
current_set_id: 0,
|
||||
};
|
||||
RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call,
|
||||
@@ -256,6 +299,18 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_set_id_is_invalid() {
|
||||
run_test(|| {
|
||||
// when set id is different from the passed one => tx is rejected
|
||||
sync_to_header_10();
|
||||
let next_set = StoredAuthoritySet::<TestRuntime, ()>::try_new(vec![], 0x42).unwrap();
|
||||
CurrentAuthoritySet::<TestRuntime, ()>::put(next_set);
|
||||
|
||||
assert!(!validate_block_submit(15));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_accepts_new_header() {
|
||||
run_test(|| {
|
||||
@@ -266,6 +321,42 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn submit_finality_proof_info_is_parsed() {
|
||||
// when `submit_finality_proof` is used, `current_set_id` is set to `None`
|
||||
let deprecated_call =
|
||||
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof {
|
||||
finality_target: Box::new(test_header(42)),
|
||||
justification: make_default_justification(&test_header(42)),
|
||||
});
|
||||
assert_eq!(
|
||||
deprecated_call.submit_finality_proof_info(),
|
||||
Some(SubmitFinalityProofInfo {
|
||||
block_number: 42,
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
})
|
||||
);
|
||||
|
||||
// when `submit_finality_proof_ex` is used, `current_set_id` is set to `Some`
|
||||
let deprecated_call =
|
||||
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(42)),
|
||||
justification: make_default_justification(&test_header(42)),
|
||||
current_set_id: 777,
|
||||
});
|
||||
assert_eq!(
|
||||
deprecated_call.submit_finality_proof_info(),
|
||||
Some(SubmitFinalityProofInfo {
|
||||
block_number: 42,
|
||||
current_set_id: Some(777),
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() {
|
||||
// when call arguments are below our limit => no refund
|
||||
@@ -275,9 +366,10 @@ mod tests {
|
||||
..Default::default()
|
||||
};
|
||||
let small_justification = make_justification_for_header(justification_params);
|
||||
let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
|
||||
let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(small_finality_target),
|
||||
justification: small_justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
});
|
||||
assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0);
|
||||
|
||||
@@ -291,9 +383,10 @@ mod tests {
|
||||
..Default::default()
|
||||
};
|
||||
let large_justification = make_justification_for_header(justification_params);
|
||||
let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
|
||||
let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(large_finality_target),
|
||||
justification: large_justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
});
|
||||
assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0);
|
||||
}
|
||||
@@ -309,9 +402,10 @@ mod tests {
|
||||
|
||||
// when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY` headers => no refund
|
||||
let justification = make_justification_for_header(justification_params.clone());
|
||||
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
|
||||
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(finality_target.clone()),
|
||||
justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
});
|
||||
assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero());
|
||||
|
||||
@@ -322,9 +416,10 @@ mod tests {
|
||||
justification.commit.precommits.len().saturated_into(),
|
||||
justification.votes_ancestries.len().saturated_into(),
|
||||
);
|
||||
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof {
|
||||
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(finality_target),
|
||||
justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
});
|
||||
assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight);
|
||||
}
|
||||
|
||||
+171
-107
@@ -151,102 +151,31 @@ pub mod pallet {
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
/// Verify a target header is finalized according to the given finality proof.
|
||||
///
|
||||
/// It 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 target header to the underlying storage
|
||||
/// pallet.
|
||||
///
|
||||
/// The call fails if:
|
||||
///
|
||||
/// - the pallet is halted;
|
||||
///
|
||||
/// - the pallet knows better header than the `finality_target`;
|
||||
///
|
||||
/// - verification is not optimized or invalid;
|
||||
///
|
||||
/// - header contains forced authorities set change or change with non-zero delay.
|
||||
/// This call is deprecated and will be removed around May 2024. Use the
|
||||
/// `submit_finality_proof_ex` instead. Semantically, this call is an equivalent of the
|
||||
/// `submit_finality_proof_ex` call without current authority set id check.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(<T::WeightInfo as WeightInfo>::submit_finality_proof(
|
||||
justification.commit.precommits.len().saturated_into(),
|
||||
justification.votes_ancestries.len().saturated_into(),
|
||||
))]
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(
|
||||
note = "`submit_finality_proof` will be removed in May 2024. Use `submit_finality_proof_ex` instead."
|
||||
)]
|
||||
pub fn submit_finality_proof(
|
||||
origin: OriginFor<T>,
|
||||
finality_target: Box<BridgedHeader<T, I>>,
|
||||
justification: GrandpaJustification<BridgedHeader<T, I>>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
|
||||
ensure_signed(origin)?;
|
||||
|
||||
let (hash, number) = (finality_target.hash(), *finality_target.number());
|
||||
log::trace!(
|
||||
target: LOG_TARGET,
|
||||
"Going to try and finalize header {:?}",
|
||||
finality_target
|
||||
);
|
||||
|
||||
SubmitFinalityProofHelper::<T, I>::check_obsolete(number)?;
|
||||
|
||||
let authority_set = <CurrentAuthoritySet<T, I>>::get();
|
||||
let unused_proof_size = authority_set.unused_proof_size();
|
||||
let set_id = authority_set.set_id;
|
||||
let authority_set: AuthoritySet = authority_set.into();
|
||||
verify_justification::<T, I>(&justification, hash, number, authority_set)?;
|
||||
|
||||
let maybe_new_authority_set =
|
||||
try_enact_authority_change::<T, I>(&finality_target, set_id)?;
|
||||
let may_refund_call_fee = maybe_new_authority_set.is_some() &&
|
||||
// if we have seen too many mandatory headers in this block, we don't want to refund
|
||||
Self::free_mandatory_headers_remaining() > 0 &&
|
||||
// if arguments out of expected bounds, we don't want to refund
|
||||
submit_finality_proof_info_from_args::<T, I>(&finality_target, &justification)
|
||||
.fits_limits();
|
||||
if may_refund_call_fee {
|
||||
FreeMandatoryHeadersRemaining::<T, I>::mutate(|count| {
|
||||
*count = count.saturating_sub(1)
|
||||
});
|
||||
}
|
||||
insert_header::<T, I>(*finality_target, hash);
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"Successfully imported finalized header with hash {:?}!",
|
||||
hash
|
||||
);
|
||||
|
||||
// mandatory header is a header that changes authorities set. The pallet can't go
|
||||
// further without importing this header. So every bridge MUST import mandatory headers.
|
||||
//
|
||||
// We don't want to charge extra costs for mandatory operations. So relayer is not
|
||||
// paying fee for mandatory headers import transactions.
|
||||
//
|
||||
// If size/weight of the call is exceeds our estimated limits, the relayer still needs
|
||||
// to pay for the transaction.
|
||||
let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes };
|
||||
|
||||
// the proof size component of the call weight assumes that there are
|
||||
// `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen`
|
||||
// estimation). But if their number is lower, then we may "refund" some `proof_size`,
|
||||
// making proof smaller and leaving block space to other useful transactions
|
||||
let pre_dispatch_weight = T::WeightInfo::submit_finality_proof(
|
||||
justification.commit.precommits.len().saturated_into(),
|
||||
justification.votes_ancestries.len().saturated_into(),
|
||||
);
|
||||
let actual_weight = pre_dispatch_weight
|
||||
.set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size));
|
||||
|
||||
Self::deposit_event(Event::UpdatedBestFinalizedHeader {
|
||||
number,
|
||||
hash,
|
||||
grandpa_info: StoredHeaderGrandpaInfo {
|
||||
finality_proof: justification,
|
||||
new_verification_context: maybe_new_authority_set,
|
||||
},
|
||||
});
|
||||
|
||||
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee })
|
||||
Self::submit_finality_proof_ex(
|
||||
origin,
|
||||
finality_target,
|
||||
justification,
|
||||
// the `submit_finality_proof_ex` also reads this value, but it is done from the
|
||||
// cache, so we don't treat it as an additional db access
|
||||
<CurrentAuthoritySet<T, I>>::get().set_id,
|
||||
)
|
||||
}
|
||||
|
||||
/// Bootstrap the bridge pallet with an initial header and authority set from which to sync.
|
||||
@@ -299,6 +228,111 @@ pub mod pallet {
|
||||
) -> DispatchResult {
|
||||
<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
|
||||
}
|
||||
|
||||
/// Verify a target header is finalized according to the given finality proof. The proof
|
||||
/// is assumed to be signed by GRANDPA authorities set with `current_set_id` id.
|
||||
///
|
||||
/// It 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 target header to the underlying storage
|
||||
/// pallet.
|
||||
///
|
||||
/// The call fails if:
|
||||
///
|
||||
/// - the pallet is halted;
|
||||
///
|
||||
/// - the pallet knows better header than the `finality_target`;
|
||||
///
|
||||
/// - the id of best GRANDPA authority set, known to the pallet is not equal to the
|
||||
/// `current_set_id`;
|
||||
///
|
||||
/// - verification is not optimized or invalid;
|
||||
///
|
||||
/// - header contains forced authorities set change or change with non-zero delay.
|
||||
#[pallet::call_index(4)]
|
||||
#[pallet::weight(<T::WeightInfo as WeightInfo>::submit_finality_proof(
|
||||
justification.commit.precommits.len().saturated_into(),
|
||||
justification.votes_ancestries.len().saturated_into(),
|
||||
))]
|
||||
pub fn submit_finality_proof_ex(
|
||||
origin: OriginFor<T>,
|
||||
finality_target: Box<BridgedHeader<T, I>>,
|
||||
justification: GrandpaJustification<BridgedHeader<T, I>>,
|
||||
current_set_id: sp_consensus_grandpa::SetId,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
|
||||
ensure_signed(origin)?;
|
||||
|
||||
let (hash, number) = (finality_target.hash(), *finality_target.number());
|
||||
log::trace!(
|
||||
target: LOG_TARGET,
|
||||
"Going to try and finalize header {:?}",
|
||||
finality_target
|
||||
);
|
||||
|
||||
// it checks whether the `number` is better than the current best block number
|
||||
// and whether the `current_set_id` matches the best known set id
|
||||
SubmitFinalityProofHelper::<T, I>::check_obsolete(number, Some(current_set_id))?;
|
||||
|
||||
let authority_set = <CurrentAuthoritySet<T, I>>::get();
|
||||
let unused_proof_size = authority_set.unused_proof_size();
|
||||
let set_id = authority_set.set_id;
|
||||
let authority_set: AuthoritySet = authority_set.into();
|
||||
verify_justification::<T, I>(&justification, hash, number, authority_set)?;
|
||||
|
||||
let maybe_new_authority_set =
|
||||
try_enact_authority_change::<T, I>(&finality_target, set_id)?;
|
||||
let may_refund_call_fee = maybe_new_authority_set.is_some() &&
|
||||
// if we have seen too many mandatory headers in this block, we don't want to refund
|
||||
Self::free_mandatory_headers_remaining() > 0 &&
|
||||
// if arguments out of expected bounds, we don't want to refund
|
||||
submit_finality_proof_info_from_args::<T, I>(&finality_target, &justification, Some(current_set_id))
|
||||
.fits_limits();
|
||||
if may_refund_call_fee {
|
||||
FreeMandatoryHeadersRemaining::<T, I>::mutate(|count| {
|
||||
*count = count.saturating_sub(1)
|
||||
});
|
||||
}
|
||||
insert_header::<T, I>(*finality_target, hash);
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"Successfully imported finalized header with hash {:?}!",
|
||||
hash
|
||||
);
|
||||
|
||||
// mandatory header is a header that changes authorities set. The pallet can't go
|
||||
// further without importing this header. So every bridge MUST import mandatory headers.
|
||||
//
|
||||
// We don't want to charge extra costs for mandatory operations. So relayer is not
|
||||
// paying fee for mandatory headers import transactions.
|
||||
//
|
||||
// If size/weight of the call is exceeds our estimated limits, the relayer still needs
|
||||
// to pay for the transaction.
|
||||
let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes };
|
||||
|
||||
// the proof size component of the call weight assumes that there are
|
||||
// `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen`
|
||||
// estimation). But if their number is lower, then we may "refund" some `proof_size`,
|
||||
// making proof smaller and leaving block space to other useful transactions
|
||||
let pre_dispatch_weight = T::WeightInfo::submit_finality_proof(
|
||||
justification.commit.precommits.len().saturated_into(),
|
||||
justification.votes_ancestries.len().saturated_into(),
|
||||
);
|
||||
let actual_weight = pre_dispatch_weight
|
||||
.set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size));
|
||||
|
||||
Self::deposit_event(Event::UpdatedBestFinalizedHeader {
|
||||
number,
|
||||
hash,
|
||||
grandpa_info: StoredHeaderGrandpaInfo {
|
||||
finality_proof: justification,
|
||||
new_verification_context: maybe_new_authority_set,
|
||||
},
|
||||
});
|
||||
|
||||
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee })
|
||||
}
|
||||
}
|
||||
|
||||
/// Number mandatory headers that we may accept in the current block for free (returning
|
||||
@@ -436,6 +470,9 @@ pub mod pallet {
|
||||
TooManyAuthoritiesInSet,
|
||||
/// Error generated by the `OwnedBridgeModule` trait.
|
||||
BridgeModule(bp_runtime::OwnedBridgeModuleError),
|
||||
/// The `current_set_id` argument of the `submit_finality_proof_ex` doesn't match
|
||||
/// the id of the current set, known to the pallet.
|
||||
InvalidAuthoritySetId,
|
||||
}
|
||||
|
||||
/// Check the given header for a GRANDPA scheduled authority set change. If a change
|
||||
@@ -663,6 +700,7 @@ mod tests {
|
||||
use bp_test_utils::{
|
||||
authority_list, generate_owned_bridge_module_tests, make_default_justification,
|
||||
make_justification_for_header, JustificationGeneratorParams, ALICE, BOB,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
};
|
||||
use codec::Encode;
|
||||
use frame_support::{
|
||||
@@ -693,7 +731,7 @@ mod tests {
|
||||
let init_data = InitializationData {
|
||||
header: Box::new(genesis),
|
||||
authority_list: authority_list(),
|
||||
set_id: 1,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
operating_mode: BasicOperatingMode::Normal,
|
||||
};
|
||||
|
||||
@@ -704,10 +742,11 @@ mod tests {
|
||||
let header = test_header(header.into());
|
||||
let justification = make_default_justification(&header);
|
||||
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -722,10 +761,11 @@ mod tests {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification,
|
||||
set_id,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -749,10 +789,11 @@ mod tests {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification,
|
||||
set_id,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -955,17 +996,30 @@ mod tests {
|
||||
|
||||
let header = test_header(1);
|
||||
|
||||
let params =
|
||||
JustificationGeneratorParams::<TestHeader> { set_id: 2, ..Default::default() };
|
||||
let next_set_id = 2;
|
||||
let params = JustificationGeneratorParams::<TestHeader> {
|
||||
set_id: next_set_id,
|
||||
..Default::default()
|
||||
};
|
||||
let justification = make_justification_for_header(params);
|
||||
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header.clone()),
|
||||
justification.clone(),
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
<Error<TestRuntime>>::InvalidJustification
|
||||
);
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification,
|
||||
next_set_id,
|
||||
),
|
||||
<Error<TestRuntime>>::InvalidJustification
|
||||
<Error<TestRuntime>>::InvalidAuthoritySetId
|
||||
);
|
||||
})
|
||||
}
|
||||
@@ -980,10 +1034,11 @@ mod tests {
|
||||
justification.round = 42;
|
||||
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
<Error<TestRuntime>>::InvalidJustification
|
||||
);
|
||||
@@ -1009,10 +1064,11 @@ mod tests {
|
||||
let justification = make_default_justification(&header);
|
||||
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
<Error<TestRuntime>>::InvalidAuthoritySet
|
||||
);
|
||||
@@ -1047,10 +1103,11 @@ mod tests {
|
||||
let justification = make_default_justification(&header);
|
||||
|
||||
// Let's import our test header
|
||||
let result = Pallet::<TestRuntime>::submit_finality_proof(
|
||||
let result = Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header.clone()),
|
||||
justification.clone(),
|
||||
TEST_GRANDPA_SET_ID,
|
||||
);
|
||||
assert_ok!(result);
|
||||
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No);
|
||||
@@ -1109,10 +1166,11 @@ mod tests {
|
||||
|
||||
// without large digest item ^^^ the relayer would have paid zero transaction fee
|
||||
// (`Pays::No`)
|
||||
let result = Pallet::<TestRuntime>::submit_finality_proof(
|
||||
let result = Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header.clone()),
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
);
|
||||
assert_ok!(result);
|
||||
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
|
||||
@@ -1140,10 +1198,11 @@ mod tests {
|
||||
|
||||
// without many headers in votes ancestries ^^^ the relayer would have paid zero
|
||||
// transaction fee (`Pays::No`)
|
||||
let result = Pallet::<TestRuntime>::submit_finality_proof(
|
||||
let result = Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header.clone()),
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
);
|
||||
assert_ok!(result);
|
||||
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
|
||||
@@ -1169,10 +1228,11 @@ mod tests {
|
||||
|
||||
// Should not be allowed to import this header
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
<Error<TestRuntime>>::UnsupportedScheduledChange
|
||||
);
|
||||
@@ -1194,10 +1254,11 @@ mod tests {
|
||||
|
||||
// Should not be allowed to import this header
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
<Error<TestRuntime>>::UnsupportedScheduledChange
|
||||
);
|
||||
@@ -1219,10 +1280,11 @@ mod tests {
|
||||
|
||||
// Should not be allowed to import this header
|
||||
assert_err!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
<Error<TestRuntime>>::TooManyAuthoritiesInSet
|
||||
);
|
||||
@@ -1283,10 +1345,11 @@ mod tests {
|
||||
let mut invalid_justification = make_default_justification(&header);
|
||||
invalid_justification.round = 42;
|
||||
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
invalid_justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -1451,10 +1514,11 @@ mod tests {
|
||||
let justification = make_default_justification(&header);
|
||||
|
||||
assert_noop!(
|
||||
Pallet::<TestRuntime>::submit_finality_proof(
|
||||
Pallet::<TestRuntime>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(header),
|
||||
justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
),
|
||||
DispatchError::BadOrigin,
|
||||
);
|
||||
|
||||
@@ -731,6 +731,7 @@ pub(crate) mod tests {
|
||||
};
|
||||
use bp_test_utils::{
|
||||
authority_list, generate_owned_bridge_module_tests, make_default_justification,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok,
|
||||
@@ -777,10 +778,11 @@ pub(crate) mod tests {
|
||||
let hash = header.hash();
|
||||
let justification = make_default_justification(&header);
|
||||
assert_ok!(
|
||||
pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::submit_finality_proof(
|
||||
pallet_bridge_grandpa::Pallet::<TestRuntime, BridgesGrandpaPalletInstance>::submit_finality_proof_ex(
|
||||
RuntimeOrigin::signed(1),
|
||||
Box::new(header),
|
||||
justification.clone(),
|
||||
TEST_GRANDPA_SET_ID,
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -244,6 +244,16 @@ pub enum BridgeGrandpaCall<Header: HeaderT> {
|
||||
/// All data, required to initialize the pallet.
|
||||
init_data: InitializationData<Header>,
|
||||
},
|
||||
/// `pallet-bridge-grandpa::Call::submit_finality_proof_ex`
|
||||
#[codec(index = 4)]
|
||||
submit_finality_proof_ex {
|
||||
/// The header that we are going to finalize.
|
||||
finality_target: Box<Header>,
|
||||
/// Finality justification for the `finality_target`.
|
||||
justification: justification::GrandpaJustification<Header>,
|
||||
/// An identifier of the validators set, that have signed the justification.
|
||||
current_set_id: SetId,
|
||||
},
|
||||
}
|
||||
|
||||
/// The `BridgeGrandpaCall` used by a chain.
|
||||
|
||||
Reference in New Issue
Block a user