mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 00:31:02 +00:00
Select header that will be fully refunded in on-demand batch finality relay (#2729)
* select header that will be fully refunded for submission in on-demand **batch** finality relay * added the only possible test * spelling * nl * updated comment
This commit is contained in:
committed by
Bastian Köcher
parent
6d35de23be
commit
6c31cdbe19
@@ -376,9 +376,8 @@ impl ChainWithGrandpa for BridgedUnderlyingChain {
|
|||||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||||
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||||
const MAX_HEADER_SIZE: u32 = 256;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chain for BridgedUnderlyingParachain {
|
impl Chain for BridgedUnderlyingParachain {
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
// 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, Error, Pallet};
|
||||||
use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
|
use bp_header_chain::{
|
||||||
|
justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size,
|
||||||
|
ChainWithGrandpa, GrandpaConsensusLogReader,
|
||||||
|
};
|
||||||
use bp_runtime::{BlockNumberOf, OwnedBridgeModule};
|
use bp_runtime::{BlockNumberOf, OwnedBridgeModule};
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight};
|
use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight};
|
||||||
@@ -169,28 +172,28 @@ pub(crate) fn submit_finality_proof_info_from_args<T: Config<I>, I: 'static>(
|
|||||||
Weight::zero()
|
Weight::zero()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// check if the `finality_target` is a mandatory header. If so, we are ready to refund larger
|
||||||
|
// size
|
||||||
|
let is_mandatory_finality_target =
|
||||||
|
GrandpaConsensusLogReader::<BridgedBlockNumber<T, I>>::find_scheduled_change(
|
||||||
|
finality_target.digest(),
|
||||||
|
)
|
||||||
|
.is_some();
|
||||||
|
|
||||||
// we can estimate extra call size easily, without any additional significant overhead
|
// we can estimate extra call size easily, without any additional significant overhead
|
||||||
let actual_call_size: u32 = finality_target
|
let actual_call_size: u32 = finality_target
|
||||||
.encoded_size()
|
.encoded_size()
|
||||||
.saturating_add(justification.encoded_size())
|
.saturating_add(justification.encoded_size())
|
||||||
.saturated_into();
|
.saturated_into();
|
||||||
let max_expected_call_size = max_expected_call_size::<T, I>(required_precommits);
|
let max_expected_call_size = max_expected_submit_finality_proof_arguments_size::<T::BridgedChain>(
|
||||||
|
is_mandatory_finality_target,
|
||||||
|
required_precommits,
|
||||||
|
);
|
||||||
let extra_size = actual_call_size.saturating_sub(max_expected_call_size);
|
let extra_size = actual_call_size.saturating_sub(max_expected_call_size);
|
||||||
|
|
||||||
SubmitFinalityProofInfo { block_number, extra_weight, extra_size }
|
SubmitFinalityProofInfo { block_number, extra_weight, extra_size }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns maximal expected size of `submit_finality_proof` call arguments.
|
|
||||||
fn max_expected_call_size<T: Config<I>, I: 'static>(required_precommits: u32) -> u32 {
|
|
||||||
let max_expected_justification_size =
|
|
||||||
GrandpaJustification::<BridgedHeader<T, I>>::max_reasonable_size::<T::BridgedChain>(
|
|
||||||
required_precommits,
|
|
||||||
);
|
|
||||||
|
|
||||||
// call arguments are header and justification
|
|
||||||
T::BridgedChain::MAX_HEADER_SIZE.saturating_add(max_expected_justification_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -86,9 +86,8 @@ impl ChainWithGrandpa for TestBridgedChain {
|
|||||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES;
|
const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||||
const MAX_HEADER_SIZE: u32 = 256;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return test externalities to use in tests.
|
/// Return test externalities to use in tests.
|
||||||
|
|||||||
@@ -252,9 +252,8 @@ impl ChainWithGrandpa for TestBridgedChain {
|
|||||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||||
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||||
const MAX_HEADER_SIZE: u32 = 256;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -284,9 +283,8 @@ impl ChainWithGrandpa for OtherBridgedChain {
|
|||||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||||
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||||
const MAX_HEADER_SIZE: u32 = 256;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return test externalities to use in tests.
|
/// Return test externalities to use in tests.
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Kusama {
|
|||||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The SignedExtension used by Kusama.
|
// The SignedExtension used by Kusama.
|
||||||
|
|||||||
@@ -42,9 +42,8 @@ use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidi
|
|||||||
// This chain reuses most of Polkadot primitives.
|
// This chain reuses most of Polkadot primitives.
|
||||||
pub use bp_polkadot_core::{
|
pub use bp_polkadot_core::{
|
||||||
AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature,
|
AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature,
|
||||||
SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE_IN_JUSTIFICATION,
|
SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE, EXTRA_STORAGE_PROOF_SIZE,
|
||||||
EXTRA_STORAGE_PROOF_SIZE, MAX_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY,
|
MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY,
|
||||||
WORST_HEADER_SIZE_IN_JUSTIFICATION,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Maximal number of GRANDPA authorities at Polkadot Bulletin chain.
|
/// Maximal number of GRANDPA authorities at Polkadot Bulletin chain.
|
||||||
@@ -208,9 +207,8 @@ impl ChainWithGrandpa for PolkadotBulletin {
|
|||||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
decl_bridge_finality_runtime_apis!(polkadot_bulletin, grandpa);
|
decl_bridge_finality_runtime_apis!(polkadot_bulletin, grandpa);
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Polkadot {
|
|||||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The SignedExtension used by Polkadot.
|
/// The SignedExtension used by Polkadot.
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Rococo {
|
|||||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Westend {
|
|||||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ impl<H: HeaderT> GrandpaJustification<H> {
|
|||||||
.saturating_add(BlockNumberOf::<C>::max_encoded_len().saturated_into())
|
.saturating_add(BlockNumberOf::<C>::max_encoded_len().saturated_into())
|
||||||
.saturating_add(HashOf::<C>::max_encoded_len().saturated_into());
|
.saturating_add(HashOf::<C>::max_encoded_len().saturated_into());
|
||||||
|
|
||||||
let max_expected_votes_ancestries_size = C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY
|
let max_expected_votes_ancestries_size =
|
||||||
.saturating_mul(C::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION);
|
C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE);
|
||||||
|
|
||||||
// justification is round number (u64=8b), a signed GRANDPA commit and the
|
// justification is round number (u64=8b), a signed GRANDPA commit and the
|
||||||
// `votes_ancestries` vector
|
// `votes_ancestries` vector
|
||||||
|
|||||||
@@ -266,36 +266,28 @@ pub trait ChainWithGrandpa: Chain {
|
|||||||
/// to submitter.
|
/// to submitter.
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32;
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32;
|
||||||
|
|
||||||
/// Maximal size of the chain header. The header may be the header that enacts new GRANDPA
|
/// Maximal size of the mandatory chain header. Mandatory header is the header that enacts new
|
||||||
/// authorities set (so it has large digest inside).
|
/// GRANDPA authorities set (so it has large digest inside).
|
||||||
///
|
///
|
||||||
/// This isn't a strict limit. The relay may submit larger headers and the pallet will accept
|
/// This isn't a strict limit. The relay may submit larger headers and the pallet will accept
|
||||||
/// the call. The limit is only used to compute maximal refund amount and doing calls which
|
/// the call. The limit is only used to compute maximal refund amount and doing calls which
|
||||||
/// exceed the limit, may be costly to submitter.
|
/// exceed the limit, may be costly to submitter.
|
||||||
const MAX_HEADER_SIZE: u32;
|
const MAX_MANDATORY_HEADER_SIZE: u32;
|
||||||
|
|
||||||
/// Average size of the chain header from justification ancestry. We don't expect to see there
|
/// Average size of the chain header. We don't expect to see there headers that change GRANDPA
|
||||||
/// headers that change GRANDPA authorities set (GRANDPA will probably be able to finalize at
|
/// authorities set (GRANDPA will probably be able to finalize at least one additional header
|
||||||
/// least one additional header per session on non test chains), so this is average size of
|
/// per session on non test chains), so this is average size of headers that aren't changing the
|
||||||
/// headers that aren't changing the set.
|
/// set.
|
||||||
///
|
///
|
||||||
/// This isn't a strict limit. The relay may submit justifications with larger headers in its
|
/// This isn't a strict limit. The relay may submit justifications with larger headers and the
|
||||||
/// ancestry and the pallet will accept the call. The limit is only used to compute fee, paid
|
/// pallet will accept the call. However, if the total size of all `submit_finality_proof`
|
||||||
/// by the user at the sending chain. It covers most of cases, but if the actual header,
|
/// arguments exceeds the maximal size, computed using this average size, relayer will only get
|
||||||
/// submitted with the messages transaction will be larger than the
|
/// partial refund.
|
||||||
/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION`, the difference (`WORST_HEADER_SIZE_IN_JUSTIFICATION`
|
|
||||||
/// - `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION`) must be covered by the sending chain.
|
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32;
|
|
||||||
|
|
||||||
/// Worst-case size of the chain header from justification ancestry. We don't expect to see
|
|
||||||
/// there headers that change GRANDPA authorities set (GRANDPA will probably be able to finalize
|
|
||||||
/// at least one additional header per session on non test chains), so this is the worst-case
|
|
||||||
/// size of headers that aren't changing the set.
|
|
||||||
///
|
///
|
||||||
/// This isn't a strict limit. The relay may submit justifications with larger headers in its
|
/// We expect some headers on production chains that are above this size. But they are rare and
|
||||||
/// ancestry and the pallet will accept the call. The limit is only used to compute maximal
|
/// if rellayer cares about its profitability, we expect it'll select other headers for
|
||||||
/// refund amount and doing calls which exceed the limit, may be costly to submitter.
|
/// submission.
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32;
|
const AVERAGE_HEADER_SIZE: u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ChainWithGrandpa for T
|
impl<T> ChainWithGrandpa for T
|
||||||
@@ -308,9 +300,67 @@ where
|
|||||||
const MAX_AUTHORITIES_COUNT: u32 = <T::Chain as ChainWithGrandpa>::MAX_AUTHORITIES_COUNT;
|
const MAX_AUTHORITIES_COUNT: u32 = <T::Chain as ChainWithGrandpa>::MAX_AUTHORITIES_COUNT;
|
||||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||||
<T::Chain as ChainWithGrandpa>::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
<T::Chain as ChainWithGrandpa>::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||||
const MAX_HEADER_SIZE: u32 = <T::Chain as ChainWithGrandpa>::MAX_HEADER_SIZE;
|
const MAX_MANDATORY_HEADER_SIZE: u32 =
|
||||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 =
|
<T::Chain as ChainWithGrandpa>::MAX_MANDATORY_HEADER_SIZE;
|
||||||
<T::Chain as ChainWithGrandpa>::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
const AVERAGE_HEADER_SIZE: u32 = <T::Chain as ChainWithGrandpa>::AVERAGE_HEADER_SIZE;
|
||||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 =
|
}
|
||||||
<T::Chain as ChainWithGrandpa>::WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
|
||||||
|
/// Returns maximal expected size of `submit_finality_proof` call arguments.
|
||||||
|
pub fn max_expected_submit_finality_proof_arguments_size<C: ChainWithGrandpa>(
|
||||||
|
is_mandatory_finality_target: bool,
|
||||||
|
precommits: u32,
|
||||||
|
) -> u32 {
|
||||||
|
let max_expected_justification_size =
|
||||||
|
GrandpaJustification::<HeaderOf<C>>::max_reasonable_size::<C>(precommits);
|
||||||
|
|
||||||
|
// call arguments are header and justification
|
||||||
|
let max_expected_finality_target_size = if is_mandatory_finality_target {
|
||||||
|
C::MAX_MANDATORY_HEADER_SIZE
|
||||||
|
} else {
|
||||||
|
C::AVERAGE_HEADER_SIZE
|
||||||
|
};
|
||||||
|
max_expected_finality_target_size.saturating_add(max_expected_justification_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use frame_support::weights::Weight;
|
||||||
|
use sp_runtime::{testing::H256, traits::BlakeTwo256, MultiSignature};
|
||||||
|
|
||||||
|
struct TestChain;
|
||||||
|
|
||||||
|
impl Chain for TestChain {
|
||||||
|
type BlockNumber = u32;
|
||||||
|
type Hash = H256;
|
||||||
|
type Hasher = BlakeTwo256;
|
||||||
|
type Header = sp_runtime::generic::Header<u32, BlakeTwo256>;
|
||||||
|
type AccountId = u64;
|
||||||
|
type Balance = u64;
|
||||||
|
type Nonce = u64;
|
||||||
|
type Signature = MultiSignature;
|
||||||
|
|
||||||
|
fn max_extrinsic_size() -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
fn max_extrinsic_weight() -> Weight {
|
||||||
|
Weight::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChainWithGrandpa for TestChain {
|
||||||
|
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "Test";
|
||||||
|
const MAX_AUTHORITIES_COUNT: u32 = 128;
|
||||||
|
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2;
|
||||||
|
const MAX_MANDATORY_HEADER_SIZE: u32 = 100_000;
|
||||||
|
const AVERAGE_HEADER_SIZE: u32 = 1_024;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn max_expected_submit_finality_proof_arguments_size_respects_mandatory_argument() {
|
||||||
|
assert!(
|
||||||
|
max_expected_submit_finality_proof_arguments_size::<TestChain>(true, 100) >
|
||||||
|
max_expected_submit_finality_proof_arguments_size::<TestChain>(false, 100),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 1_256;
|
|||||||
///
|
///
|
||||||
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
||||||
///
|
///
|
||||||
/// This value comes from recent (February, 2023) Kusama and Polkadot headers. There are no
|
/// This value comes from recent (December, 2023) Kusama and Polkadot headers. There are no
|
||||||
/// justifications with any additional headers in votes ancestry, so reasonable headers may
|
/// justifications with any additional headers in votes ancestry, so reasonable headers may
|
||||||
/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some
|
/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some
|
||||||
/// reserve here.
|
/// reserve here.
|
||||||
@@ -75,28 +75,17 @@ pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2;
|
|||||||
///
|
///
|
||||||
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
||||||
///
|
///
|
||||||
/// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but let's
|
/// This value comes from recent (December, 2023) Kusama headers. Most of headers are `327` bytes
|
||||||
/// have some reserve and make it 1024.
|
/// there, but let's have some reserve and make it 1024.
|
||||||
pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 1024;
|
pub const AVERAGE_HEADER_SIZE: u32 = 1024;
|
||||||
|
|
||||||
/// Worst-case header size in `votes_ancestries` field of justification on Polkadot-like
|
|
||||||
/// chains.
|
|
||||||
///
|
|
||||||
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
|
||||||
///
|
|
||||||
/// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but some
|
|
||||||
/// non-mandatory headers has size `40kb` (they contain the BABE epoch descriptor with all
|
|
||||||
/// authorities - just like our mandatory header). Since we assume `2` headers in justification
|
|
||||||
/// votes ancestry, let's set average header to `40kb / 2`.
|
|
||||||
pub const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 20 * 1024;
|
|
||||||
|
|
||||||
/// Approximate maximal header size on Polkadot-like chains.
|
/// Approximate maximal header size on Polkadot-like chains.
|
||||||
///
|
///
|
||||||
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
||||||
///
|
///
|
||||||
/// This value comes from recent (February, 2023) Kusama headers. Maximal header is a mandatory
|
/// This value comes from recent (December, 2023) Kusama headers. Maximal header is a mandatory
|
||||||
/// header. In its SCALE-encoded form it is `80348` bytes. Let's have some reserve here.
|
/// header. In its SCALE-encoded form it is `113407` bytes. Let's have some reserve here.
|
||||||
pub const MAX_HEADER_SIZE: u32 = 90_000;
|
pub const MAX_MANDATORY_HEADER_SIZE: u32 = 120 * 1024;
|
||||||
|
|
||||||
/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at
|
/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at
|
||||||
/// Polkadot-like chain. This mostly depends on number of entries in the storage trie.
|
/// Polkadot-like chain. This mostly depends on number of entries in the storage trie.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ anyhow = "1.0"
|
|||||||
async-std = "1.9.0"
|
async-std = "1.9.0"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
codec = { package = "parity-scale-codec", version = "3.1.5" }
|
codec = { package = "parity-scale-codec", version = "3.1.5" }
|
||||||
|
env_logger = "0.10"
|
||||||
futures = "0.3.29"
|
futures = "0.3.29"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ use bp_header_chain::{
|
|||||||
verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification,
|
verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification,
|
||||||
JustificationVerificationContext,
|
JustificationVerificationContext,
|
||||||
},
|
},
|
||||||
AuthoritySet, ConsensusLogReader, FinalityProof, FindEquivocations, GrandpaConsensusLogReader,
|
max_expected_submit_finality_proof_arguments_size, AuthoritySet, ConsensusLogReader,
|
||||||
HeaderFinalityInfo, HeaderGrandpaInfo, StoredHeaderGrandpaInfo,
|
FinalityProof, FindEquivocations, GrandpaConsensusLogReader, HeaderFinalityInfo,
|
||||||
|
HeaderGrandpaInfo, StoredHeaderGrandpaInfo,
|
||||||
};
|
};
|
||||||
use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode};
|
use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
@@ -35,9 +36,22 @@ use relay_substrate_client::{
|
|||||||
};
|
};
|
||||||
use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID};
|
use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID};
|
||||||
use sp_core::{storage::StorageKey, Bytes};
|
use sp_core::{storage::StorageKey, Bytes};
|
||||||
use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId};
|
use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId, SaturatedConversion};
|
||||||
use std::{fmt::Debug, marker::PhantomData};
|
use std::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
|
/// Result of checking maximal expected call size.
|
||||||
|
pub enum MaxExpectedCallSizeCheck {
|
||||||
|
/// Size is ok and call will be refunded.
|
||||||
|
Ok,
|
||||||
|
/// The call size exceeds the maximal expected and relayer will only get partial refund.
|
||||||
|
Exceeds {
|
||||||
|
/// Actual call size.
|
||||||
|
call_size: u32,
|
||||||
|
/// Maximal expected call size.
|
||||||
|
max_call_size: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// Finality engine, used by the Substrate chain.
|
/// Finality engine, used by the Substrate chain.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Engine<C: Chain>: Send {
|
pub trait Engine<C: Chain>: Send {
|
||||||
@@ -111,6 +125,14 @@ pub trait Engine<C: Chain>: Send {
|
|||||||
proof: &mut Self::FinalityProof,
|
proof: &mut Self::FinalityProof,
|
||||||
) -> Result<(), SubstrateError>;
|
) -> Result<(), SubstrateError>;
|
||||||
|
|
||||||
|
/// Checks whether the given `header` and its finality `proof` fit the maximal expected
|
||||||
|
/// call size limit. If result is `MaxExpectedCallSizeCheck::Exceeds { .. }`, this
|
||||||
|
/// submission won't be fully refunded and relayer will spend its own funds on that.
|
||||||
|
fn check_max_expected_call_size(
|
||||||
|
header: &C::Header,
|
||||||
|
proof: &Self::FinalityProof,
|
||||||
|
) -> MaxExpectedCallSizeCheck;
|
||||||
|
|
||||||
/// Prepare initialization data for the finality bridge pallet.
|
/// Prepare initialization data for the finality bridge pallet.
|
||||||
async fn prepare_initialization_data(
|
async fn prepare_initialization_data(
|
||||||
client: Client<C>,
|
client: Client<C>,
|
||||||
@@ -219,6 +241,24 @@ impl<C: ChainWithGrandpa> Engine<C> for Grandpa<C> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_max_expected_call_size(
|
||||||
|
header: &C::Header,
|
||||||
|
proof: &Self::FinalityProof,
|
||||||
|
) -> MaxExpectedCallSizeCheck {
|
||||||
|
let is_mandatory = Self::ConsensusLogReader::schedules_authorities_change(header.digest());
|
||||||
|
let call_size: u32 =
|
||||||
|
header.encoded_size().saturating_add(proof.encoded_size()).saturated_into();
|
||||||
|
let max_call_size = max_expected_submit_finality_proof_arguments_size::<C>(
|
||||||
|
is_mandatory,
|
||||||
|
proof.commit.precommits.len().saturated_into(),
|
||||||
|
);
|
||||||
|
if call_size > max_call_size {
|
||||||
|
MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size }
|
||||||
|
} else {
|
||||||
|
MaxExpectedCallSizeCheck::Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Prepare initialization data for the GRANDPA verifier pallet.
|
/// Prepare initialization data for the GRANDPA verifier pallet.
|
||||||
async fn prepare_initialization_data(
|
async fn prepare_initialization_data(
|
||||||
source_client: Client<C>,
|
source_client: Client<C>,
|
||||||
|
|||||||
@@ -16,14 +16,16 @@
|
|||||||
|
|
||||||
//! On-demand Substrate -> Substrate header finality relay.
|
//! On-demand Substrate -> Substrate header finality relay.
|
||||||
|
|
||||||
use crate::finality::SubmitFinalityProofCallBuilder;
|
use crate::{
|
||||||
|
finality::SubmitFinalityProofCallBuilder, finality_base::engine::MaxExpectedCallSizeCheck,
|
||||||
|
};
|
||||||
|
|
||||||
use async_std::sync::{Arc, Mutex};
|
use async_std::sync::{Arc, Mutex};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bp_header_chain::ConsensusLogReader;
|
use bp_header_chain::ConsensusLogReader;
|
||||||
use bp_runtime::HeaderIdProvider;
|
use bp_runtime::HeaderIdProvider;
|
||||||
use futures::{select, FutureExt};
|
use futures::{select, FutureExt};
|
||||||
use num_traits::{One, Zero};
|
use num_traits::{One, Saturating, Zero};
|
||||||
use sp_runtime::traits::Header;
|
use sp_runtime::traits::Header;
|
||||||
|
|
||||||
use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient};
|
use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient};
|
||||||
@@ -133,29 +135,61 @@ impl<P: SubstrateFinalitySyncPipeline> OnDemandRelay<P::SourceChain, P::TargetCh
|
|||||||
&self,
|
&self,
|
||||||
required_header: BlockNumberOf<P::SourceChain>,
|
required_header: BlockNumberOf<P::SourceChain>,
|
||||||
) -> Result<(HeaderIdOf<P::SourceChain>, Vec<CallOf<P::TargetChain>>), SubstrateError> {
|
) -> Result<(HeaderIdOf<P::SourceChain>, Vec<CallOf<P::TargetChain>>), SubstrateError> {
|
||||||
// first find proper header (either `required_header`) or its descendant
|
const MAX_ITERATIONS: u32 = 4;
|
||||||
let finality_source = SubstrateFinalitySource::<P>::new(self.source_client.clone(), None);
|
let mut iterations = 0;
|
||||||
let (header, mut proof) = finality_source.prove_block_finality(required_header).await?;
|
let mut current_required_header = required_header;
|
||||||
let header_id = header.id();
|
loop {
|
||||||
|
// first find proper header (either `current_required_header`) or its descendant
|
||||||
|
let finality_source =
|
||||||
|
SubstrateFinalitySource::<P>::new(self.source_client.clone(), None);
|
||||||
|
let (header, mut proof) =
|
||||||
|
finality_source.prove_block_finality(current_required_header).await?;
|
||||||
|
let header_id = header.id();
|
||||||
|
|
||||||
// optimize justification before including it into the call
|
// optimize justification before including it into the call
|
||||||
P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?;
|
P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?;
|
||||||
|
|
||||||
log::debug!(
|
// now we have the header and its proof, but we want to minimize our losses, so let's
|
||||||
target: "bridge",
|
// check if we'll get the full refund for submitting this header
|
||||||
"[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}",
|
let check_result = P::FinalityEngine::check_max_expected_call_size(&header, &proof);
|
||||||
self.relay_task_name,
|
if let MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size } = check_result {
|
||||||
P::SourceChain::NAME,
|
iterations += 1;
|
||||||
required_header,
|
current_required_header = header_id.number().saturating_add(One::one());
|
||||||
P::SourceChain::NAME,
|
if iterations < MAX_ITERATIONS {
|
||||||
header_id,
|
log::debug!(
|
||||||
);
|
target: "bridge",
|
||||||
|
"[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}. But it is too large: {} vs {}. \
|
||||||
|
Going to select next header",
|
||||||
|
self.relay_task_name,
|
||||||
|
P::SourceChain::NAME,
|
||||||
|
required_header,
|
||||||
|
P::SourceChain::NAME,
|
||||||
|
header_id,
|
||||||
|
call_size,
|
||||||
|
max_call_size,
|
||||||
|
);
|
||||||
|
|
||||||
// and then craft the submit-proof call
|
continue;
|
||||||
let call =
|
}
|
||||||
P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
|
}
|
||||||
|
|
||||||
Ok((header_id, vec![call]))
|
log::debug!(
|
||||||
|
target: "bridge",
|
||||||
|
"[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?} (after {} iterations)",
|
||||||
|
self.relay_task_name,
|
||||||
|
P::SourceChain::NAME,
|
||||||
|
required_header,
|
||||||
|
P::SourceChain::NAME,
|
||||||
|
header_id,
|
||||||
|
iterations,
|
||||||
|
);
|
||||||
|
|
||||||
|
// and then craft the submit-proof call
|
||||||
|
let call =
|
||||||
|
P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
|
||||||
|
|
||||||
|
return Ok((header_id, vec![call]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user