referrerpolicy=no-referrer-when-downgrade

substrate_relay_helper/finality/
initialize.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Initialize Substrate -> Substrate finality bridge.
18//!
19//! Initialization is a transaction that calls `initialize()` function of the
20//! finality pallet (GRANDPA/BEEFY/...). This transaction brings initial header
21//! and authorities set from source to target chain. The finality sync starts
22//! with this header.
23
24use crate::{error::Error, finality_base::engine::Engine};
25use sp_core::Pair;
26
27use bp_runtime::HeaderIdOf;
28use relay_substrate_client::{
29	AccountKeyPairOf, Chain, ChainWithTransactions, Client, Error as SubstrateError,
30	UnsignedTransaction,
31};
32use relay_utils::{TrackedTransactionStatus, TransactionTracker};
33use sp_runtime::traits::Header as HeaderT;
34
35/// Submit headers-bridge initialization transaction.
36pub async fn initialize<
37	E: Engine<SourceChain>,
38	SourceChain: Chain,
39	TargetChain: ChainWithTransactions,
40	F,
41>(
42	source_client: impl Client<SourceChain>,
43	target_client: impl Client<TargetChain>,
44	target_signer: AccountKeyPairOf<TargetChain>,
45	prepare_initialize_transaction: F,
46	dry_run: bool,
47) where
48	F: FnOnce(
49			TargetChain::Nonce,
50			E::InitializationData,
51		) -> Result<UnsignedTransaction<TargetChain>, SubstrateError>
52		+ Send
53		+ 'static,
54	TargetChain::AccountId: From<<TargetChain::AccountKeyPair as Pair>::Public>,
55{
56	let result = do_initialize::<E, _, _, _>(
57		source_client,
58		target_client,
59		target_signer,
60		prepare_initialize_transaction,
61		dry_run,
62	)
63	.await;
64
65	match result {
66		Ok(Some(tx_status)) => match tx_status {
67			TrackedTransactionStatus::Lost => {
68				log::error!(
69					target: "bridge",
70					"Failed to execute {}-headers bridge initialization transaction on {}: {:?}.",
71					SourceChain::NAME,
72					TargetChain::NAME,
73					tx_status
74				)
75			},
76			TrackedTransactionStatus::Finalized(_) => {
77				log::info!(
78					target: "bridge",
79					"Successfully executed {}-headers bridge initialization transaction on {}: {:?}.",
80					SourceChain::NAME,
81					TargetChain::NAME,
82					tx_status
83				)
84			},
85		},
86		Ok(None) => (),
87		Err(err) => log::error!(
88			target: "bridge",
89			"Failed to submit {}-headers bridge initialization transaction to {}: {:?}",
90			SourceChain::NAME,
91			TargetChain::NAME,
92			err,
93		),
94	}
95}
96
97/// Craft and submit initialization transaction, returning any error that may occur.
98async fn do_initialize<
99	E: Engine<SourceChain>,
100	SourceChain: Chain,
101	TargetChain: ChainWithTransactions,
102	F,
103>(
104	source_client: impl Client<SourceChain>,
105	target_client: impl Client<TargetChain>,
106	target_signer: AccountKeyPairOf<TargetChain>,
107	prepare_initialize_transaction: F,
108	dry_run: bool,
109) -> Result<
110	Option<TrackedTransactionStatus<HeaderIdOf<TargetChain>>>,
111	Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>,
112>
113where
114	F: FnOnce(
115			TargetChain::Nonce,
116			E::InitializationData,
117		) -> Result<UnsignedTransaction<TargetChain>, SubstrateError>
118		+ Send
119		+ 'static,
120	TargetChain::AccountId: From<<TargetChain::AccountKeyPair as Pair>::Public>,
121{
122	let is_initialized = E::is_initialized(&target_client)
123		.await
124		.map_err(|e| Error::IsInitializedRetrieve(SourceChain::NAME, TargetChain::NAME, e))?;
125	if is_initialized {
126		log::info!(
127			target: "bridge",
128			"{}-headers bridge at {} is already initialized. Skipping",
129			SourceChain::NAME,
130			TargetChain::NAME,
131		);
132		if !dry_run {
133			return Ok(None)
134		}
135	}
136
137	let initialization_data = E::prepare_initialization_data(source_client).await?;
138	log::info!(
139		target: "bridge",
140		"Prepared initialization data for {}-headers bridge at {}: {:?}",
141		SourceChain::NAME,
142		TargetChain::NAME,
143		initialization_data,
144	);
145
146	let tx_status = target_client
147		.submit_and_watch_signed_extrinsic(&target_signer, move |_, transaction_nonce| {
148			let tx = prepare_initialize_transaction(transaction_nonce, initialization_data);
149			if dry_run {
150				Err(SubstrateError::Custom(
151					"Not submitting extrinsic in `dry-run` mode!".to_string(),
152				))
153			} else {
154				tx
155			}
156		})
157		.await
158		.map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?
159		.wait()
160		.await;
161
162	Ok(Some(tx_status))
163}