referrerpolicy=no-referrer-when-downgrade

snowbridge_pallet_system/
migration.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
3//! Governance API for controlling the Ethereum side of the bridge
4use super::*;
5use frame_support::{
6	migrations::VersionedMigration,
7	pallet_prelude::*,
8	traits::{OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade},
9	weights::Weight,
10};
11use sp_std::marker::PhantomData;
12
13#[cfg(feature = "try-runtime")]
14use sp_runtime::TryRuntimeError;
15
16const LOG_TARGET: &str = "ethereum_system::migration";
17
18/// The in-code storage version.
19pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
20
21pub mod v0 {
22	use super::*;
23
24	pub struct InitializeOnUpgrade<T, BridgeHubParaId, AssetHubParaId>(
25		PhantomData<(T, BridgeHubParaId, AssetHubParaId)>,
26	);
27
28	impl<T, BridgeHubParaId, AssetHubParaId> OnRuntimeUpgrade
29		for InitializeOnUpgrade<T, BridgeHubParaId, AssetHubParaId>
30	where
31		T: Config,
32		BridgeHubParaId: Get<u32>,
33		AssetHubParaId: Get<u32>,
34	{
35		fn on_runtime_upgrade() -> Weight {
36			if !Pallet::<T>::is_initialized() {
37				Pallet::<T>::initialize(
38					BridgeHubParaId::get().into(),
39					AssetHubParaId::get().into(),
40				)
41				.expect("infallible; qed");
42				tracing::info!(
43					target: LOG_TARGET,
44					"Ethereum system initialized."
45				);
46				T::DbWeight::get().reads_writes(2, 5)
47			} else {
48				tracing::info!(
49					target: LOG_TARGET,
50					"Ethereum system already initialized. Skipping."
51				);
52				T::DbWeight::get().reads(2)
53			}
54		}
55
56		#[cfg(feature = "try-runtime")]
57		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
58			if !Pallet::<T>::is_initialized() {
59				tracing::info!(
60					target: LOG_TARGET,
61					"Agents and channels not initialized. Initialization will run."
62				);
63			} else {
64				tracing::info!(
65					target: LOG_TARGET,
66					"Agents and channels are initialized. Initialization will not run."
67				);
68			}
69			Ok(vec![])
70		}
71
72		#[cfg(feature = "try-runtime")]
73		fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
74			frame_support::ensure!(
75				Pallet::<T>::is_initialized(),
76				"Agents and channels were not initialized."
77			);
78			Ok(())
79		}
80	}
81}
82
83pub mod v1 {
84	use super::*;
85
86	#[cfg(feature = "try-runtime")]
87	use sp_core::U256;
88
89	/// Descreases the fee per gas.
90	pub struct FeePerGasMigration<T>(PhantomData<T>);
91
92	#[cfg(feature = "try-runtime")]
93	impl<T> FeePerGasMigration<T>
94	where
95		T: Config,
96	{
97		/// Calculate the fee required to pay for gas on Ethereum.
98		fn calculate_remote_fee_v1(params: &PricingParametersOf<T>) -> U256 {
99			use snowbridge_outbound_queue_primitives::v1::{
100				AgentExecuteCommand, Command, ConstantGasMeter, GasMeter,
101			};
102			let command = Command::AgentExecute {
103				agent_id: H256::zero(),
104				command: AgentExecuteCommand::TransferToken {
105					token: H160::zero(),
106					recipient: H160::zero(),
107					amount: 0,
108				},
109			};
110			let gas_used_at_most = ConstantGasMeter::maximum_gas_used_at_most(&command);
111			params
112				.fee_per_gas
113				.saturating_mul(gas_used_at_most.into())
114				.saturating_add(params.rewards.remote)
115		}
116
117		/// Calculate the fee required to pay for gas on Ethereum.
118		fn calculate_remote_fee_v2(params: &PricingParametersOf<T>) -> U256 {
119			use snowbridge_outbound_queue_primitives::v2::{Command, ConstantGasMeter, GasMeter};
120			let command = Command::UnlockNativeToken {
121				token: H160::zero(),
122				recipient: H160::zero(),
123				amount: 0,
124			};
125			let gas_used_at_most = ConstantGasMeter::maximum_dispatch_gas_used_at_most(&command);
126			params
127				.fee_per_gas
128				.saturating_mul(gas_used_at_most.into())
129				.saturating_add(params.rewards.remote)
130		}
131	}
132
133	/// The percentage gas increase. We must adjust the fee per gas by this percentage.
134	const GAS_INCREASE_PERCENTAGE: u64 = 70;
135
136	impl<T> UncheckedOnRuntimeUpgrade for FeePerGasMigration<T>
137	where
138		T: Config,
139	{
140		fn on_runtime_upgrade() -> Weight {
141			let mut params = Pallet::<T>::parameters();
142
143			let old_fee_per_gas = params.fee_per_gas;
144
145			// Fee per gas can be set based on a percentage in order to keep the remote fee the
146			// same.
147			params.fee_per_gas = params.fee_per_gas * GAS_INCREASE_PERCENTAGE / 100;
148
149			tracing::info!(
150				target: LOG_TARGET,
151				from=?old_fee_per_gas,
152				to=?params.fee_per_gas,
153				"Fee per gas migrated."
154			);
155
156			PricingParameters::<T>::put(params);
157			T::DbWeight::get().reads_writes(1, 1)
158		}
159
160		#[cfg(feature = "try-runtime")]
161		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
162			use codec::Encode;
163
164			let params = Pallet::<T>::parameters();
165			let remote_fee_v1 = Self::calculate_remote_fee_v1(&params);
166			let remote_fee_v2 = Self::calculate_remote_fee_v2(&params);
167
168			tracing::info!(
169				target: LOG_TARGET,
170				pricing_parameters=?params, ?remote_fee_v1, ?remote_fee_v2,
171				"Pre fee per gas migration"
172			);
173			Ok((params, remote_fee_v1, remote_fee_v2).encode())
174		}
175
176		#[cfg(feature = "try-runtime")]
177		fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
178			use codec::Decode;
179
180			let (old_params, old_remote_fee_v1, old_remote_fee_v2): (
181				PricingParametersOf<T>,
182				U256,
183				U256,
184			) = Decode::decode(&mut &state[..]).unwrap();
185
186			let params = Pallet::<T>::parameters();
187			ensure!(old_params.exchange_rate == params.exchange_rate, "Exchange rate unchanged.");
188			ensure!(old_params.rewards == params.rewards, "Rewards unchanged.");
189			ensure!(
190				(old_params.fee_per_gas * GAS_INCREASE_PERCENTAGE / 100) == params.fee_per_gas,
191				"Fee per gas decreased."
192			);
193			ensure!(old_params.multiplier == params.multiplier, "Multiplier unchanged.");
194
195			let remote_fee_v1 = Self::calculate_remote_fee_v1(&params);
196			let remote_fee_v2 = Self::calculate_remote_fee_v2(&params);
197			ensure!(
198				remote_fee_v1 <= old_remote_fee_v1,
199				"The v1 remote fee can cover the cost of the previous fee."
200			);
201			ensure!(
202				remote_fee_v2 <= old_remote_fee_v2,
203				"The v2 remote fee can cover the cost of the previous fee."
204			);
205
206			tracing::info!(
207				target: LOG_TARGET,
208				pricing_parameters=?params, ?remote_fee_v1, ?remote_fee_v2,
209				"Post fee per gas migration"
210			);
211			Ok(())
212		}
213	}
214}
215
216/// Run the migration of the gas price and increment the pallet version so it cannot be re-run.
217pub type FeePerGasMigrationV0ToV1<T> = VersionedMigration<
218	0,
219	1,
220	v1::FeePerGasMigration<T>,
221	Pallet<T>,
222	<T as frame_system::Config>::DbWeight,
223>;