Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! On-demand Substrate -> Substrate parachain finality relay.
use crate::{
messages_source::best_finalized_peer_header_at_self,
on_demand::OnDemandRelay,
parachains::{
source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter,
SubstrateParachainsPipeline,
},
TransactionParams,
};
use async_std::{
channel::{unbounded, Receiver, Sender},
sync::{Arc, Mutex},
};
use async_trait::async_trait;
use bp_polkadot_core::parachains::ParaHash;
use futures::{select, FutureExt};
use num_traits::Zero;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use parachains_relay::parachains_loop::{ParachainSyncParams, TargetClient};
use relay_substrate_client::{
Svyatoslav Nikolsky
committed
AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf,
TransactionSignScheme,
};
use relay_utils::{
metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, HeaderId,
};
use sp_runtime::traits::Header as HeaderT;
Svyatoslav Nikolsky
committed
use std::fmt::Debug;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/// On-demand Substrate <-> Substrate parachain finality relay.
///
/// This relay may be requested to sync more parachain headers, whenever some other relay
/// (e.g. messages relay) needs it to continue its regular work. When enough parachain headers
/// are relayed, on-demand stops syncing headers.
#[derive(Clone)]
pub struct OnDemandParachainsRelay<SourceParachain: Chain> {
/// Relay task name.
relay_task_name: String,
/// Channel used to communicate with background task and ask for relay of parachain heads.
required_header_number_sender: Sender<BlockNumberOf<SourceParachain>>,
}
impl<SourceParachain: Chain> OnDemandParachainsRelay<SourceParachain> {
/// Create new on-demand parachains relay.
///
/// Note that the argument is the source relay chain client, not the parachain client.
/// That's because parachain finality is determined by the relay chain and we don't
/// need to connect to the parachain itself here.
pub fn new<P: SubstrateParachainsPipeline<SourceParachain = SourceParachain>>(
source_relay_client: Client<P::SourceRelayChain>,
target_client: Client<P::TargetChain>,
target_transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
on_demand_source_relay_to_target_headers: Arc<
dyn OnDemandRelay<BlockNumberOf<P::SourceRelayChain>>,
>,
) -> Self
where
P::SourceParachain: Chain<Hash = ParaHash>,
P::SourceRelayChain:
Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher>,
AccountIdOf<P::TargetChain>:
From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
let (required_header_number_sender, required_header_number_receiver) = unbounded();
let this = OnDemandParachainsRelay {
relay_task_name: on_demand_parachains_relay_name::<SourceParachain, P::TargetChain>(),
required_header_number_sender,
};
async_std::task::spawn(async move {
background_task::<P>(
source_relay_client,
target_client,
target_transaction_params,
on_demand_source_relay_to_target_headers,
required_header_number_receiver,
)
.await;
});
this
}
}
#[async_trait]
impl<SourceParachain> OnDemandRelay<BlockNumberOf<SourceParachain>>
for OnDemandParachainsRelay<SourceParachain>
where
SourceParachain: Chain,
{
async fn require_more_headers(&self, required_header: BlockNumberOf<SourceParachain>) {
if let Err(e) = self.required_header_number_sender.send(required_header).await {
log::trace!(
target: "bridge",
"Failed to request {} header {:?} in {:?}: {:?}",
SourceParachain::NAME,
required_header,
self.relay_task_name,
e,
);
}
}
}
/// Background task that is responsible for starting parachain headers relay.
async fn background_task<P: SubstrateParachainsPipeline>(
source_relay_client: Client<P::SourceRelayChain>,
target_client: Client<P::TargetChain>,
target_transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>,
on_demand_source_relay_to_target_headers: Arc<
dyn OnDemandRelay<BlockNumberOf<P::SourceRelayChain>>,
>,
required_parachain_header_number_receiver: Receiver<BlockNumberOf<P::SourceParachain>>,
) where
P::SourceParachain: Chain<Hash = ParaHash>,
P::SourceRelayChain:
Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher>,
AccountIdOf<P::TargetChain>:
From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>,
P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>,
{
let relay_task_name = on_demand_parachains_relay_name::<P::SourceParachain, P::TargetChain>();
let target_transactions_mortality = target_transaction_params.mortality;
let mut relay_state = RelayState::Idle;
let mut required_parachain_header_number = Zero::zero();
Svyatoslav Nikolsky
committed
let required_para_header_number_ref = Arc::new(Mutex::new(None));
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
let mut restart_relay = true;
let parachains_relay_task = futures::future::Fuse::terminated();
futures::pin_mut!(parachains_relay_task);
let mut parachains_source = ParachainsSource::<P>::new(
source_relay_client.clone(),
Some(required_para_header_number_ref.clone()),
);
let mut parachains_target =
ParachainsTarget::<P>::new(target_client.clone(), target_transaction_params.clone());
loop {
select! {
new_required_parachain_header_number = required_parachain_header_number_receiver.recv().fuse() => {
let new_required_parachain_header_number = match new_required_parachain_header_number {
Ok(new_required_parachain_header_number) => new_required_parachain_header_number,
Err(e) => {
log::error!(
target: "bridge",
"Background task of {} has exited with error: {:?}",
relay_task_name,
e,
);
return;
},
};
// keep in mind that we are not updating `required_para_header_number_ref` here, because
// then we'll be submitting all previous headers as well (while required relay headers are
// delivered) and we want to avoid that (to reduce cost)
required_parachain_header_number = std::cmp::max(
required_parachain_header_number,
new_required_parachain_header_number,
);
},
_ = parachains_relay_task => {
// this should never happen in practice given the current code
restart_relay = true;
},
}
// the workflow of the on-demand parachains relay is:
//
// 1) message relay (or any other dependent relay) sees new message at parachain header
Svyatoslav Nikolsky
committed
// `PH`;
//
// 2) it sees that the target chain does not know `PH`;
//
// 3) it asks on-demand parachains relay to relay `PH` to the target chain;
//
// Phase#1: relaying relay chain header
//
// 4) on-demand parachains relay waits for GRANDPA-finalized block of the source relay chain
// `RH` that is storing `PH` or its descendant. Let it be `PH'`;
// 5) it asks on-demand headers relay to relay `RH` to the target chain;
// 6) it waits until `RH` (or its descendant) is relayed to the target chain;
//
// Phase#2: relaying parachain header
//
// 7) on-demand parachains relay sets `ParachainsSource::maximal_header_number` to the
Svyatoslav Nikolsky
committed
// `PH'.number()`.
// 8) parachains finality relay sees that the parachain head has been
// updated and relays `PH'` to the target chain.
// select headers to relay
let relay_data = read_relay_data(
¶chains_source,
¶chains_target,
required_parachain_header_number,
)
.await;
match relay_data {
Svyatoslav Nikolsky
committed
Ok(relay_data) => {
let prev_relay_state = relay_state;
Svyatoslav Nikolsky
committed
relay_state = select_headers_to_relay(&relay_data, relay_state);
log::trace!(
target: "bridge",
"Selected new relay state in {}: {:?} using old state {:?} and data {:?}",
relay_task_name,
relay_state,
prev_relay_state,
relay_data,
);
},
Err(failed_client) => {
relay_utils::relay_loop::reconnect_failed_client(
failed_client,
relay_utils::relay_loop::RECONNECT_DELAY,
&mut parachains_source,
&mut parachains_target,
)
.await;
continue
},
}
// we have selected our new 'state' => let's notify our source clients about our new
// requirements
match relay_state {
RelayState::Idle => (),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(required_relay_header) => {
on_demand_source_relay_to_target_headers
.require_more_headers(required_relay_header)
.await;
},
RelayState::RelayingParaHeader(required_para_header) => {
Svyatoslav Nikolsky
committed
*required_para_header_number_ref.lock().await = Some(required_para_header);
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
},
}
// start/restart relay
if restart_relay {
let stall_timeout = relay_substrate_client::transaction_stall_timeout(
target_transactions_mortality,
P::TargetChain::AVERAGE_BLOCK_INTERVAL,
crate::STALL_TIMEOUT,
);
log::info!(
target: "bridge",
"Starting {} relay\n\t\
Tx mortality: {:?} (~{}m)\n\t\
Stall timeout: {:?}",
relay_task_name,
target_transactions_mortality,
stall_timeout.as_secs_f64() / 60.0f64,
stall_timeout,
);
parachains_relay_task.set(
parachains_relay::parachains_loop::run(
parachains_source.clone(),
parachains_target.clone(),
ParachainSyncParams {
parachains: vec![P::SOURCE_PARACHAIN_PARA_ID.into()],
stall_timeout: std::time::Duration::from_secs(60),
strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any,
},
MetricsParams::disabled(),
futures::future::pending(),
)
.fuse(),
);
restart_relay = false;
}
}
}
/// On-demand parachains relay task name.
fn on_demand_parachains_relay_name<SourceChain: Chain, TargetChain: Chain>() -> String {
format!("on-demand-{}-to-{}", SourceChain::NAME, TargetChain::NAME)
}
/// On-demand relay state.
#[derive(Clone, Copy, Debug, PartialEq)]
Svyatoslav Nikolsky
committed
enum RelayState<ParaHash, ParaNumber, RelayNumber> {
/// On-demand relay is not doing anything.
Idle,
/// Relaying given relay header to relay given parachain header later.
Svyatoslav Nikolsky
committed
RelayingRelayHeader(RelayNumber),
/// Relaying given parachain header.
Svyatoslav Nikolsky
committed
RelayingParaHeader(HeaderId<ParaHash, ParaNumber>),
}
/// Data gathered from source and target clients, used by on-demand relay.
#[derive(Debug)]
Svyatoslav Nikolsky
committed
struct RelayData<ParaHash, ParaNumber, RelayNumber> {
/// Parachain header number that is required at the target chain.
Svyatoslav Nikolsky
committed
pub required_para_header: ParaNumber,
/// Parachain header number, known to the target chain.
Svyatoslav Nikolsky
committed
pub para_header_at_target: ParaNumber,
/// Parachain header id, known to the source (relay) chain.
pub para_header_at_source: Option<HeaderId<ParaHash, ParaNumber>>,
/// Parachain header, that is available at the source relay chain at `relay_header_at_target`
/// block.
pub para_header_at_relay_header_at_target: Option<HeaderId<ParaHash, ParaNumber>>,
/// Relay header number at the source chain.
Svyatoslav Nikolsky
committed
pub relay_header_at_source: RelayNumber,
/// Relay header number at the target chain.
Svyatoslav Nikolsky
committed
pub relay_header_at_target: RelayNumber,
}
/// Read required data from source and target clients.
Svyatoslav Nikolsky
committed
async fn read_relay_data<P: SubstrateParachainsPipeline>(
source: &ParachainsSource<P>,
target: &ParachainsTarget<P>,
required_header_number: BlockNumberOf<P::SourceParachain>,
Svyatoslav Nikolsky
committed
) -> Result<
RelayData<
HashOf<P::SourceParachain>,
BlockNumberOf<P::SourceParachain>,
Svyatoslav Nikolsky
committed
BlockNumberOf<P::SourceRelayChain>,
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
>,
FailedClient,
>
where
ParachainsTarget<P>:
TargetClient<ParachainsPipelineAdapter<P>> + RelayClient<Error = SubstrateError>,
{
let map_target_err = |e| {
log::error!(
target: "bridge",
"Failed to read {} relay data from {} client: {:?}",
on_demand_parachains_relay_name::<P::SourceParachain, P::TargetChain>(),
P::TargetChain::NAME,
e,
);
FailedClient::Target
};
let map_source_err = |e| {
log::error!(
target: "bridge",
"Failed to read {} relay data from {} client: {:?}",
on_demand_parachains_relay_name::<P::SourceParachain, P::TargetChain>(),
P::SourceRelayChain::NAME,
e,
);
FailedClient::Source
};
let best_target_block_hash = target.best_block().await.map_err(map_target_err)?.1;
let para_header_at_target =
best_finalized_peer_header_at_self::<P::TargetChain, P::SourceParachain>(
target.client(),
best_target_block_hash,
P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD,
)
.await
.map_err(map_target_err)?
.0;
let best_finalized_relay_header =
source.client().best_finalized_header().await.map_err(map_source_err)?;
let best_finalized_relay_block_id =
HeaderId(*best_finalized_relay_header.number(), best_finalized_relay_header.hash());
let para_header_at_source = source
.on_chain_parachain_header(
best_finalized_relay_block_id,
P::SOURCE_PARACHAIN_PARA_ID.into(),
)
.await
.map_err(map_source_err)?
Svyatoslav Nikolsky
committed
.map(|h| HeaderId(*h.number(), h.hash()));
let relay_header_at_source = best_finalized_relay_block_id.0;
let relay_header_at_target =
best_finalized_peer_header_at_self::<P::TargetChain, P::SourceRelayChain>(
target.client(),
best_target_block_hash,
P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD,
)
.await
Svyatoslav Nikolsky
committed
.map_err(map_target_err)?;
let para_header_at_relay_header_at_target = source
.on_chain_parachain_header(relay_header_at_target, P::SOURCE_PARACHAIN_PARA_ID.into())
.await
.map_err(map_source_err)?
.map(|h| HeaderId(*h.number(), h.hash()));
Ok(RelayData {
required_para_header: required_header_number,
para_header_at_target,
para_header_at_source,
relay_header_at_source,
Svyatoslav Nikolsky
committed
relay_header_at_target: relay_header_at_target.0,
para_header_at_relay_header_at_target,
})
}
/// Select relay and parachain headers that need to be relayed.
Svyatoslav Nikolsky
committed
fn select_headers_to_relay<ParaHash, ParaNumber, RelayNumber>(
data: &RelayData<ParaHash, ParaNumber, RelayNumber>,
mut state: RelayState<ParaHash, ParaNumber, RelayNumber>,
) -> RelayState<ParaHash, ParaNumber, RelayNumber>
Svyatoslav Nikolsky
committed
ParaHash: Clone,
ParaNumber: Copy + PartialOrd,
RelayNumber: Copy + Debug + Ord,
{
// this switch is responsible for processing `RelayingRelayHeader` state
match state {
RelayState::Idle | RelayState::RelayingParaHeader(_) => (),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(relay_header_number) => {
if data.relay_header_at_target < relay_header_number {
// required relay header hasn't yet been relayed
return RelayState::RelayingRelayHeader(relay_header_number)
}
// we may switch to `RelayingParaHeader` if parachain head is available
if let Some(para_header_at_relay_header_at_target) =
data.para_header_at_relay_header_at_target.clone()
{
state = RelayState::RelayingParaHeader(para_header_at_relay_header_at_target);
} else {
// otherwise, we'd need to restart (this may happen only if parachain has been
// deregistered)
state = RelayState::Idle;
}
},
}
// this switch is responsible for processing `RelayingParaHeader` state
match state {
RelayState::Idle => (),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(_) => unreachable!("processed by previous match; qed"),
RelayState::RelayingParaHeader(para_header_id) => {
if data.para_header_at_target < para_header_id.0 {
// parachain header hasn't yet been relayed
Svyatoslav Nikolsky
committed
return RelayState::RelayingParaHeader(para_header_id)
}
},
}
// if we have already satisfied our "customer", do nothing
if data.required_para_header <= data.para_header_at_target {
return RelayState::Idle
}
Svyatoslav Nikolsky
committed
// if we haven't read para head from the source, we can't yet do anyhting
let para_header_at_source = match data.para_header_at_source {
Some(ref para_header_at_source) => para_header_at_source.clone(),
None => return RelayState::Idle,
};
// if required header is not available even at the source chain, let's wait
Svyatoslav Nikolsky
committed
if data.required_para_header > para_header_at_source.0 {
return RelayState::Idle
}
// we will always try to sync latest parachain/relay header, even if we've been asked for some
// its ancestor
// we need relay chain header first
if data.relay_header_at_target < data.relay_header_at_source {
Svyatoslav Nikolsky
committed
return RelayState::RelayingRelayHeader(data.relay_header_at_source)
}
// if all relay headers synced, we may start directly with parachain header
Svyatoslav Nikolsky
committed
RelayState::RelayingParaHeader(para_header_at_source)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn relay_waits_for_relay_header_to_be_delivered() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 90,
para_header_at_target: 50,
Svyatoslav Nikolsky
committed
para_header_at_source: Some(HeaderId(110, 110)),
relay_header_at_source: 800,
relay_header_at_target: 700,
Svyatoslav Nikolsky
committed
para_header_at_relay_header_at_target: Some(HeaderId(100, 100)),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(750),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(750),
);
}
#[test]
fn relay_starts_relaying_requested_para_header_after_relay_header_is_delivered() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 90,
para_header_at_target: 50,
Svyatoslav Nikolsky
committed
para_header_at_source: Some(HeaderId(110, 110)),
relay_header_at_source: 800,
relay_header_at_target: 750,
Svyatoslav Nikolsky
committed
para_header_at_relay_header_at_target: Some(HeaderId(100, 100)),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(750),
Svyatoslav Nikolsky
committed
RelayState::RelayingParaHeader(HeaderId(100, 100)),
);
}
#[test]
fn relay_selects_better_para_header_after_better_relay_header_is_delivered() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 90,
para_header_at_target: 50,
Svyatoslav Nikolsky
committed
para_header_at_source: Some(HeaderId(110, 110)),
relay_header_at_source: 800,
relay_header_at_target: 780,
Svyatoslav Nikolsky
committed
para_header_at_relay_header_at_target: Some(HeaderId(105, 105)),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(750),
Svyatoslav Nikolsky
committed
RelayState::RelayingParaHeader(HeaderId(105, 105)),
);
}
#[test]
fn relay_waits_for_para_header_to_be_delivered() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 90,
para_header_at_target: 50,
Svyatoslav Nikolsky
committed
para_header_at_source: Some(HeaderId(110, 110)),
relay_header_at_source: 800,
Svyatoslav Nikolsky
committed
relay_header_at_target: 780,
para_header_at_relay_header_at_target: Some(HeaderId(105, 105)),
Svyatoslav Nikolsky
committed
RelayState::RelayingParaHeader(HeaderId(105, 105)),
Svyatoslav Nikolsky
committed
RelayState::RelayingParaHeader(HeaderId(105, 105)),
);
}
#[test]
fn relay_stays_idle_if_required_para_header_is_already_delivered() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 90,
para_header_at_target: 105,
para_header_at_source: Some(HeaderId(110, 110)),
relay_header_at_source: 800,
Svyatoslav Nikolsky
committed
relay_header_at_target: 780,
para_header_at_relay_header_at_target: Some(HeaderId(105, 105)),
},
RelayState::Idle,
),
RelayState::Idle,
);
}
#[test]
fn relay_waits_for_required_para_header_to_appear_at_source_1() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 120,
para_header_at_target: 105,
para_header_at_source: None,
relay_header_at_source: 800,
Svyatoslav Nikolsky
committed
relay_header_at_target: 780,
para_header_at_relay_header_at_target: Some(HeaderId(105, 105)),
},
RelayState::Idle,
),
RelayState::Idle,
);
}
#[test]
fn relay_waits_for_required_para_header_to_appear_at_source_2() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 120,
para_header_at_target: 105,
para_header_at_source: Some(HeaderId(110, 110)),
relay_header_at_source: 800,
Svyatoslav Nikolsky
committed
relay_header_at_target: 780,
para_header_at_relay_header_at_target: Some(HeaderId(105, 105)),
},
RelayState::Idle,
),
RelayState::Idle,
);
}
#[test]
fn relay_starts_relaying_relay_header_when_new_para_header_is_requested() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 120,
para_header_at_target: 105,
para_header_at_source: Some(HeaderId(125, 125)),
relay_header_at_source: 800,
Svyatoslav Nikolsky
committed
relay_header_at_target: 780,
para_header_at_relay_header_at_target: Some(HeaderId(105, 105)),
},
RelayState::Idle,
),
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(800),
);
}
#[test]
fn relay_starts_relaying_para_header_when_new_para_header_is_requested() {
assert_eq!(
select_headers_to_relay(
Svyatoslav Nikolsky
committed
&RelayData {
required_para_header: 120,
para_header_at_target: 105,
para_header_at_source: Some(HeaderId(125, 125)),
relay_header_at_source: 800,
relay_header_at_target: 800,
Svyatoslav Nikolsky
committed
para_header_at_relay_header_at_target: Some(HeaderId(125, 125)),
},
RelayState::Idle,
),
Svyatoslav Nikolsky
committed
RelayState::RelayingParaHeader(HeaderId(125, 125)),
);
}
#[test]
Svyatoslav Nikolsky
committed
fn relay_goes_idle_when_parachain_is_deregistered() {
assert_eq!(
select_headers_to_relay::<i32, _, _>(
&RelayData {
required_para_header: 120,
para_header_at_target: 105,
para_header_at_source: None,
relay_header_at_source: 800,
relay_header_at_target: 800,
para_header_at_relay_header_at_target: None,
Svyatoslav Nikolsky
committed
RelayState::RelayingRelayHeader(800),
),
RelayState::Idle,