Skip to content
Snippets Groups Projects
Commit d6ec82c3 authored by Svyatoslav Nikolsky's avatar Svyatoslav Nikolsky Committed by Serban Iorga
Browse files

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
parent ba7d3562
Branches
No related merge requests found
Showing
with 222 additions and 115 deletions
......@@ -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?;
let header_id = header.id();
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 {:?}. 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,
);
// optimize justification before including it into the call
P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?;
continue;
}
}
log::debug!(
target: "bridge",
"[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}",
self.relay_task_name,
P::SourceChain::NAME,
required_header,
P::SourceChain::NAME,
header_id,
);
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);
// 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]));
}
}
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment