mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 14: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
@@ -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?;
|
||||
// optimize justification before including it into the call
|
||||
P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?;
|
||||
|
||||
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,
|
||||
);
|
||||
// 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,
|
||||
);
|
||||
|
||||
// and then craft the submit-proof call
|
||||
let call =
|
||||
P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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