mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 04:01: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 MAX_AUTHORITIES_COUNT: u32 = 16;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||
const MAX_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||
}
|
||||
|
||||
impl Chain for BridgedUnderlyingParachain {
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 codec::Encode;
|
||||
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()
|
||||
};
|
||||
|
||||
// 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
|
||||
let actual_call_size: u32 = finality_target
|
||||
.encoded_size()
|
||||
.saturating_add(justification.encoded_size())
|
||||
.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);
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
|
||||
@@ -86,9 +86,8 @@ impl ChainWithGrandpa for TestBridgedChain {
|
||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||
const MAX_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||
}
|
||||
|
||||
/// Return test externalities to use in tests.
|
||||
|
||||
@@ -252,9 +252,8 @@ impl ChainWithGrandpa for TestBridgedChain {
|
||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||
const MAX_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -284,9 +283,8 @@ impl ChainWithGrandpa for OtherBridgedChain {
|
||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||
const MAX_AUTHORITIES_COUNT: u32 = 16;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
|
||||
const MAX_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||
}
|
||||
|
||||
/// Return test externalities to use in tests.
|
||||
|
||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Kusama {
|
||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
// The SignedExtension used by Kusama.
|
||||
|
||||
@@ -42,9 +42,8 @@ use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidi
|
||||
// This chain reuses most of Polkadot primitives.
|
||||
pub use bp_polkadot_core::{
|
||||
AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature,
|
||||
SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE_IN_JUSTIFICATION,
|
||||
EXTRA_STORAGE_PROOF_SIZE, MAX_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY,
|
||||
WORST_HEADER_SIZE_IN_JUSTIFICATION,
|
||||
SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE, EXTRA_STORAGE_PROOF_SIZE,
|
||||
MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY,
|
||||
};
|
||||
|
||||
/// 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 REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
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 REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
/// The SignedExtension used by Polkadot.
|
||||
|
||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Rococo {
|
||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
|
||||
@@ -52,9 +52,8 @@ impl ChainWithGrandpa for Westend {
|
||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||
REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||
const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 = WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
|
||||
@@ -82,8 +82,8 @@ impl<H: HeaderT> GrandpaJustification<H> {
|
||||
.saturating_add(BlockNumberOf::<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
|
||||
.saturating_mul(C::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION);
|
||||
let max_expected_votes_ancestries_size =
|
||||
C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE);
|
||||
|
||||
// justification is round number (u64=8b), a signed GRANDPA commit and the
|
||||
// `votes_ancestries` vector
|
||||
|
||||
@@ -266,36 +266,28 @@ pub trait ChainWithGrandpa: Chain {
|
||||
/// to submitter.
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32;
|
||||
|
||||
/// Maximal size of the chain header. The header may be the header that enacts new GRANDPA
|
||||
/// authorities set (so it has large digest inside).
|
||||
/// Maximal size of the mandatory chain header. Mandatory header is the header that enacts new
|
||||
/// 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
|
||||
/// the call. The limit is only used to compute maximal refund amount and doing calls which
|
||||
/// 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
|
||||
/// 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 average size of
|
||||
/// headers that aren't changing the set.
|
||||
/// Average size of the chain header. 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 average 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
|
||||
/// ancestry and the pallet will accept the call. The limit is only used to compute fee, paid
|
||||
/// by the user at the sending chain. It covers most of cases, but if the actual header,
|
||||
/// submitted with the messages transaction will be larger than the
|
||||
/// `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 and the
|
||||
/// pallet will accept the call. However, if the total size of all `submit_finality_proof`
|
||||
/// arguments exceeds the maximal size, computed using this average size, relayer will only get
|
||||
/// partial refund.
|
||||
///
|
||||
/// This isn't a strict limit. The relay may submit justifications with larger headers in its
|
||||
/// ancestry and the pallet will accept the call. The limit is only used to compute maximal
|
||||
/// refund amount and doing calls which exceed the limit, may be costly to submitter.
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32;
|
||||
/// We expect some headers on production chains that are above this size. But they are rare and
|
||||
/// if rellayer cares about its profitability, we expect it'll select other headers for
|
||||
/// submission.
|
||||
const AVERAGE_HEADER_SIZE: u32;
|
||||
}
|
||||
|
||||
impl<T> ChainWithGrandpa for T
|
||||
@@ -308,9 +300,67 @@ where
|
||||
const MAX_AUTHORITIES_COUNT: u32 = <T::Chain as ChainWithGrandpa>::MAX_AUTHORITIES_COUNT;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 =
|
||||
<T::Chain as ChainWithGrandpa>::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY;
|
||||
const MAX_HEADER_SIZE: u32 = <T::Chain as ChainWithGrandpa>::MAX_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 =
|
||||
<T::Chain as ChainWithGrandpa>::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const WORST_HEADER_SIZE_IN_JUSTIFICATION: u32 =
|
||||
<T::Chain as ChainWithGrandpa>::WORST_HEADER_SIZE_IN_JUSTIFICATION;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 =
|
||||
<T::Chain as ChainWithGrandpa>::MAX_MANDATORY_HEADER_SIZE;
|
||||
const AVERAGE_HEADER_SIZE: u32 = <T::Chain as ChainWithGrandpa>::AVERAGE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some
|
||||
/// reserve here.
|
||||
@@ -75,28 +75,17 @@ pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2;
|
||||
///
|
||||
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
||||
///
|
||||
/// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but let's
|
||||
/// have some reserve and make it 1024.
|
||||
pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: 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;
|
||||
/// This value comes from recent (December, 2023) Kusama headers. Most of headers are `327` bytes
|
||||
/// there, but let's have some reserve and make it 1024.
|
||||
pub const AVERAGE_HEADER_SIZE: u32 = 1024;
|
||||
|
||||
/// Approximate maximal header size on Polkadot-like chains.
|
||||
///
|
||||
/// See [`bp-header-chain::ChainWithGrandpa`] for more details.
|
||||
///
|
||||
/// This value comes from recent (February, 2023) Kusama headers. Maximal header is a mandatory
|
||||
/// header. In its SCALE-encoded form it is `80348` bytes. Let's have some reserve here.
|
||||
pub const MAX_HEADER_SIZE: u32 = 90_000;
|
||||
/// This value comes from recent (December, 2023) Kusama headers. Maximal header is a mandatory
|
||||
/// header. In its SCALE-encoded form it is `113407` bytes. Let's have some reserve here.
|
||||
pub const MAX_MANDATORY_HEADER_SIZE: u32 = 120 * 1024;
|
||||
|
||||
/// 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.
|
||||
|
||||
@@ -10,6 +10,7 @@ anyhow = "1.0"
|
||||
async-std = "1.9.0"
|
||||
async-trait = "0.1"
|
||||
codec = { package = "parity-scale-codec", version = "3.1.5" }
|
||||
env_logger = "0.10"
|
||||
futures = "0.3.29"
|
||||
hex = "0.4"
|
||||
log = "0.4.20"
|
||||
|
||||
@@ -23,8 +23,9 @@ use bp_header_chain::{
|
||||
verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification,
|
||||
JustificationVerificationContext,
|
||||
},
|
||||
AuthoritySet, ConsensusLogReader, FinalityProof, FindEquivocations, GrandpaConsensusLogReader,
|
||||
HeaderFinalityInfo, HeaderGrandpaInfo, StoredHeaderGrandpaInfo,
|
||||
max_expected_submit_finality_proof_arguments_size, AuthoritySet, ConsensusLogReader,
|
||||
FinalityProof, FindEquivocations, GrandpaConsensusLogReader, HeaderFinalityInfo,
|
||||
HeaderGrandpaInfo, StoredHeaderGrandpaInfo,
|
||||
};
|
||||
use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode};
|
||||
use codec::{Decode, Encode};
|
||||
@@ -35,9 +36,22 @@ use relay_substrate_client::{
|
||||
};
|
||||
use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID};
|
||||
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};
|
||||
|
||||
/// 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.
|
||||
#[async_trait]
|
||||
pub trait Engine<C: Chain>: Send {
|
||||
@@ -111,6 +125,14 @@ pub trait Engine<C: Chain>: Send {
|
||||
proof: &mut Self::FinalityProof,
|
||||
) -> 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.
|
||||
async fn prepare_initialization_data(
|
||||
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.
|
||||
async fn prepare_initialization_data(
|
||||
source_client: Client<C>,
|
||||
|
||||
@@ -16,14 +16,16 @@
|
||||
|
||||
//! 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_trait::async_trait;
|
||||
use bp_header_chain::ConsensusLogReader;
|
||||
use bp_runtime::HeaderIdProvider;
|
||||
use futures::{select, FutureExt};
|
||||
use num_traits::{One, Zero};
|
||||
use num_traits::{One, Saturating, Zero};
|
||||
use sp_runtime::traits::Header;
|
||||
|
||||
use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient};
|
||||
@@ -133,29 +135,61 @@ impl<P: SubstrateFinalitySyncPipeline> OnDemandRelay<P::SourceChain, P::TargetCh
|
||||
&self,
|
||||
required_header: BlockNumberOf<P::SourceChain>,
|
||||
) -> Result<(HeaderIdOf<P::SourceChain>, Vec<CallOf<P::TargetChain>>), SubstrateError> {
|
||||
// first find proper header (either `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(required_header).await?;
|
||||
const MAX_ITERATIONS: u32 = 4;
|
||||
let mut iterations = 0;
|
||||
let mut current_required_header = required_header;
|
||||
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
|
||||
P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?;
|
||||
|
||||
// now we have the header and its proof, but we want to minimize our losses, so let's
|
||||
// check if we'll get the full refund for submitting this header
|
||||
let check_result = P::FinalityEngine::check_max_expected_call_size(&header, &proof);
|
||||
if let MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size } = check_result {
|
||||
iterations += 1;
|
||||
current_required_header = header_id.number().saturating_add(One::one());
|
||||
if iterations < MAX_ITERATIONS {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}",
|
||||
"[{}] 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,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Ok((header_id, vec![call]))
|
||||
return Ok((header_id, vec![call]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user