mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 01:41:03 +00:00
Batch transactions in complex relays (#1669)
* batch transactions in message relay: API prototype * get rid of Box<dyn BatchTransaction> and actually submit it * test batch transactions * message_lane_loop_works_with_batch_transactions * removed logger * BatchConfirmationTransaction + BatchDeliveryTransaction * more prototyping * fmt * continue with batch calls * impl BatchCallBuilder for () * BatchDeliveryTransaction impl * BundledBatchCallBuilder * proper impl of BundledBatchCallBuilder + use it in RialtoParachain -> Millau * impl prove_header in OnDemandHeadersRelay * impl OnDemandParachainsRelay::prove_header (needs extensive tests) * added a couple of TODOs * return Result<Option<BatchTx>> when asking for more headers * prove headers when reauire_* is called && return proper headers from required_header_id * split parachains::prove_header and test select_headers_to_prove * more traces and leave TODOs * use finality stream in SubstrateFinalitySource::prove_block_finality * prove parachain head at block, selected by headers relay * const ANCIENT_BLOCK_THRESHOLD * TODO -> proof * clippy and spelling * BatchCallBuilder::build_batch_call() returns Result * read first proof from two streams * FailedToFindFinalityProof -> FinalityProofNotFound * changed select_headers_to_prove to version from PR review
This commit is contained in:
committed by
Bastian Köcher
parent
a732a04ed4
commit
be27bd5e97
@@ -109,9 +109,28 @@ pub struct NoncesSubmitArtifacts<T> {
|
||||
pub tx_tracker: T,
|
||||
}
|
||||
|
||||
/// Batch transaction that already submit some headers and needs to be extended with
|
||||
/// messages/delivery proof before sending.
|
||||
#[async_trait]
|
||||
pub trait BatchTransaction<HeaderId, Proof, TransactionTracker, Error>: Send {
|
||||
/// Header that was required in the original call and which is bundled within this
|
||||
/// batch transaction.
|
||||
fn required_header_id(&self) -> HeaderId;
|
||||
|
||||
/// Append proof and send transaction to the connected node.
|
||||
async fn append_proof_and_send(self, proof: Proof) -> Result<TransactionTracker, Error>;
|
||||
}
|
||||
|
||||
/// Source client trait.
|
||||
#[async_trait]
|
||||
pub trait SourceClient<P: MessageLane>: RelayClient {
|
||||
/// Type of batch transaction that submits finality and message receiving proof.
|
||||
type BatchTransaction: BatchTransaction<
|
||||
TargetHeaderIdOf<P>,
|
||||
P::MessagesReceivingProof,
|
||||
Self::TransactionTracker,
|
||||
Self::Error,
|
||||
>;
|
||||
/// Transaction tracker to track submitted transactions.
|
||||
type TransactionTracker: TransactionTracker<HeaderId = SourceHeaderIdOf<P>>;
|
||||
|
||||
@@ -156,12 +175,31 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
|
||||
) -> Result<Self::TransactionTracker, Self::Error>;
|
||||
|
||||
/// We need given finalized target header on source to continue synchronization.
|
||||
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<P>);
|
||||
///
|
||||
/// We assume that the absence of header `id` has already been checked by caller.
|
||||
///
|
||||
/// The client may return `Some(_)`, which means that nothing has happened yet and
|
||||
/// the caller must generate and append message receiving proof to the batch transaction
|
||||
/// to actually send it (along with required header) to the node.
|
||||
///
|
||||
/// If function has returned `None`, it means that the caller now must wait for the
|
||||
/// appearance of the target header `id` at the source client.
|
||||
async fn require_target_header_on_source(
|
||||
&self,
|
||||
id: TargetHeaderIdOf<P>,
|
||||
) -> Result<Option<Self::BatchTransaction>, Self::Error>;
|
||||
}
|
||||
|
||||
/// Target client trait.
|
||||
#[async_trait]
|
||||
pub trait TargetClient<P: MessageLane>: RelayClient {
|
||||
/// Type of batch transaction that submits finality and messages proof.
|
||||
type BatchTransaction: BatchTransaction<
|
||||
SourceHeaderIdOf<P>,
|
||||
P::MessagesProof,
|
||||
Self::TransactionTracker,
|
||||
Self::Error,
|
||||
>;
|
||||
/// Transaction tracker to track submitted transactions.
|
||||
type TransactionTracker: TransactionTracker<HeaderId = TargetHeaderIdOf<P>>;
|
||||
|
||||
@@ -201,7 +239,17 @@ pub trait TargetClient<P: MessageLane>: RelayClient {
|
||||
) -> Result<NoncesSubmitArtifacts<Self::TransactionTracker>, Self::Error>;
|
||||
|
||||
/// We need given finalized source header on target to continue synchronization.
|
||||
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P>);
|
||||
///
|
||||
/// The client may return `Some(_)`, which means that nothing has happened yet and
|
||||
/// the caller must generate and append messages proof to the batch transaction
|
||||
/// to actually send it (along with required header) to the node.
|
||||
///
|
||||
/// If function has returned `None`, it means that the caller now must wait for the
|
||||
/// appearance of the source header `id` at the target client.
|
||||
async fn require_source_header_on_target(
|
||||
&self,
|
||||
id: SourceHeaderIdOf<P>,
|
||||
) -> Result<Option<Self::BatchTransaction>, Self::Error>;
|
||||
}
|
||||
|
||||
/// State of the client.
|
||||
@@ -483,6 +531,61 @@ pub(crate) mod tests {
|
||||
type TargetHeaderHash = TestTargetHeaderHash;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TestMessagesBatchTransaction {
|
||||
data: Arc<Mutex<TestClientData>>,
|
||||
required_header_id: TestSourceHeaderId,
|
||||
tx_tracker: TestTransactionTracker,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl BatchTransaction<TestSourceHeaderId, TestMessagesProof, TestTransactionTracker, TestError>
|
||||
for TestMessagesBatchTransaction
|
||||
{
|
||||
fn required_header_id(&self) -> TestSourceHeaderId {
|
||||
self.required_header_id
|
||||
}
|
||||
|
||||
async fn append_proof_and_send(
|
||||
self,
|
||||
proof: TestMessagesProof,
|
||||
) -> Result<TestTransactionTracker, TestError> {
|
||||
let mut data = self.data.lock();
|
||||
data.receive_messages(proof);
|
||||
Ok(self.tx_tracker)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TestConfirmationBatchTransaction {
|
||||
data: Arc<Mutex<TestClientData>>,
|
||||
required_header_id: TestTargetHeaderId,
|
||||
tx_tracker: TestTransactionTracker,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl
|
||||
BatchTransaction<
|
||||
TestTargetHeaderId,
|
||||
TestMessagesReceivingProof,
|
||||
TestTransactionTracker,
|
||||
TestError,
|
||||
> for TestConfirmationBatchTransaction
|
||||
{
|
||||
fn required_header_id(&self) -> TestTargetHeaderId {
|
||||
self.required_header_id
|
||||
}
|
||||
|
||||
async fn append_proof_and_send(
|
||||
self,
|
||||
proof: TestMessagesReceivingProof,
|
||||
) -> Result<TestTransactionTracker, TestError> {
|
||||
let mut data = self.data.lock();
|
||||
data.receive_messages_delivery_proof(proof);
|
||||
Ok(self.tx_tracker)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TestTransactionTracker(TrackedTransactionStatus<TestTargetHeaderId>);
|
||||
|
||||
@@ -517,8 +620,10 @@ pub(crate) mod tests {
|
||||
target_latest_confirmed_received_nonce: MessageNonce,
|
||||
target_tracked_transaction_status: TrackedTransactionStatus<TestTargetHeaderId>,
|
||||
submitted_messages_proofs: Vec<TestMessagesProof>,
|
||||
target_to_source_batch_transaction: Option<TestConfirmationBatchTransaction>,
|
||||
target_to_source_header_required: Option<TestTargetHeaderId>,
|
||||
target_to_source_header_requirements: Vec<TestTargetHeaderId>,
|
||||
source_to_target_batch_transaction: Option<TestMessagesBatchTransaction>,
|
||||
source_to_target_header_required: Option<TestSourceHeaderId>,
|
||||
source_to_target_header_requirements: Vec<TestSourceHeaderId>,
|
||||
}
|
||||
@@ -546,14 +651,38 @@ pub(crate) mod tests {
|
||||
Default::default(),
|
||||
)),
|
||||
submitted_messages_proofs: Vec::new(),
|
||||
target_to_source_batch_transaction: None,
|
||||
target_to_source_header_required: None,
|
||||
target_to_source_header_requirements: Vec::new(),
|
||||
source_to_target_batch_transaction: None,
|
||||
source_to_target_header_required: None,
|
||||
source_to_target_header_requirements: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestClientData {
|
||||
fn receive_messages(&mut self, proof: TestMessagesProof) {
|
||||
self.target_state.best_self =
|
||||
HeaderId(self.target_state.best_self.0 + 1, self.target_state.best_self.1 + 1);
|
||||
self.target_state.best_finalized_self = self.target_state.best_self;
|
||||
self.target_latest_received_nonce = *proof.0.end();
|
||||
if let Some(target_latest_confirmed_received_nonce) = proof.1 {
|
||||
self.target_latest_confirmed_received_nonce =
|
||||
target_latest_confirmed_received_nonce;
|
||||
}
|
||||
self.submitted_messages_proofs.push(proof);
|
||||
}
|
||||
|
||||
fn receive_messages_delivery_proof(&mut self, proof: TestMessagesReceivingProof) {
|
||||
self.source_state.best_self =
|
||||
HeaderId(self.source_state.best_self.0 + 1, self.source_state.best_self.1 + 1);
|
||||
self.source_state.best_finalized_self = self.source_state.best_self;
|
||||
self.submitted_messages_receiving_proofs.push(proof);
|
||||
self.source_latest_confirmed_received_nonce = proof;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestSourceClient {
|
||||
data: Arc<Mutex<TestClientData>>,
|
||||
@@ -588,6 +717,7 @@ pub(crate) mod tests {
|
||||
|
||||
#[async_trait]
|
||||
impl SourceClient<TestMessageLane> for TestSourceClient {
|
||||
type BatchTransaction = TestConfirmationBatchTransaction;
|
||||
type TransactionTracker = TestTransactionTracker;
|
||||
|
||||
async fn state(&self) -> Result<SourceClientState<TestMessageLane>, TestError> {
|
||||
@@ -675,21 +805,25 @@ pub(crate) mod tests {
|
||||
) -> Result<Self::TransactionTracker, TestError> {
|
||||
let mut data = self.data.lock();
|
||||
(self.tick)(&mut data);
|
||||
data.source_state.best_self =
|
||||
HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1);
|
||||
data.source_state.best_finalized_self = data.source_state.best_self;
|
||||
data.submitted_messages_receiving_proofs.push(proof);
|
||||
data.source_latest_confirmed_received_nonce = proof;
|
||||
data.receive_messages_delivery_proof(proof);
|
||||
(self.post_tick)(&mut data);
|
||||
Ok(TestTransactionTracker(data.source_tracked_transaction_status))
|
||||
}
|
||||
|
||||
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<TestMessageLane>) {
|
||||
async fn require_target_header_on_source(
|
||||
&self,
|
||||
id: TargetHeaderIdOf<TestMessageLane>,
|
||||
) -> Result<Option<Self::BatchTransaction>, Self::Error> {
|
||||
let mut data = self.data.lock();
|
||||
data.target_to_source_header_required = Some(id);
|
||||
data.target_to_source_header_requirements.push(id);
|
||||
(self.tick)(&mut data);
|
||||
(self.post_tick)(&mut data);
|
||||
|
||||
Ok(data.target_to_source_batch_transaction.take().map(|mut tx| {
|
||||
tx.required_header_id = id;
|
||||
tx
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,6 +861,7 @@ pub(crate) mod tests {
|
||||
|
||||
#[async_trait]
|
||||
impl TargetClient<TestMessageLane> for TestTargetClient {
|
||||
type BatchTransaction = TestMessagesBatchTransaction;
|
||||
type TransactionTracker = TestTransactionTracker;
|
||||
|
||||
async fn state(&self) -> Result<TargetClientState<TestMessageLane>, TestError> {
|
||||
@@ -798,15 +933,7 @@ pub(crate) mod tests {
|
||||
if data.is_target_fails {
|
||||
return Err(TestError)
|
||||
}
|
||||
data.target_state.best_self =
|
||||
HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1);
|
||||
data.target_state.best_finalized_self = data.target_state.best_self;
|
||||
data.target_latest_received_nonce = *proof.0.end();
|
||||
if let Some(target_latest_confirmed_received_nonce) = proof.1 {
|
||||
data.target_latest_confirmed_received_nonce =
|
||||
target_latest_confirmed_received_nonce;
|
||||
}
|
||||
data.submitted_messages_proofs.push(proof);
|
||||
data.receive_messages(proof);
|
||||
(self.post_tick)(&mut data);
|
||||
Ok(NoncesSubmitArtifacts {
|
||||
nonces,
|
||||
@@ -814,17 +941,25 @@ pub(crate) mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<TestMessageLane>) {
|
||||
async fn require_source_header_on_target(
|
||||
&self,
|
||||
id: SourceHeaderIdOf<TestMessageLane>,
|
||||
) -> Result<Option<Self::BatchTransaction>, Self::Error> {
|
||||
let mut data = self.data.lock();
|
||||
data.source_to_target_header_required = Some(id);
|
||||
data.source_to_target_header_requirements.push(id);
|
||||
(self.tick)(&mut data);
|
||||
(self.post_tick)(&mut data);
|
||||
|
||||
Ok(data.source_to_target_batch_transaction.take().map(|mut tx| {
|
||||
tx.required_header_id = id;
|
||||
tx
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_loop_test(
|
||||
data: TestClientData,
|
||||
data: Arc<Mutex<TestClientData>>,
|
||||
source_tick: Arc<dyn Fn(&mut TestClientData) + Send + Sync>,
|
||||
source_post_tick: Arc<dyn Fn(&mut TestClientData) + Send + Sync>,
|
||||
target_tick: Arc<dyn Fn(&mut TestClientData) + Send + Sync>,
|
||||
@@ -832,8 +967,6 @@ pub(crate) mod tests {
|
||||
exit_signal: impl Future<Output = ()> + 'static + Send,
|
||||
) -> TestClientData {
|
||||
async_std::task::block_on(async {
|
||||
let data = Arc::new(Mutex::new(data));
|
||||
|
||||
let source_client = TestSourceClient {
|
||||
data: data.clone(),
|
||||
tick: source_tick,
|
||||
@@ -876,7 +1009,7 @@ pub(crate) mod tests {
|
||||
// able to deliver messages.
|
||||
let (exit_sender, exit_receiver) = unbounded();
|
||||
let result = run_loop_test(
|
||||
TestClientData {
|
||||
Arc::new(Mutex::new(TestClientData {
|
||||
is_source_fails: true,
|
||||
source_state: ClientState {
|
||||
best_self: HeaderId(0, 0),
|
||||
@@ -893,7 +1026,7 @@ pub(crate) mod tests {
|
||||
},
|
||||
target_latest_received_nonce: 0,
|
||||
..Default::default()
|
||||
},
|
||||
})),
|
||||
Arc::new(|data: &mut TestClientData| {
|
||||
if data.is_source_reconnected {
|
||||
data.is_source_fails = false;
|
||||
@@ -929,7 +1062,7 @@ pub(crate) mod tests {
|
||||
let (source_exit_sender, exit_receiver) = unbounded();
|
||||
let target_exit_sender = source_exit_sender.clone();
|
||||
let result = run_loop_test(
|
||||
TestClientData {
|
||||
Arc::new(Mutex::new(TestClientData {
|
||||
source_state: ClientState {
|
||||
best_self: HeaderId(0, 0),
|
||||
best_finalized_self: HeaderId(0, 0),
|
||||
@@ -947,7 +1080,7 @@ pub(crate) mod tests {
|
||||
target_latest_received_nonce: 0,
|
||||
target_tracked_transaction_status: TrackedTransactionStatus::Lost,
|
||||
..Default::default()
|
||||
},
|
||||
})),
|
||||
Arc::new(move |data: &mut TestClientData| {
|
||||
if data.is_source_reconnected {
|
||||
data.source_tracked_transaction_status =
|
||||
@@ -980,7 +1113,7 @@ pub(crate) mod tests {
|
||||
// their corresponding nonce won't be udpated => reconnect will happen
|
||||
let (exit_sender, exit_receiver) = unbounded();
|
||||
let result = run_loop_test(
|
||||
TestClientData {
|
||||
Arc::new(Mutex::new(TestClientData {
|
||||
source_state: ClientState {
|
||||
best_self: HeaderId(0, 0),
|
||||
best_finalized_self: HeaderId(0, 0),
|
||||
@@ -996,7 +1129,7 @@ pub(crate) mod tests {
|
||||
},
|
||||
target_latest_received_nonce: 0,
|
||||
..Default::default()
|
||||
},
|
||||
})),
|
||||
Arc::new(move |data: &mut TestClientData| {
|
||||
// blocks are produced on every tick
|
||||
data.source_state.best_self =
|
||||
@@ -1054,7 +1187,7 @@ pub(crate) mod tests {
|
||||
fn message_lane_loop_works() {
|
||||
let (exit_sender, exit_receiver) = unbounded();
|
||||
let result = run_loop_test(
|
||||
TestClientData {
|
||||
Arc::new(Mutex::new(TestClientData {
|
||||
source_state: ClientState {
|
||||
best_self: HeaderId(10, 10),
|
||||
best_finalized_self: HeaderId(10, 10),
|
||||
@@ -1070,7 +1203,7 @@ pub(crate) mod tests {
|
||||
},
|
||||
target_latest_received_nonce: 0,
|
||||
..Default::default()
|
||||
},
|
||||
})),
|
||||
Arc::new(|data: &mut TestClientData| {
|
||||
// blocks are produced on every tick
|
||||
data.source_state.best_self =
|
||||
@@ -1133,4 +1266,74 @@ pub(crate) mod tests {
|
||||
assert!(!result.target_to_source_header_requirements.is_empty());
|
||||
assert!(!result.source_to_target_header_requirements.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_lane_loop_works_with_batch_transactions() {
|
||||
let (exit_sender, exit_receiver) = unbounded();
|
||||
let original_data = Arc::new(Mutex::new(TestClientData {
|
||||
source_state: ClientState {
|
||||
best_self: HeaderId(10, 10),
|
||||
best_finalized_self: HeaderId(10, 10),
|
||||
best_finalized_peer_at_best_self: HeaderId(0, 0),
|
||||
actual_best_finalized_peer_at_best_self: HeaderId(0, 0),
|
||||
},
|
||||
source_latest_generated_nonce: 10,
|
||||
target_state: ClientState {
|
||||
best_self: HeaderId(0, 0),
|
||||
best_finalized_self: HeaderId(0, 0),
|
||||
best_finalized_peer_at_best_self: HeaderId(0, 0),
|
||||
actual_best_finalized_peer_at_best_self: HeaderId(0, 0),
|
||||
},
|
||||
target_latest_received_nonce: 0,
|
||||
..Default::default()
|
||||
}));
|
||||
let target_original_data = original_data.clone();
|
||||
let source_original_data = original_data.clone();
|
||||
let result = run_loop_test(
|
||||
original_data,
|
||||
Arc::new(|_| {}),
|
||||
Arc::new(move |data: &mut TestClientData| {
|
||||
if let Some(target_to_source_header_required) =
|
||||
data.target_to_source_header_required.take()
|
||||
{
|
||||
data.target_to_source_batch_transaction =
|
||||
Some(TestConfirmationBatchTransaction {
|
||||
data: source_original_data.clone(),
|
||||
required_header_id: target_to_source_header_required,
|
||||
tx_tracker: TestTransactionTracker::default(),
|
||||
})
|
||||
}
|
||||
}),
|
||||
Arc::new(|_| {}),
|
||||
Arc::new(move |data: &mut TestClientData| {
|
||||
if let Some(source_to_target_header_required) =
|
||||
data.source_to_target_header_required.take()
|
||||
{
|
||||
data.source_to_target_batch_transaction = Some(TestMessagesBatchTransaction {
|
||||
data: target_original_data.clone(),
|
||||
required_header_id: source_to_target_header_required,
|
||||
tx_tracker: TestTransactionTracker::default(),
|
||||
})
|
||||
}
|
||||
|
||||
if data.source_latest_confirmed_received_nonce == 10 {
|
||||
exit_sender.unbounded_send(()).unwrap();
|
||||
}
|
||||
}),
|
||||
exit_receiver.into_future().map(|(_, _)| ()),
|
||||
);
|
||||
|
||||
// there are no strict restrictions on when reward confirmation should come
|
||||
// (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation
|
||||
// depends on the state of both clients)
|
||||
// => we do not check it here
|
||||
assert_eq!(result.submitted_messages_proofs[0].0, 1..=4);
|
||||
assert_eq!(result.submitted_messages_proofs[1].0, 5..=8);
|
||||
assert_eq!(result.submitted_messages_proofs[2].0, 9..=10);
|
||||
assert!(!result.submitted_messages_receiving_proofs.is_empty());
|
||||
|
||||
// check that we have at least once required new source->target or target->source headers
|
||||
assert!(!result.target_to_source_header_requirements.is_empty());
|
||||
assert!(!result.source_to_target_header_requirements.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,9 +171,13 @@ where
|
||||
{
|
||||
type Error = C::Error;
|
||||
type TargetNoncesData = DeliveryRaceTargetNoncesData;
|
||||
type BatchTransaction = C::BatchTransaction;
|
||||
type TransactionTracker = C::TransactionTracker;
|
||||
|
||||
async fn require_source_header(&self, id: SourceHeaderIdOf<P>) {
|
||||
async fn require_source_header(
|
||||
&self,
|
||||
id: SourceHeaderIdOf<P>,
|
||||
) -> Result<Option<C::BatchTransaction>, Self::Error> {
|
||||
self.client.require_source_header_on_target(id).await
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
//! associated data - like messages, lane state, etc) to the target node by
|
||||
//! generating and submitting proof.
|
||||
|
||||
use crate::message_lane_loop::{ClientState, NoncesSubmitArtifacts};
|
||||
use crate::message_lane_loop::{BatchTransaction, ClientState, NoncesSubmitArtifacts};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bp_messages::MessageNonce;
|
||||
@@ -127,12 +127,29 @@ pub trait TargetClient<P: MessageRace> {
|
||||
type Error: std::fmt::Debug + MaybeConnectionError;
|
||||
/// Type of the additional data from the target client, used by the race.
|
||||
type TargetNoncesData: std::fmt::Debug;
|
||||
/// Type of batch transaction that submits finality and proof to the target node.
|
||||
type BatchTransaction: BatchTransaction<
|
||||
P::SourceHeaderId,
|
||||
P::Proof,
|
||||
Self::TransactionTracker,
|
||||
Self::Error,
|
||||
>;
|
||||
/// Transaction tracker to track submitted transactions.
|
||||
type TransactionTracker: TransactionTracker<HeaderId = P::TargetHeaderId>;
|
||||
|
||||
/// Ask headers relay to relay finalized headers up to (and including) given header
|
||||
/// from race source to race target.
|
||||
async fn require_source_header(&self, id: P::SourceHeaderId);
|
||||
///
|
||||
/// The client may return `Some(_)`, which means that nothing has happened yet and
|
||||
/// the caller must generate and append proof to the batch transaction
|
||||
/// to actually send it (along with required header) to the node.
|
||||
///
|
||||
/// If function has returned `None`, it means that the caller now must wait for the
|
||||
/// appearance of the required header `id` at the target client.
|
||||
async fn require_source_header(
|
||||
&self,
|
||||
id: P::SourceHeaderId,
|
||||
) -> Result<Option<Self::BatchTransaction>, Self::Error>;
|
||||
|
||||
/// Return nonces that are known to the target client.
|
||||
async fn nonces(
|
||||
@@ -242,6 +259,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
let mut source_retry_backoff = retry_backoff();
|
||||
let mut source_client_is_online = true;
|
||||
let mut source_nonces_required = false;
|
||||
let mut source_required_header = None;
|
||||
let source_nonces = futures::future::Fuse::terminated();
|
||||
let source_generate_proof = futures::future::Fuse::terminated();
|
||||
let source_go_offline_future = futures::future::Fuse::terminated();
|
||||
@@ -250,6 +268,8 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
let mut target_client_is_online = true;
|
||||
let mut target_best_nonces_required = false;
|
||||
let mut target_finalized_nonces_required = false;
|
||||
let mut target_batch_transaction = None;
|
||||
let target_require_source_header = futures::future::Fuse::terminated();
|
||||
let target_best_nonces = futures::future::Fuse::terminated();
|
||||
let target_finalized_nonces = futures::future::Fuse::terminated();
|
||||
let target_submit_proof = futures::future::Fuse::terminated();
|
||||
@@ -262,6 +282,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
source_generate_proof,
|
||||
source_go_offline_future,
|
||||
race_target_updated,
|
||||
target_require_source_header,
|
||||
target_best_nonces,
|
||||
target_finalized_nonces,
|
||||
target_submit_proof,
|
||||
@@ -326,13 +347,10 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
).fail_if_connection_error(FailedClient::Source)?;
|
||||
|
||||
// ask for more headers if we have nonces to deliver and required headers are missing
|
||||
let required_source_header_id = race_state
|
||||
source_required_header = race_state
|
||||
.best_finalized_source_header_id_at_best_target
|
||||
.as_ref()
|
||||
.and_then(|best|strategy.required_source_header_at_target(best));
|
||||
if let Some(required_source_header_id) = required_source_header_id {
|
||||
race_target.require_source_header(required_source_header_id).await;
|
||||
}
|
||||
.and_then(|best| strategy.required_source_header_at_target(best));
|
||||
},
|
||||
nonces = target_best_nonces => {
|
||||
target_best_nonces_required = false;
|
||||
@@ -378,6 +396,28 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
},
|
||||
|
||||
// proof generation and submission
|
||||
maybe_batch_transaction = target_require_source_header => {
|
||||
source_required_header = None;
|
||||
|
||||
target_client_is_online = process_future_result(
|
||||
maybe_batch_transaction,
|
||||
&mut target_retry_backoff,
|
||||
|maybe_batch_transaction: Option<TC::BatchTransaction>| {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Target {} client has been asked for more {} headers. Batch tx: {:?}",
|
||||
P::target_name(),
|
||||
P::source_name(),
|
||||
maybe_batch_transaction.is_some(),
|
||||
);
|
||||
|
||||
target_batch_transaction = maybe_batch_transaction;
|
||||
},
|
||||
&mut target_go_offline_future,
|
||||
async_std::task::sleep,
|
||||
|| format!("Error asking for source headers at {}", P::target_name()),
|
||||
).fail_if_connection_error(FailedClient::Target)?;
|
||||
},
|
||||
proof = source_generate_proof => {
|
||||
source_client_is_online = process_future_result(
|
||||
proof,
|
||||
@@ -409,6 +449,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
P::target_name(),
|
||||
);
|
||||
|
||||
target_batch_transaction = None;
|
||||
race_state.nonces_to_submit = None;
|
||||
race_state.nonces_submitted = Some(artifacts.nonces);
|
||||
target_tx_tracker.set(artifacts.tx_tracker.wait().fuse());
|
||||
@@ -479,8 +520,23 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
if source_client_is_online {
|
||||
source_client_is_online = false;
|
||||
|
||||
// if we've started to submit batch transaction, let's prioritize it
|
||||
let expected_race_state =
|
||||
if let Some(ref target_batch_transaction) = target_batch_transaction {
|
||||
// when selecting nonces for the batch transaction, we assume that the required
|
||||
// source header is already at the target chain
|
||||
let required_source_header_at_target =
|
||||
target_batch_transaction.required_header_id();
|
||||
let mut expected_race_state = race_state.clone();
|
||||
expected_race_state.best_finalized_source_header_id_at_best_target =
|
||||
Some(required_source_header_at_target);
|
||||
expected_race_state
|
||||
} else {
|
||||
race_state.clone()
|
||||
};
|
||||
|
||||
let nonces_to_deliver =
|
||||
select_nonces_to_deliver(race_state.clone(), &mut strategy).await;
|
||||
select_nonces_to_deliver(expected_race_state, &mut strategy).await;
|
||||
let best_at_source = strategy.best_at_source();
|
||||
|
||||
if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver {
|
||||
@@ -491,6 +547,7 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
nonces_range,
|
||||
at_block,
|
||||
);
|
||||
|
||||
source_generate_proof.set(
|
||||
race_source.generate_proof(at_block, nonces_range, proof_parameters).fuse(),
|
||||
);
|
||||
@@ -518,17 +575,45 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
target_client_is_online = false;
|
||||
|
||||
if let Some((at_block, nonces_range, proof)) = race_state.nonces_to_submit.as_ref() {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Going to submit proof of messages in range {:?} to {} node",
|
||||
nonces_range,
|
||||
P::target_name(),
|
||||
);
|
||||
target_submit_proof.set(
|
||||
race_target
|
||||
.submit_proof(at_block.clone(), nonces_range.clone(), proof.clone())
|
||||
.fuse(),
|
||||
);
|
||||
if let Some(target_batch_transaction) = target_batch_transaction.take() {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Going to submit batch transaction with header {:?} and proof of messages in range {:?} to {} node",
|
||||
target_batch_transaction.required_header_id(),
|
||||
nonces_range,
|
||||
P::target_name(),
|
||||
);
|
||||
|
||||
let nonces = nonces_range.clone();
|
||||
target_submit_proof.set(
|
||||
target_batch_transaction
|
||||
.append_proof_and_send(proof.clone())
|
||||
.map(|result| {
|
||||
result
|
||||
.map(|tx_tracker| NoncesSubmitArtifacts { nonces, tx_tracker })
|
||||
})
|
||||
.left_future()
|
||||
.fuse(),
|
||||
);
|
||||
} else {
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"Going to submit proof of messages in range {:?} to {} node",
|
||||
nonces_range,
|
||||
P::target_name(),
|
||||
);
|
||||
|
||||
target_submit_proof.set(
|
||||
race_target
|
||||
.submit_proof(at_block.clone(), nonces_range.clone(), proof.clone())
|
||||
.right_future()
|
||||
.fuse(),
|
||||
);
|
||||
}
|
||||
} else if let Some(source_required_header) = source_required_header.clone() {
|
||||
log::debug!(target: "bridge", "Going to require {} header {:?} at {}", P::source_name(), source_required_header, P::target_name());
|
||||
target_require_source_header
|
||||
.set(race_target.require_source_header(source_required_header).fuse());
|
||||
} else if target_best_nonces_required {
|
||||
log::debug!(target: "bridge", "Asking {} about best message nonces", P::target_name());
|
||||
let at_block = race_state
|
||||
|
||||
@@ -155,9 +155,13 @@ where
|
||||
{
|
||||
type Error = C::Error;
|
||||
type TargetNoncesData = ();
|
||||
type BatchTransaction = C::BatchTransaction;
|
||||
type TransactionTracker = C::TransactionTracker;
|
||||
|
||||
async fn require_source_header(&self, id: TargetHeaderIdOf<P>) {
|
||||
async fn require_source_header(
|
||||
&self,
|
||||
id: TargetHeaderIdOf<P>,
|
||||
) -> Result<Option<C::BatchTransaction>, Self::Error> {
|
||||
self.client.require_target_header_on_source(id).await
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user