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