From 8c22145db7e79ddc269c77ec6ba5810aa1682032 Mon Sep 17 00:00:00 2001 From: kylezs Date: Wed, 20 Sep 2023 16:18:41 +0200 Subject: [PATCH 01/19] fix: SC recycles based on external chain block --- api/bin/chainflip-broker-api/src/main.rs | 2 - api/bin/chainflip-cli/src/main.rs | 3 +- api/lib/src/lib.rs | 7 +- api/lib/src/queries.rs | 58 +++--- engine/src/witness/btc.rs | 5 + .../cf-integration-tests/src/mock_runtime.rs | 4 +- state-chain/node/src/chain_spec.rs | 9 +- state-chain/node/src/chain_spec/common.rs | 1 - .../cf-ingress-egress/src/benchmarking.rs | 20 +- .../pallets/cf-ingress-egress/src/lib.rs | 128 +++++++------ .../pallets/cf-ingress-egress/src/mock.rs | 34 ++-- .../pallets/cf-ingress-egress/src/tests.rs | 175 +++++++++--------- state-chain/pallets/cf-lp/src/benchmarking.rs | 34 +--- state-chain/pallets/cf-lp/src/lib.rs | 95 +--------- state-chain/pallets/cf-lp/src/tests.rs | 86 +-------- .../pallets/cf-swapping/src/benchmarking.rs | 35 ---- state-chain/pallets/cf-swapping/src/lib.rs | 78 +------- state-chain/pallets/cf-swapping/src/mock.rs | 8 +- state-chain/pallets/cf-swapping/src/tests.rs | 85 +-------- .../pallets/cf-swapping/src/weights.rs | 17 -- state-chain/runtime/src/chainflip.rs | 16 -- state-chain/traits/src/lib.rs | 5 - .../traits/src/mocks/deposit_handler.rs | 25 --- 23 files changed, 256 insertions(+), 674 deletions(-) diff --git a/api/bin/chainflip-broker-api/src/main.rs b/api/bin/chainflip-broker-api/src/main.rs index b8dfd3eca9..291d039fd2 100644 --- a/api/bin/chainflip-broker-api/src/main.rs +++ b/api/bin/chainflip-broker-api/src/main.rs @@ -24,7 +24,6 @@ use tracing::log; #[derive(Serialize, Deserialize, Clone)] pub struct BrokerSwapDepositAddress { pub address: String, - pub expiry_block: BlockNumber, pub issued_block: BlockNumber, pub channel_id: ChannelId, } @@ -33,7 +32,6 @@ impl From for BrokerSwapDepositAddress { fn from(value: chainflip_api::SwapDepositAddress) -> Self { Self { address: value.address, - expiry_block: value.expiry_block, issued_block: value.issued_block, channel_id: value.channel_id, } diff --git a/api/bin/chainflip-cli/src/main.rs b/api/bin/chainflip-cli/src/main.rs index 2f423a1a1c..47de1818c7 100644 --- a/api/bin/chainflip-cli/src/main.rs +++ b/api/bin/chainflip-cli/src/main.rs @@ -57,7 +57,7 @@ async fn run_cli() -> Result<()> { let api = StateChainApi::connect(scope, cli_settings.state_chain).await?; match command_line_opts.cmd { Broker(BrokerSubcommands::RequestSwapDepositAddress(params)) => { - let SwapDepositAddress { address, expiry_block, .. } = api + let SwapDepositAddress { address, .. } = api .broker_api() .request_swap_deposit_address( params.source_asset, @@ -71,7 +71,6 @@ async fn run_cli() -> Result<()> { ) .await?; println!("Deposit Address: {address}"); - println!("Address expires at block {expiry_block}"); }, LiquidityProvider( LiquidityProviderSubcommands::RequestLiquidityDepositAddress { asset }, diff --git a/api/lib/src/lib.rs b/api/lib/src/lib.rs index 860c88414d..519f2d3d98 100644 --- a/api/lib/src/lib.rs +++ b/api/lib/src/lib.rs @@ -320,7 +320,6 @@ pub trait GovernanceApi: SignedExtrinsicApi { pub struct SwapDepositAddress { pub address: String, - pub expiry_block: state_chain_runtime::BlockNumber, pub issued_block: state_chain_runtime::BlockNumber, pub channel_id: ChannelId, } @@ -349,10 +348,7 @@ pub trait BrokerApi: SignedExtrinsicApi { if let Some(state_chain_runtime::RuntimeEvent::Swapping( pallet_cf_swapping::Event::SwapDepositAddressReady { - deposit_address, - expiry_block, - channel_id, - .. + deposit_address, channel_id, .. }, )) = events.iter().find(|event| { matches!( @@ -364,7 +360,6 @@ pub trait BrokerApi: SignedExtrinsicApi { }) { Ok(SwapDepositAddress { address: deposit_address.to_string(), - expiry_block: *expiry_block, issued_block: header.number, channel_id: *channel_id, }) diff --git a/api/lib/src/queries.rs b/api/lib/src/queries.rs index eacd1e5a6f..2da798e592 100644 --- a/api/lib/src/queries.rs +++ b/api/lib/src/queries.rs @@ -4,6 +4,7 @@ use cf_primitives::{chains::assets::any, AssetAmount}; use chainflip_engine::state_chain_observer::client::{ chain_api::ChainApi, storage_api::StorageApi, }; +use pallet_cf_ingress_egress::DepositChannelDetails; use serde::Deserialize; use state_chain_runtime::PalletInstanceAlias; use std::{collections::BTreeMap, sync::Arc}; @@ -15,7 +16,6 @@ pub struct SwapChannelInfo { deposit_address: ::Humanreadable, source_asset: any::Asset, destination_asset: any::Asset, - expiry_block: state_chain_runtime::BlockNumber, } pub struct QueryApi { @@ -52,47 +52,33 @@ impl QueryApi { let block_hash = block_hash.unwrap_or_else(|| self.state_chain_client.latest_finalized_hash()); - let (channel_details, channel_actions, network_environment) = tokio::try_join!( - self.state_chain_client - .storage_map::, Vec<_>>(block_hash) - .map(|result| { - result.map(|channels| channels.into_iter().collect::>()) - }), - self.state_chain_client.storage_map::, Vec<_>>(block_hash,), - self.state_chain_client - .storage_value::>(block_hash), - )?; + >, Vec<_>>(block_hash) + .map(|result| { + result.map(|channels| channels.into_iter().collect::>()) + }), + self.state_chain_client + .storage_value::>( + block_hash + ), + )?; - Ok(channel_actions + Ok(channel_details .iter() - .filter_map(|(address, action)| { - match action { - pallet_cf_ingress_egress::ChannelAction::Swap { destination_asset, .. } | - pallet_cf_ingress_egress::ChannelAction::CcmTransfer { - destination_asset, - .. - } => Some(destination_asset), - _ => None, - } - .and_then(|destination_asset| { - channel_details.get(address).map(|details| { - (destination_asset, details.deposit_channel.clone(), details.expires_at) - }) - }) - .map(|(&destination_asset, deposit_channel, expiry)| SwapChannelInfo { + .filter_map(|(_, DepositChannelDetails { action, deposit_channel, .. })| match action { + pallet_cf_ingress_egress::ChannelAction::Swap { destination_asset, .. } | + pallet_cf_ingress_egress::ChannelAction::CcmTransfer { + destination_asset, .. + } => Some(SwapChannelInfo { deposit_address: deposit_channel.address.to_humanreadable(network_environment), source_asset: deposit_channel.asset.into(), - destination_asset, - expiry_block: expiry, - }) + destination_asset: *destination_asset, + }), + _ => None, }) .collect::>()) } diff --git a/engine/src/witness/btc.rs b/engine/src/witness/btc.rs index d78511eca5..97c2827a07 100644 --- a/engine/src/witness/btc.rs +++ b/engine/src/witness/btc.rs @@ -196,6 +196,8 @@ mod tests { btc::{deposit_address::DepositAddress, ScriptPubkey}, DepositChannel, }; + use pallet_cf_ingress_egress::ChannelAction; + use sp_runtime::AccountId32; fn fake_transaction(tx_outs: Vec) -> Transaction { Transaction { @@ -218,6 +220,9 @@ mod tests { asset: btc::Asset::Btc, state: DepositAddress::new([0; 32], 1), }, + action: ChannelAction::::LiquidityProvision { + lp_account: AccountId32::new([0xab; 32]), + }, } } diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 7ae8a3f3bc..bee02a5d54 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -212,9 +212,11 @@ impl ExtBuilder { environment: Default::default(), liquidity_pools: Default::default(), swapping: Default::default(), - liquidity_provider: Default::default(), system: Default::default(), transaction_payment: Default::default(), + bitcoin_ingress_egress: Default::default(), + polkadot_ingress_egress: Default::default(), + ethereum_ingress_egress: Default::default(), }) } } diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index c900e443ef..b698fba6e2 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -267,7 +267,6 @@ pub fn cf_development_config() -> Result { common::PENALTIES.to_vec(), common::KEYGEN_CEREMONY_TIMEOUT_BLOCKS, common::THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, - common::SWAP_TTL, common::MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, ) @@ -390,7 +389,6 @@ macro_rules! network_spec { PENALTIES.to_vec(), KEYGEN_CEREMONY_TIMEOUT_BLOCKS, THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, - SWAP_TTL, MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, ) @@ -445,7 +443,6 @@ fn testnet_genesis( penalties: Vec<(Offence, (i32, BlockNumber))>, keygen_ceremony_timeout_blocks: BlockNumber, threshold_signature_ceremony_timeout_blocks: BlockNumber, - swap_ttl: BlockNumber, minimum_swap_amounts: Vec<(assets::any::Asset, AssetAmount)>, dot_runtime_version: RuntimeVersion, ) -> RuntimeGenesisConfig { @@ -650,8 +647,10 @@ fn testnet_genesis( }, transaction_payment: Default::default(), liquidity_pools: Default::default(), - swapping: SwappingConfig { swap_ttl, minimum_swap_amounts }, - liquidity_provider: Default::default(), + swapping: SwappingConfig { minimum_swap_amounts, _phantom: PhantomData }, + bitcoin_ingress_egress: Default::default(), + ethereum_ingress_egress: Default::default(), + polkadot_ingress_egress: Default::default(), } } diff --git a/state-chain/node/src/chain_spec/common.rs b/state-chain/node/src/chain_spec/common.rs index 0ec7b19c1a..d5d925bb02 100644 --- a/state-chain/node/src/chain_spec/common.rs +++ b/state-chain/node/src/chain_spec/common.rs @@ -40,7 +40,6 @@ pub const PENALTIES: &[(Offence, (i32, BlockNumber))] = &[ (Offence::GrandpaEquivocation, (50, HEARTBEAT_BLOCK_INTERVAL * 5)), ]; -pub const SWAP_TTL: BlockNumber = 2 * HOURS; pub const MINIMUM_SWAP_AMOUNTS: &[(Asset, AssetAmount)] = &[ (Asset::Eth, 580_000_000_000_000u128), // 1usd worth of Eth = 0.00058 * 18 d.p (Asset::Flip, FLIPPERINOS_PER_FLIP), // 1 Flip diff --git a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs index 695a7a7254..da65ff8972 100644 --- a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs +++ b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs @@ -7,7 +7,6 @@ use cf_chains::{ DepositChannel, }; use frame_benchmarking::{account, benchmarks_instance_pallet}; -use frame_system::pallet_prelude::BlockNumberFor; pub(crate) type TargetChainBlockNumber = <>::TargetChain as Chain>::ChainBlockNumber; @@ -27,16 +26,17 @@ benchmarks_instance_pallet! { let deposit_address: <>::TargetChain as Chain>::ChainAccount = BenchmarkValue::benchmark_value(); let source_asset: <>::TargetChain as Chain>::ChainAsset = BenchmarkValue::benchmark_value(); let deposit_amount: <>::TargetChain as Chain>::ChainAmount = BenchmarkValue::benchmark_value(); + let block_number: TargetChainBlockNumber = BenchmarkValue::benchmark_value(); DepositChannelLookup::::insert(&deposit_address, DepositChannelDetails { - opened_at: TargetChainBlockNumber::::benchmark_value(), + opened_at: block_number, + expires_at: block_number, deposit_channel: DepositChannel::generate_new::<>::AddressDerivation>( 1, source_asset, ).unwrap(), - expires_at: BlockNumberFor::::from(1_000u32), - }); - ChannelActions::::insert(&deposit_address, ChannelAction::::LiquidityProvision { - lp_account: account("doogle", 0, 0), + action: ChannelAction::::LiquidityProvision { + lp_account: account("doogle", 0, 0), + }, }); }: { Pallet::::process_single_deposit(deposit_address, source_asset, deposit_amount, BenchmarkValue::benchmark_value(), BenchmarkValue::benchmark_value()).unwrap() @@ -61,13 +61,17 @@ benchmarks_instance_pallet! { let deposit_address = <>::TargetChain as Chain>::ChainAccount::benchmark_value_by_id(a as u8); let deposit_fetch_id = <>::TargetChain as Chain>::DepositFetchId::benchmark_value_by_id(a as u8); let source_asset: <>::TargetChain as Chain>::ChainAsset = BenchmarkValue::benchmark_value(); + let block_number = TargetChainBlockNumber::::benchmark_value(); let mut channel = DepositChannelDetails:: { - opened_at: TargetChainBlockNumber::::benchmark_value(), + opened_at: block_number, + expires_at: block_number, deposit_channel: DepositChannel::generate_new::<>::AddressDerivation>( 1, source_asset, ).unwrap(), - expires_at: BlockNumberFor::::from(1_000u32), + action: ChannelAction::::LiquidityProvision { + lp_account: account("doogle", 0, 0), + }, }; channel.deposit_channel.state.on_fetch_scheduled(); DepositChannelLookup::::insert(deposit_address.clone(), channel); diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 59afc93e29..bd653da6af 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -21,7 +21,6 @@ use cf_chains::{ use cf_primitives::{ Asset, AssetAmount, BasisPoints, ChannelId, EgressCounter, EgressId, ForeignChain, }; -use cf_runtime_utilities::log_or_panic; use cf_traits::{ liquidity::LpBalanceApi, Broadcaster, CcmHandler, Chainflip, DepositApi, DepositHandler, EgressApi, GetBlockHeight, SwapDepositHandler, @@ -106,6 +105,8 @@ pub mod pallet { pub(crate) type TargetChainAccount = <>::TargetChain as Chain>::ChainAccount; pub(crate) type TargetChainAmount = <>::TargetChain as Chain>::ChainAmount; + pub(crate) type TargetChainBlockNumber = + <>::TargetChain as Chain>::ChainBlockNumber; #[derive(Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] pub struct DepositWitness { @@ -123,12 +124,13 @@ pub mod pallet { pub deposit_channel: DepositChannel, /// The block number at which the deposit channel was opened, expressed as a block number /// on the external Chain. - pub opened_at: ::ChainBlockNumber, - /// The block number at which the deposit channel will be closed, expressed as a - /// Chainflip-native block number. - // TODO: We should consider changing this to also be an external block number and expire - // based on external block numbers. See PRO-689. - pub expires_at: BlockNumberFor, + pub opened_at: TargetChainBlockNumber, + /// The last block on the target chain that the witnessing will witness it in. If funds are + /// sent after this block, they will not be witnessed. + pub expires_at: TargetChainBlockNumber, + + /// The action to be taken when the DepositChannel is deposited to. + pub action: ChannelAction, } /// Determines the action to take when a deposit is made to a channel. @@ -196,6 +198,24 @@ pub mod pallet { } } + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub deposit_channel_lifetime: TargetChainBlockNumber, + } + + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { deposit_channel_lifetime: Default::default() } + } + } + + #[pallet::genesis_build] + impl, I: 'static> BuildGenesisConfig for GenesisConfig { + fn build(&self) { + DepositChannelLifetime::::put(self.deposit_channel_lifetime); + } + } + #[pallet::pallet] #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); @@ -259,16 +279,6 @@ pub mod pallet { OptionQuery, >; - /// Stores the channel action against the address - #[pallet::storage] - pub type ChannelActions, I: 'static = ()> = StorageMap< - _, - Twox64Concat, - TargetChainAccount, - ChannelAction, - OptionQuery, - >; - /// Stores the latest channel id used to generate an address. #[pallet::storage] pub type ChannelIdCounter, I: 'static = ()> = @@ -304,6 +314,10 @@ pub mod pallet { pub type MinimumDeposit, I: 'static = ()> = StorageMap<_, Twox64Concat, TargetChainAsset, TargetChainAmount, ValueQuery>; + #[pallet::storage] + pub type DepositChannelLifetime, I: 'static = ()> = + StorageValue<_, TargetChainBlockNumber, ValueQuery>; + /// Stores any failed transfers by the Vault contract. /// Without dealing with the underlying reason for the failure, retrying is unlike to succeed. /// Therefore these calls are stored here, until we can react to the reason for failure and @@ -381,6 +395,33 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { + /// Recycle addresses if we can + fn on_idle(_n: BlockNumberFor, remaining_weight: Weight) -> Weight { + let current_target_chain_height = T::ChainTracking::get_block_height(); + let channel_lifetime = DepositChannelLifetime::::get(); + + for (_, details) in DepositChannelLookup::::iter() { + // We add an extra lifetime of safety. + // The CFEs will stop witnessing the address of this deposit channel at the + // expires_at block number. However, because the CFE uses a safety margin, and here + // we're using teh ChainTracking block number, we don't want to expire + // this too early. Even accounting for the safety margin, there is potential for + // latency, full SC blocks which might block ingress witnesses getting in etc. By + // having this buffer we protect against this probabilistically. + if details.expires_at + channel_lifetime <= current_target_chain_height { + if let Some(state) = details.deposit_channel.state.maybe_recycle() { + DepositChannelPool::::insert( + details.deposit_channel.channel_id, + DepositChannel { state, ..details.deposit_channel }, + ); + } + } + } + + // TODO: return the actual weight + remaining_weight + } + /// Take all scheduled Egress and send them out fn on_finalize(_n: BlockNumberFor) { // Send all fetch/transfer requests as a batch. Revert storage if failed. @@ -403,6 +444,7 @@ pub mod pallet { addresses: Vec>, ) -> DispatchResult { T::EnsureWitnessedAtCurrentEpoch::ensure_origin(origin)?; + for deposit_address in addresses { DepositChannelLookup::::mutate(deposit_address, |deposit_channel_details| { deposit_channel_details @@ -583,6 +625,7 @@ impl, I: 'static> Pallet { let mut addresses = vec![]; for request in batch_to_send { + // We need to pull out what we need above. match request { FetchOrTransfer::::Fetch { asset, @@ -690,6 +733,13 @@ impl, I: 'static> Pallet { let deposit_channel_details = DepositChannelLookup::::get(&deposit_address) .ok_or(Error::::InvalidDepositAddress)?; + // The address should not be used if it's being recycled + ensure!( + DepositChannelPool::::get(&deposit_channel_details.deposit_channel.channel_id) + .is_none(), + Error::::InvalidDepositAddress + ); + ensure!( deposit_channel_details.deposit_channel.asset == asset, Error::::AssetMismatch @@ -716,12 +766,7 @@ impl, I: 'static> Pallet { let channel_id = deposit_channel_details.deposit_channel.channel_id; Self::deposit_event(Event::::DepositFetchesScheduled { channel_id, asset }); - // NB: Don't take here. We should continue witnessing this address - // even after an deposit to it has occurred. - // https://github.com/chainflip-io/chainflip-eth-contracts/pull/226 - match ChannelActions::::get(&deposit_address) - .ok_or(Error::::InvalidDepositAddress)? - { + match deposit_channel_details.action { ChannelAction::LiquidityProvision { lp_account, .. } => T::LpBalance::try_credit_account(&lp_account, asset.into(), amount.into())?, ChannelAction::Swap { @@ -788,8 +833,7 @@ impl, I: 'static> Pallet { /// May re-use an existing deposit address, depending on chain configuration. fn open_channel( source_asset: TargetChainAsset, - channel_action: ChannelAction, - expires_at: BlockNumberFor, + action: ChannelAction, ) -> Result<(ChannelId, TargetChainAccount), DispatchError> { let (deposit_channel, channel_id) = if let Some((channel_id, mut deposit_channel)) = DepositChannelPool::::drain().next() @@ -813,13 +857,15 @@ impl, I: 'static> Pallet { let deposit_address = deposit_channel.address.clone(); - ChannelActions::::insert(&deposit_address, channel_action); + let current_target_chain_height = T::ChainTracking::get_block_height(); + DepositChannelLookup::::insert( &deposit_address, DepositChannelDetails { deposit_channel, - opened_at: T::ChainTracking::get_block_height(), - expires_at, + opened_at: current_target_chain_height, + expires_at: current_target_chain_height + DepositChannelLifetime::::get(), + action, }, ); @@ -882,13 +928,9 @@ impl, I: 'static> DepositApi for Pallet { fn request_liquidity_deposit_address( lp_account: T::AccountId, source_asset: TargetChainAsset, - expiry_block: BlockNumberFor, ) -> Result<(ChannelId, ForeignChainAddress), DispatchError> { - let (channel_id, deposit_address) = Self::open_channel( - source_asset, - ChannelAction::LiquidityProvision { lp_account }, - expiry_block, - )?; + let (channel_id, deposit_address) = + Self::open_channel(source_asset, ChannelAction::LiquidityProvision { lp_account })?; Ok((channel_id, deposit_address.into())) } @@ -901,7 +943,6 @@ impl, I: 'static> DepositApi for Pallet { broker_commission_bps: BasisPoints, broker_id: T::AccountId, channel_metadata: Option, - expiry_block: BlockNumberFor, ) -> Result<(ChannelId, ForeignChainAddress), DispatchError> { let (channel_id, deposit_address) = Self::open_channel( source_asset, @@ -918,25 +959,8 @@ impl, I: 'static> DepositApi for Pallet { broker_id, }, }, - expiry_block, )?; Ok((channel_id, deposit_address.into())) } - - // Note: we expect that the mapping from any instantiable pallet to the instance of this pallet - // is matching to the right chain. Because of that we can ignore the chain parameter. - fn expire_channel(address: TargetChainAccount) { - ChannelActions::::remove(&address); - if let Some(deposit_channel_details) = DepositChannelLookup::::get(&address) { - if let Some(state) = deposit_channel_details.deposit_channel.state.maybe_recycle() { - DepositChannelPool::::insert( - deposit_channel_details.deposit_channel.channel_id, - DepositChannel { state, ..deposit_channel_details.deposit_channel }, - ); - } - } else { - log_or_panic!("Tried to close an unknown channel."); - } - } } diff --git a/state-chain/pallets/cf-ingress-egress/src/mock.rs b/state-chain/pallets/cf-ingress-egress/src/mock.rs index f5fb366b92..e59f979b58 100644 --- a/state-chain/pallets/cf-ingress-egress/src/mock.rs +++ b/state-chain/pallets/cf-ingress-egress/src/mock.rs @@ -1,5 +1,5 @@ pub use crate::{self as pallet_cf_ingress_egress}; -use crate::{DepositBalances, DepositWitness}; +use crate::{DepositBalances, DepositChannelLifetime, DepositWitness}; pub use cf_chains::{ address::{AddressDerivationApi, ForeignChainAddress}, @@ -26,7 +26,6 @@ use cf_traits::{ }; use frame_support::traits::{OriginTrait, UnfilteredDispatchable}; use frame_system as system; -use frame_system::pallet_prelude::BlockNumberFor; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, IdentityLookup, Zero}; @@ -116,8 +115,18 @@ impl crate::Config for Test { pub const ALICE: ::AccountId = 123u64; pub const BROKER: ::AccountId = 456u64; +// TODO: Use genesis config here? // Configure a mock runtime to test the pallet. -impl_test_helpers!(Test); +impl_test_helpers! { + Test, + RuntimeGenesisConfig { + system: Default::default(), + ingress_egress: Default::default(), + }, + || { + DepositChannelLifetime::::put(100); + }, +} type TestChainAccount = <::TargetChain as Chain>::ChainAccount; type TestChainAmount = <::TargetChain as Chain>::ChainAmount; @@ -131,6 +140,7 @@ pub trait RequestAddressAndDeposit { } impl RequestAddressAndDeposit for TestRunner { + /// Request desposit addresses and complete the deposit of funds into those addresses. #[track_caller] fn request_address_and_deposit( self, @@ -168,14 +178,12 @@ pub enum DepositRequest { Liquidity { lp_account: AccountId, asset: TestChainAsset, - expiry_block: BlockNumberFor, }, /// Do a non-ccm swap using a default broker and no fees. SimpleSwap { source_asset: TestChainAsset, destination_asset: TestChainAsset, destination_address: ForeignChainAddress, - expiry_block: BlockNumberFor, }, } @@ -206,19 +214,16 @@ impl RequestAddress for TestExternalities { .iter() .cloned() .map(|request| match request { - DepositRequest::Liquidity { lp_account, asset, expiry_block } => - IngressEgress::request_liquidity_deposit_address( - lp_account, - asset, - expiry_block, - ) - .map(|(id, addr)| (request, id, TestChainAccount::try_from(addr).unwrap())) - .unwrap(), + DepositRequest::Liquidity { lp_account, asset } => + IngressEgress::request_liquidity_deposit_address(lp_account, asset) + .map(|(id, addr)| { + (request, id, TestChainAccount::try_from(addr).unwrap()) + }) + .unwrap(), DepositRequest::SimpleSwap { source_asset, destination_asset, ref destination_address, - expiry_block, } => IngressEgress::request_swap_deposit_address( source_asset, destination_asset.into(), @@ -226,7 +231,6 @@ impl RequestAddress for TestExternalities { Default::default(), BROKER, None, - expiry_block, ) .map(|(channel_id, deposit_address)| { (request, channel_id, TestChainAccount::try_from(deposit_address).unwrap()) diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index fc086e4f3d..cd359594fc 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -1,8 +1,9 @@ use crate::{ mock::*, Call as PalletCall, ChannelAction, ChannelIdCounter, CrossChainMessage, - DepositChannelLookup, DepositChannelPool, DepositWitness, DisabledEgressAssets, Error, - Event as PalletEvent, FailedVaultTransfers, FetchOrTransfer, MinimumDeposit, Pallet, - ScheduledEgressCcm, ScheduledEgressFetchOrTransfer, TargetChainAccount, VaultTransfer, + DepositChannelLifetime, DepositChannelLookup, DepositChannelPool, DepositWitness, + DisabledEgressAssets, Error, Event as PalletEvent, FailedVaultTransfers, FetchOrTransfer, + MinimumDeposit, Pallet, ScheduledEgressCcm, ScheduledEgressFetchOrTransfer, TargetChainAccount, + VaultTransfer, }; use cf_chains::{ address::AddressConverter, evm::EvmFetchId, mocks::MockEthereum, CcmChannelMetadata, @@ -22,6 +23,7 @@ use cf_traits::{ use frame_support::{ assert_noop, assert_ok, traits::{Hooks, OriginTrait}, + weights::Weight, }; use sp_core::H160; @@ -29,7 +31,6 @@ const ALICE_ETH_ADDRESS: EthereumAddress = H160([100u8; 20]); const BOB_ETH_ADDRESS: EthereumAddress = H160([101u8; 20]); const ETH_ETH: eth::Asset = eth::Asset::Eth; const ETH_FLIP: eth::Asset = eth::Asset::Flip; -const EXPIRY_BLOCK: u64 = 6; #[track_caller] fn expect_size_of_address_pool(size: usize) { @@ -200,10 +201,8 @@ fn can_schedule_swap_egress_to_batch() { fn request_address_and_deposit( who: ChannelId, asset: eth::Asset, - expiry: u64, ) -> (ChannelId, ::ChainAccount) { - let (id, address) = - IngressEgress::request_liquidity_deposit_address(who, asset, expiry).unwrap(); + let (id, address) = IngressEgress::request_liquidity_deposit_address(who, asset).unwrap(); let address: ::ChainAccount = address.try_into().unwrap(); assert_ok!(IngressEgress::process_single_deposit( address, @@ -220,9 +219,9 @@ fn can_schedule_deposit_fetch() { new_test_ext().execute_with(|| { assert!(ScheduledEgressFetchOrTransfer::::get().is_empty()); - request_address_and_deposit(1u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(2u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(3u64, eth::Asset::Flip, 1_000u64); + request_address_and_deposit(1u64, eth::Asset::Eth); + request_address_and_deposit(2u64, eth::Asset::Eth); + request_address_and_deposit(3u64, eth::Asset::Flip); assert!(matches!( &ScheduledEgressFetchOrTransfer::::get()[..], @@ -237,7 +236,7 @@ fn can_schedule_deposit_fetch() { crate::Event::DepositFetchesScheduled { channel_id: 1, asset: eth::Asset::Eth }, )); - request_address_and_deposit(4u64, eth::Asset::Eth, 1_000u64); + request_address_and_deposit(4u64, eth::Asset::Eth); assert!(matches!( &ScheduledEgressFetchOrTransfer::::get()[..], @@ -258,16 +257,16 @@ fn on_finalize_can_send_batch_all() { IngressEgress::schedule_egress(ETH_ETH, 2_000, ALICE_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_ETH, 3_000, BOB_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_ETH, 4_000, BOB_ETH_ADDRESS, None); - request_address_and_deposit(1u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(2u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(3u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(4u64, eth::Asset::Eth, 1_000u64); + request_address_and_deposit(1u64, eth::Asset::Eth); + request_address_and_deposit(2u64, eth::Asset::Eth); + request_address_and_deposit(3u64, eth::Asset::Eth); + request_address_and_deposit(4u64, eth::Asset::Eth); IngressEgress::schedule_egress(ETH_FLIP, 5_000, ALICE_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_FLIP, 6_000, ALICE_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_FLIP, 7_000, BOB_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_FLIP, 8_000, BOB_ETH_ADDRESS, None); - request_address_and_deposit(5u64, eth::Asset::Flip, 1_000u64); + request_address_and_deposit(5u64, eth::Asset::Flip); // Take all scheduled Egress and Broadcast as batch IngressEgress::on_finalize(1); @@ -299,19 +298,19 @@ fn all_batch_apicall_creation_failure_should_rollback_storage() { IngressEgress::schedule_egress(ETH_ETH, 2_000, ALICE_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_ETH, 3_000, BOB_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_ETH, 4_000, BOB_ETH_ADDRESS, None); - request_address_and_deposit(1u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(2u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(3u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(4u64, eth::Asset::Eth, 1_000u64); + request_address_and_deposit(1u64, eth::Asset::Eth); + request_address_and_deposit(2u64, eth::Asset::Eth); + request_address_and_deposit(3u64, eth::Asset::Eth); + request_address_and_deposit(4u64, eth::Asset::Eth); IngressEgress::schedule_egress(ETH_FLIP, 5_000, ALICE_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_FLIP, 6_000, ALICE_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_FLIP, 7_000, BOB_ETH_ADDRESS, None); IngressEgress::schedule_egress(ETH_FLIP, 8_000, BOB_ETH_ADDRESS, None); - request_address_and_deposit(5u64, eth::Asset::Flip, 1_000u64); + request_address_and_deposit(5u64, eth::Asset::Flip); MockAllBatch::::set_success(false); - request_address_and_deposit(4u64, eth::Asset::Usdc, 1_000u64); + request_address_and_deposit(4u64, eth::Asset::Usdc); let scheduled_requests = ScheduledEgressFetchOrTransfer::::get(); @@ -329,21 +328,10 @@ fn addresses_are_getting_reused() { // Request 2 deposit addresses and deposit to one of them. .request_address_and_deposit(&[ ( - DepositRequest::Liquidity { - lp_account: ALICE, - asset: eth::Asset::Eth, - expiry_block: 1000_u64, - }, + DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Eth }, 100u32.into(), ), - ( - DepositRequest::Liquidity { - lp_account: ALICE, - asset: eth::Asset::Eth, - expiry_block: 1000_u64, - }, - 0u32.into(), - ), + (DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Eth }, 0u32.into()), ]) .inspect_storage(|deposit_details| { assert_eq!(ChannelIdCounter::::get(), deposit_details.len() as u64); @@ -365,9 +353,10 @@ fn addresses_are_getting_reused() { channels }) .then_execute_at_next_block(|channels| { - for (_request, _id, address) in &channels { - IngressEgress::expire_channel(*address); - } + let recycle_block = BlockHeightProvider::::get_block_height() + + DepositChannelLifetime::::get() * 2; + BlockHeightProvider::::set_block_height(recycle_block); + channels[0].clone() }) // Check that the used address is now deployed and in the pool of available addresses. @@ -379,7 +368,6 @@ fn addresses_are_getting_reused() { .request_deposit_addresses(&[(DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Eth, - expiry_block: 1000_u64, })]) // The address should have been taken from the pool and the id counter unchanged. .inspect_storage(|_| { @@ -392,18 +380,23 @@ fn addresses_are_getting_reused() { fn proof_address_pool_integrity() { new_test_ext().execute_with(|| { let channel_details = (0..3) - .map(|id| request_address_and_deposit(id, eth::Asset::Eth, 1_000u64)) + .map(|id| request_address_and_deposit(id, eth::Asset::Eth)) .collect::>(); // All addresses in use expect_size_of_address_pool(0); IngressEgress::on_finalize(1); for (_id, address) in channel_details { assert_ok!(IngressEgress::finalise_ingress(RuntimeOrigin::root(), vec![address])); - IngressEgress::expire_channel(address); } + let recycle_block = BlockHeightProvider::::get_block_height() + + DepositChannelLifetime::::get() * 2; + BlockHeightProvider::::set_block_height(recycle_block); + + IngressEgress::on_idle(1, Weight::MAX); + // Expect all addresses to be available expect_size_of_address_pool(3); - request_address_and_deposit(4u64, eth::Asset::Eth, 1_000u64); + request_address_and_deposit(4u64, eth::Asset::Eth); // Expect one address to be in use expect_size_of_address_pool(2); }); @@ -413,19 +406,21 @@ fn proof_address_pool_integrity() { fn create_new_address_while_pool_is_empty() { new_test_ext().execute_with(|| { let channel_details = (0..2) - .map(|id| request_address_and_deposit(id, eth::Asset::Eth, 1_000u64)) + .map(|id| request_address_and_deposit(id, eth::Asset::Eth)) .collect::>(); IngressEgress::on_finalize(1); for (_id, address) in channel_details { assert_ok!(IngressEgress::finalise_ingress(RuntimeOrigin::root(), vec![address])); - IngressEgress::expire_channel(address); } - IngressEgress::on_initialize(EXPIRY_BLOCK); + let recycle_block = BlockHeightProvider::::get_block_height() + + DepositChannelLifetime::::get() * 2; + BlockHeightProvider::::set_block_height(recycle_block); + IngressEgress::on_idle(1, Weight::MAX); + assert_eq!(ChannelIdCounter::::get(), 2); - request_address_and_deposit(3u64, eth::Asset::Eth, 1_000u64); + request_address_and_deposit(3u64, eth::Asset::Eth); assert_eq!(ChannelIdCounter::::get(), 2); IngressEgress::on_finalize(1); - IngressEgress::on_initialize(EXPIRY_BLOCK); assert_eq!(ChannelIdCounter::::get(), 2); }); } @@ -442,7 +437,6 @@ fn reused_address_channel_id_matches() { let (reused_channel_id, reused_address) = IngressEgress::open_channel( eth::Asset::Eth, ChannelAction::LiquidityProvision { lp_account: 0 }, - 1_000u64, ) .unwrap(); // The reused details should be the same as before. @@ -478,7 +472,6 @@ fn can_process_ccm_deposit() { 0, 1, Some(channel_metadata), - 1_000u64, ) .unwrap(); @@ -590,7 +583,7 @@ fn multi_use_deposit_address_different_blocks() { const ETH: eth::Asset = eth::Asset::Eth; new_test_ext() - .then_execute_at_next_block(|_| request_address_and_deposit(ALICE, ETH, 1_000u64)) + .then_execute_at_next_block(|_| request_address_and_deposit(ALICE, ETH)) .then_execute_at_next_block(|channel @ (_, deposit_address)| { // Set the address to deployed. // Do another, should succeed. @@ -601,11 +594,14 @@ fn multi_use_deposit_address_different_blocks() { (), Default::default() )); + let recycle_block = BlockHeightProvider::::get_block_height() + + DepositChannelLifetime::::get() * 2; + BlockHeightProvider::::set_block_height(recycle_block); + channel }) .then_execute_at_next_block(|(_, deposit_address)| { // Closing the channel should invalidate the deposit address. - IngressEgress::expire_channel(deposit_address); assert_noop!( IngressEgress::process_deposits( RuntimeOrigin::root(), @@ -628,11 +624,7 @@ fn multi_use_deposit_same_block() { const FLIP: eth::Asset = eth::Asset::Flip; const DEPOSIT_AMOUNT: ::ChainAmount = 1_000; new_test_ext() - .request_deposit_addresses(&[DepositRequest::Liquidity { - lp_account: ALICE, - asset: FLIP, - expiry_block: 1_000u64, - }]) + .request_deposit_addresses(&[DepositRequest::Liquidity { lp_account: ALICE, asset: FLIP }]) .map_context(|mut ctx| { assert!(ctx.len() == 1); ctx.pop().unwrap() @@ -785,7 +777,7 @@ fn deposits_below_minimum_are_rejected() { )); // Observe that eth deposit gets rejected. - let (_, deposit_address) = request_address_and_deposit(0, eth, 1_000u64); + let (_, deposit_address) = request_address_and_deposit(0, eth); System::assert_last_event(RuntimeEvent::IngressEgress( crate::Event::::DepositIgnored { deposit_address, @@ -796,7 +788,7 @@ fn deposits_below_minimum_are_rejected() { )); // Flip deposit should succeed. - let (_, deposit_address) = request_address_and_deposit(0, flip, 1_000u64); + let (_, deposit_address) = request_address_and_deposit(0, flip); System::assert_last_event(RuntimeEvent::IngressEgress( crate::Event::::DepositReceived { deposit_address, @@ -813,7 +805,7 @@ fn handle_pending_deployment() { const ETH: eth::Asset = eth::Asset::Eth; new_test_ext().execute_with(|| { // Initial request. - let (_, deposit_address) = request_address_and_deposit(ALICE, eth::Asset::Eth, 1_000u64); + let (_, deposit_address) = request_address_and_deposit(ALICE, eth::Asset::Eth); assert_eq!(ScheduledEgressFetchOrTransfer::::decode_len().unwrap_or_default(), 1); // Process deposits. IngressEgress::on_finalize(1); @@ -822,8 +814,8 @@ fn handle_pending_deployment() { Pallet::::process_single_deposit(deposit_address, ETH, 1, (), Default::default()) .unwrap(); // None-pending requests can still be sent - request_address_and_deposit(1u64, eth::Asset::Eth, 1_000u64); - request_address_and_deposit(2u64, eth::Asset::Eth, 1_000u64); + request_address_and_deposit(1u64, eth::Asset::Eth); + request_address_and_deposit(2u64, eth::Asset::Eth); assert_eq!(ScheduledEgressFetchOrTransfer::::decode_len().unwrap_or_default(), 3); // Process deposit again. IngressEgress::on_finalize(1); @@ -841,7 +833,7 @@ fn handle_pending_deployment() { fn handle_pending_deployment_same_block() { new_test_ext().execute_with(|| { // Initial request. - let (_, deposit_address) = request_address_and_deposit(ALICE, eth::Asset::Eth, 1_000u64); + let (_, deposit_address) = request_address_and_deposit(ALICE, eth::Asset::Eth); Pallet::::process_single_deposit( deposit_address, eth::Asset::Eth, @@ -877,7 +869,7 @@ fn channel_reuse_with_different_assets() { new_test_ext() // First, request a deposit address and use it, then close it so it gets recycled. .request_address_and_deposit(&[( - DepositRequest::Liquidity { lp_account: ALICE, asset: ASSET_1, expiry_block: 1_000u64 }, + DepositRequest::Liquidity { lp_account: ALICE, asset: ASSET_1 }, 100_000, )]) .map_context(|mut result| result.pop().unwrap()) @@ -894,8 +886,11 @@ fn channel_reuse_with_different_assets() { asset ); }) - .then_execute_at_next_block(|(_, channel_id, channel_address)| { - IngressEgress::expire_channel(channel_address); + // move forward expired blocks + .then_execute_at_next_block(|(_, channel_id, _)| { + let recycle_block = BlockHeightProvider::::get_block_height() + + DepositChannelLifetime::::get() * 2; + BlockHeightProvider::::set_block_height(recycle_block); channel_id }) .inspect_storage(|channel_id| { @@ -909,7 +904,6 @@ fn channel_reuse_with_different_assets() { .request_deposit_addresses(&[DepositRequest::Liquidity { lp_account: ALICE, asset: ASSET_2, - expiry_block: 1_000u64, }]) .map_context(|mut result| result.pop().unwrap()) // Ensure that the deposit channel's asset is updated. @@ -923,6 +917,28 @@ fn channel_reuse_with_different_assets() { }); } +/// This is the sequence we're testing. +/// 1. Request deposit address +/// 2. Deposit to address when it's almost expired +/// 3. The channel is expired +/// 4. We need to finalise the ingress, by fetching +/// 5. The fetch should succeed. +#[test] +fn ingress_finalisation_succeeds_after_channel_recycled() { + new_test_ext().execute_with(|| { + request_address_and_deposit(ALICE, eth::Asset::Eth); + + let recycle_block = BlockHeightProvider::::get_block_height() + + DepositChannelLifetime::::get() * 2; + BlockHeightProvider::::set_block_height(recycle_block); + IngressEgress::on_idle(1, Weight::MAX); + + IngressEgress::on_finalize(1); + + assert!(ScheduledEgressFetchOrTransfer::::get().is_empty()); + }); +} + #[test] fn can_store_failed_vault_transfers() { new_test_ext().execute_with(|| { @@ -953,8 +969,6 @@ fn basic_balance_tracking() { const ETH_DEPOSIT_AMOUNT: u128 = 1_000; const FLIP_DEPOSIT_AMOUNT: u128 = 2_000; const USDC_DEPOSIT_AMOUNT: u128 = 3_000; - // Expiry just needs to be sufficiently high so that it won't trigger. - const EXPIRY_BLOCK: u64 = 1_000; new_test_ext() .check_deposit_balances(&[ @@ -963,11 +977,7 @@ fn basic_balance_tracking() { (eth::Asset::Usdc, 0), ]) .request_address_and_deposit(&[( - DepositRequest::Liquidity { - lp_account: ALICE, - asset: eth::Asset::Eth, - expiry_block: EXPIRY_BLOCK, - }, + DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Eth }, ETH_DEPOSIT_AMOUNT, )]) .check_deposit_balances(&[ @@ -976,11 +986,7 @@ fn basic_balance_tracking() { (eth::Asset::Usdc, 0), ]) .request_address_and_deposit(&[( - DepositRequest::Liquidity { - lp_account: ALICE, - asset: eth::Asset::Flip, - expiry_block: EXPIRY_BLOCK, - }, + DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Flip }, FLIP_DEPOSIT_AMOUNT, )]) .check_deposit_balances(&[ @@ -989,11 +995,7 @@ fn basic_balance_tracking() { (eth::Asset::Usdc, 0), ]) .request_address_and_deposit(&[( - DepositRequest::Liquidity { - lp_account: ALICE, - asset: eth::Asset::Usdc, - expiry_block: EXPIRY_BLOCK, - }, + DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Usdc }, USDC_DEPOSIT_AMOUNT, )]) .check_deposit_balances(&[ @@ -1002,11 +1004,7 @@ fn basic_balance_tracking() { (eth::Asset::Usdc, USDC_DEPOSIT_AMOUNT), ]) .request_address_and_deposit(&[( - DepositRequest::Liquidity { - lp_account: ALICE, - asset: eth::Asset::Eth, - expiry_block: EXPIRY_BLOCK, - }, + DepositRequest::Liquidity { lp_account: ALICE, asset: eth::Asset::Eth }, ETH_DEPOSIT_AMOUNT, )]) .check_deposit_balances(&[ @@ -1019,7 +1017,6 @@ fn basic_balance_tracking() { source_asset: eth::Asset::Eth, destination_asset: eth::Asset::Flip, destination_address: ForeignChainAddress::Eth(Default::default()), - expiry_block: EXPIRY_BLOCK, }, ETH_DEPOSIT_AMOUNT, )]) diff --git a/state-chain/pallets/cf-lp/src/benchmarking.rs b/state-chain/pallets/cf-lp/src/benchmarking.rs index a341899a2c..9f6123a72f 100644 --- a/state-chain/pallets/cf-lp/src/benchmarking.rs +++ b/state-chain/pallets/cf-lp/src/benchmarking.rs @@ -5,7 +5,7 @@ use cf_chains::{address::EncodedAddress, benchmarking_value::BenchmarkValue}; use cf_primitives::Asset; use cf_traits::AccountRoleRegistry; use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::{assert_ok, dispatch::UnfilteredDispatchable, traits::OnNewAccount}; +use frame_support::{assert_ok, traits::OnNewAccount}; use frame_system::RawOrigin; benchmarks! { @@ -40,38 +40,6 @@ benchmarks! { verify { assert_ok!(T::AccountRoleRegistry::ensure_liquidity_provider(RawOrigin::Signed(caller).into())); } - - on_initialize { - let a in 1..100; - let caller: T::AccountId = whitelisted_caller(); - ::OnNewAccount::on_new_account(&caller); - let _ = Pallet::::register_lp_account(RawOrigin::Signed(caller.clone()).into()); - let _ = Pallet::::register_liquidity_refund_address( - RawOrigin::Signed(caller.clone()).into(), - EncodedAddress::Eth(Default::default()), - ); - for i in 0..a { - assert_ok!(Pallet::::request_liquidity_deposit_address(RawOrigin::Signed(caller.clone()).into(), Asset::Eth)); - } - let expiry = LpTTL::::get() + frame_system::Pallet::::current_block_number(); - assert!(!LiquidityChannelExpiries::::get(expiry).is_empty()); - }: { - Pallet::::on_initialize(expiry); - } verify { - assert!(LiquidityChannelExpiries::::get(expiry).is_empty()); - } - - set_lp_ttl { - let ttl = BlockNumberFor::::from(1_000u32); - let call = Call::::set_lp_ttl { - ttl, - }; - }: { - let _ = call.dispatch_bypass_filter(T::EnsureGovernance::try_successful_origin().unwrap()); - } verify { - assert_eq!(crate::LpTTL::::get(), ttl); - } - register_liquidity_refund_address { let caller: T::AccountId = whitelisted_caller(); ::OnNewAccount::on_new_account(&caller); diff --git a/state-chain/pallets/cf-lp/src/lib.rs b/state-chain/pallets/cf-lp/src/lib.rs index 5c112da763..8f781fe184 100644 --- a/state-chain/pallets/cf-lp/src/lib.rs +++ b/state-chain/pallets/cf-lp/src/lib.rs @@ -7,13 +7,9 @@ use cf_traits::{ impl_pallet_safe_mode, liquidity::LpBalanceApi, AccountRoleRegistry, Chainflip, DepositApi, EgressApi, }; -use frame_support::{ - pallet_prelude::*, - sp_runtime::{traits::BlockNumberProvider, DispatchResult, Saturating}, -}; +use frame_support::{pallet_prelude::*, sp_runtime::DispatchResult}; use frame_system::pallet_prelude::*; pub use pallet::*; -use sp_std::vec::Vec; mod benchmarking; @@ -83,21 +79,6 @@ pub mod pallet { WithdrawalsDisabled, } - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_initialize(n: BlockNumberFor) -> Weight { - let expired = LiquidityChannelExpiries::::take(n); - let expired_count = expired.len(); - for (_, address) in expired { - T::DepositHandler::expire_channel(address.clone()); - Self::deposit_event(Event::LiquidityDepositAddressExpired { - address: T::AddressConverter::to_encoded_address(address), - }); - } - T::WeightInfo::on_initialize(expired_count as u32) - } - } - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -115,22 +96,15 @@ pub mod pallet { channel_id: ChannelId, asset: Asset, deposit_address: EncodedAddress, - expiry_block: BlockNumberFor, // account the funds will be credited to upon deposit account_id: T::AccountId, }, - LiquidityDepositAddressExpired { - address: EncodedAddress, - }, WithdrawalEgressScheduled { egress_id: EgressId, asset: Asset, amount: AssetAmount, destination_address: EncodedAddress, }, - LpTtlSet { - ttl: BlockNumberFor, - }, LiquidityRefundAddressRegistered { account_id: T::AccountId, chain: ForeignChain, @@ -138,24 +112,6 @@ pub mod pallet { }, } - #[pallet::genesis_config] - pub struct GenesisConfig { - pub lp_ttl: BlockNumberFor, - } - - #[pallet::genesis_build] - impl BuildGenesisConfig for GenesisConfig { - fn build(&self) { - LpTTL::::put(self.lp_ttl); - } - } - - impl Default for GenesisConfig { - fn default() -> Self { - Self { lp_ttl: BlockNumberFor::::from(1200u32) } - } - } - #[pallet::pallet] #[pallet::without_storage_info] pub struct Pallet(PhantomData); @@ -165,18 +121,7 @@ pub mod pallet { pub type FreeBalances = StorageDoubleMap<_, Twox64Concat, T::AccountId, Identity, Asset, AssetAmount>; - /// For a given block number, stores the list of liquidity deposit channels that expire at that - /// block. - #[pallet::storage] - pub(super) type LiquidityChannelExpiries = StorageMap< - _, - Twox64Concat, - BlockNumberFor, - Vec<(ChannelId, cf_chains::ForeignChainAddress)>, - ValueQuery, - >; - - /// Stores the registered refund address for an Account + /// Stores the registered energency withdrawal address for an Account #[pallet::storage] pub type LiquidityRefundAddress = StorageDoubleMap< _, @@ -187,10 +132,6 @@ pub mod pallet { ForeignChainAddress, >; - /// The TTL for liquidity channels. - #[pallet::storage] - pub type LpTTL = StorageValue<_, BlockNumberFor, ValueQuery>; - #[pallet::call] impl Pallet { /// For when the user wants to deposit assets into the Chain. @@ -210,26 +151,13 @@ pub mod pallet { Error::::NoLiquidityRefundAddressRegistered ); - let expiry_block = - frame_system::Pallet::::current_block_number().saturating_add(LpTTL::::get()); - let (channel_id, deposit_address) = - T::DepositHandler::request_liquidity_deposit_address( - account_id.clone(), - asset, - expiry_block, - )?; - - LiquidityChannelExpiries::::append( - expiry_block, - (channel_id, deposit_address.clone()), - ); + T::DepositHandler::request_liquidity_deposit_address(account_id.clone(), asset)?; Self::deposit_event(Event::LiquidityDepositAddressReady { channel_id, asset, deposit_address: T::AddressConverter::to_encoded_address(deposit_address), - expiry_block, account_id, }); @@ -296,23 +224,6 @@ pub mod pallet { Ok(()) } - /// Sets the lifetime of liquidity deposit channels. - /// - /// Requires Governance - /// - /// ## Events - /// - /// - [On update](Event::LpTtlSet) - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::set_lp_ttl())] - pub fn set_lp_ttl(origin: OriginFor, ttl: BlockNumberFor) -> DispatchResult { - T::EnsureGovernance::ensure_origin(origin)?; - LpTTL::::set(ttl); - - Self::deposit_event(Event::::LpTtlSet { ttl }); - Ok(()) - } - /// Registers an Liquidity Refund Address(LRA) for an account. /// To request deposit address for a chain, an LRA must be registered for that chain. /// diff --git a/state-chain/pallets/cf-lp/src/tests.rs b/state-chain/pallets/cf-lp/src/tests.rs index 8b1731dcea..57b99deb44 100644 --- a/state-chain/pallets/cf-lp/src/tests.rs +++ b/state-chain/pallets/cf-lp/src/tests.rs @@ -1,20 +1,11 @@ -use crate::{mock::*, FreeBalances, LiquidityChannelExpiries, LiquidityRefundAddress, LpTTL}; +use crate::{mock::*, FreeBalances, LiquidityRefundAddress}; -use cf_chains::{ - address::{AddressConverter, EncodedAddress}, - AnyChain, ForeignChainAddress, -}; +use cf_chains::{address::EncodedAddress, ForeignChainAddress}; use cf_primitives::{AccountId, Asset, ForeignChain}; use cf_test_utilities::assert_events_match; -use cf_traits::{ - mocks::{ - address_converter::MockAddressConverter, - deposit_handler::{LpChannel, MockDepositHandler}, - }, - SetSafeMode, -}; -use frame_support::{assert_noop, assert_ok, error::BadOrigin, traits::Hooks}; +use cf_traits::SetSafeMode; +use frame_support::{assert_noop, assert_ok, error::BadOrigin}; #[test] fn egress_chain_and_asset_must_match() { @@ -118,75 +109,6 @@ fn cannot_deposit_and_withdrawal_during_safe_mode() { }); } -#[test] -fn deposit_channel_expires() { - new_test_ext().execute_with(|| { - assert_ok!(LiquidityProvider::register_liquidity_refund_address( - RuntimeOrigin::signed(LP_ACCOUNT.into()), - EncodedAddress::Eth(Default::default()), - )); - // Expiry = current (1) + ttl - let expiry = LpTTL::::get() + 1; - let asset = Asset::Eth; - assert_ok!(LiquidityProvider::request_liquidity_deposit_address( - RuntimeOrigin::signed(LP_ACCOUNT.into()), - asset, - )); - - let (channel_id, deposit_address) = assert_events_match!(Test, RuntimeEvent::LiquidityProvider(crate::Event::LiquidityDepositAddressReady { - asset: event_asset, - channel_id, - deposit_address, - expiry_block, - account_id, - }) if expiry_block == expiry && event_asset == asset && account_id == LP_ACCOUNT.into() => (channel_id, deposit_address)); - let lp_channel = LpChannel { - deposit_address: MockAddressConverter::try_from_encoded_address(deposit_address.clone()).unwrap(), - source_asset: asset, - lp_account: LP_ACCOUNT.into(), - expiry, - }; - - assert_eq!( - LiquidityChannelExpiries::::get(expiry), - vec![(channel_id, MockAddressConverter::try_from_encoded_address(deposit_address.clone()).unwrap())] - ); - assert_eq!( - MockDepositHandler::::get_liquidity_channels(), - vec![lp_channel.clone()] - ); - - // Does not expire until expiry - LiquidityProvider::on_initialize(expiry - 1); - assert_eq!( - LiquidityChannelExpiries::::get(expiry), - vec![(channel_id, MockAddressConverter::try_from_encoded_address(deposit_address.clone()).unwrap())] - ); - assert_eq!( - MockDepositHandler::::get_liquidity_channels(), - vec![lp_channel] - ); - - // Expire the address on the expiry block - LiquidityProvider::on_initialize(expiry); - - assert_eq!(LiquidityChannelExpiries::::get(expiry), vec![]); - System::assert_last_event(RuntimeEvent::LiquidityProvider( - crate::Event::::LiquidityDepositAddressExpired { address: deposit_address }, - )); - assert!(MockDepositHandler::::get_liquidity_channels().is_empty()); - }); -} - -#[test] -fn can_set_lp_ttl() { - new_test_ext().execute_with(|| { - assert_eq!(LpTTL::::get(), 1_200); - assert_ok!(LiquidityProvider::set_lp_ttl(RuntimeOrigin::root(), 10)); - assert_eq!(LpTTL::::get(), 10); - }); -} - #[test] fn can_register_and_deregister_liquidity_refund_address() { new_test_ext().execute_with(|| { diff --git a/state-chain/pallets/cf-swapping/src/benchmarking.rs b/state-chain/pallets/cf-swapping/src/benchmarking.rs index 0440e2cb7f..ba5ce5a8b2 100644 --- a/state-chain/pallets/cf-swapping/src/benchmarking.rs +++ b/state-chain/pallets/cf-swapping/src/benchmarking.rs @@ -123,41 +123,6 @@ benchmarks! { )]); } - on_initialize { - let a in 1..100; - let caller: T::AccountId = whitelisted_caller(); - ::OnNewAccount::on_new_account(&caller); - T::AccountRoleRegistry::register_as_broker(&caller).unwrap(); - let origin = RawOrigin::Signed(caller); - for i in 0..a { - let call = Call::::request_swap_deposit_address{ - source_asset: Asset::Usdc, - destination_asset: Asset::Eth, - destination_address: EncodedAddress::Eth(Default::default()), - broker_commission_bps: Default::default(), - channel_metadata: None, - }; - call.dispatch_bypass_filter(origin.clone().into())?; - } - let expiry = SwapTTL::::get() + frame_system::Pallet::::current_block_number(); - assert!(!SwapChannelExpiries::::get(expiry).is_empty()); - }: { - Pallet::::on_initialize(expiry); - } verify { - assert!(SwapChannelExpiries::::get(expiry).is_empty()); - } - - set_swap_ttl { - let ttl = BlockNumberFor::::from(1_000u32); - let call = Call::::set_swap_ttl { - ttl - }; - }: { - let _ = call.dispatch_bypass_filter(::EnsureGovernance::try_successful_origin().unwrap()); - } verify { - assert_eq!(crate::SwapTTL::::get(), ttl); - } - set_minimum_swap_amount { let asset = Asset::Eth; let amount = 1_000; diff --git a/state-chain/pallets/cf-swapping/src/lib.rs b/state-chain/pallets/cf-swapping/src/lib.rs index 488a68918c..3cd21a0370 100644 --- a/state-chain/pallets/cf-swapping/src/lib.rs +++ b/state-chain/pallets/cf-swapping/src/lib.rs @@ -7,14 +7,17 @@ use cf_primitives::{ Asset, AssetAmount, ChannelId, ForeignChain, SwapLeg, TransactionHash, STABLE_ASSET, }; use cf_traits::{impl_pallet_safe_mode, liquidity::SwappingApi, CcmHandler, DepositApi}; -use frame_support::{pallet_prelude::*, storage::with_storage_layer}; +use frame_support::{ + pallet_prelude::*, + sp_runtime::{ + traits::{Get, Saturating}, + DispatchError, Permill, + }, + storage::with_storage_layer, +}; use frame_system::pallet_prelude::*; pub use pallet::*; use sp_arithmetic::{helpers_128bit::multiply_by_rational_with_rounding, traits::Zero, Rounding}; -use sp_runtime::{ - traits::{BlockNumberProvider, Get, Saturating}, - DispatchError, Permill, -}; use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec}; #[cfg(test)] @@ -223,23 +226,10 @@ pub mod pallet { #[pallet::storage] pub type CcmGasBudget = StorageMap<_, Twox64Concat, u64, (Asset, AssetAmount)>; - /// Stores the swap TTL in blocks. - #[pallet::storage] - pub type SwapTTL = StorageValue<_, BlockNumberFor, ValueQuery>; - /// Storage for storing CCMs pending assets to be swapped. #[pallet::storage] pub(crate) type PendingCcms = StorageMap<_, Twox64Concat, u64, CcmSwap>; - /// For a given block number, stores the list of swap channels that expire at that block. - #[pallet::storage] - pub type SwapChannelExpiries = StorageMap< - _, - Twox64Concat, - BlockNumberFor, - Vec<(ChannelId, ForeignChainAddress)>, - ValueQuery, - >; /// Tracks the outputs of Ccm swaps. #[pallet::storage] pub(crate) type CcmOutputs = StorageMap<_, Twox64Concat, u64, CcmSwapOutput>; @@ -262,7 +252,6 @@ pub mod pallet { SwapDepositAddressReady { deposit_address: EncodedAddress, destination_address: EncodedAddress, - expiry_block: BlockNumberFor, source_asset: Asset, destination_asset: Asset, channel_id: ChannelId, @@ -313,13 +302,6 @@ pub mod pallet { ccm_id: u64, egress_id: EgressId, }, - SwapDepositAddressExpired { - deposit_address: EncodedAddress, - channel_id: ChannelId, - }, - SwapTtlSet { - ttl: BlockNumberFor, - }, CcmDepositReceived { ccm_id: u64, principal_swap_id: Option, @@ -370,14 +352,13 @@ pub mod pallet { #[pallet::genesis_config] pub struct GenesisConfig { - pub swap_ttl: BlockNumberFor, pub minimum_swap_amounts: Vec<(Asset, AssetAmount)>, + pub _phantom: PhantomData, } #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - SwapTTL::::put(self.swap_ttl); for (asset, min) in &self.minimum_swap_amounts { MinimumSwapAmount::::insert(asset, min); } @@ -386,27 +367,12 @@ pub mod pallet { impl Default for GenesisConfig { fn default() -> Self { - // 1200 = 2 hours (6 sec per block) - Self { swap_ttl: BlockNumberFor::::from(1_200u32), minimum_swap_amounts: vec![] } + Self { minimum_swap_amounts: vec![], _phantom: PhantomData } } } #[pallet::hooks] impl Hooks> for Pallet { - /// Clean up expired deposit channels - fn on_initialize(n: BlockNumberFor) -> Weight { - let expired = SwapChannelExpiries::::take(n); - let expired_count = expired.len(); - for (channel_id, address) in expired { - T::DepositHandler::expire_channel(address.clone()); - Self::deposit_event(Event::::SwapDepositAddressExpired { - deposit_address: T::AddressConverter::to_encoded_address(address), - channel_id, - }); - } - T::WeightInfo::on_initialize(expired_count as u32) - } - /// Execute all swaps in the SwapQueue fn on_finalize(_n: BlockNumberFor) { if !T::SafeMode::get().swaps_enabled { @@ -528,9 +494,6 @@ pub mod pallet { ); } - let expiry_block = frame_system::Pallet::::current_block_number() - .saturating_add(SwapTTL::::get()); - let (channel_id, deposit_address) = T::DepositHandler::request_swap_deposit_address( source_asset, destination_asset, @@ -538,15 +501,11 @@ pub mod pallet { broker_commission_bps, broker, channel_metadata.clone(), - expiry_block, )?; - SwapChannelExpiries::::append(expiry_block, (channel_id, deposit_address.clone())); - Self::deposit_event(Event::::SwapDepositAddressReady { deposit_address: T::AddressConverter::to_encoded_address(deposit_address), destination_address, - expiry_block, source_asset, destination_asset, channel_id, @@ -684,23 +643,6 @@ pub mod pallet { Ok(()) } - /// Sets the lifetime of swap channels. - /// - /// Requires Governance. - /// - /// ## Events - /// - /// - [On update](Event::SwapTtlSet) - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::set_swap_ttl())] - pub fn set_swap_ttl(origin: OriginFor, ttl: BlockNumberFor) -> DispatchResult { - T::EnsureGovernance::ensure_origin(origin)?; - SwapTTL::::set(ttl); - - Self::deposit_event(Event::::SwapTtlSet { ttl }); - Ok(()) - } - /// Sets the Minimum swap amount allowed for an asset. /// /// Requires Governance. diff --git a/state-chain/pallets/cf-swapping/src/mock.rs b/state-chain/pallets/cf-swapping/src/mock.rs index 11c57c0a83..3779906737 100644 --- a/state-chain/pallets/cf-swapping/src/mock.rs +++ b/state-chain/pallets/cf-swapping/src/mock.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use crate::{self as pallet_cf_swapping, PalletSafeMode, WeightInfo}; use cf_chains::AnyChain; use cf_primitives::{Asset, AssetAmount}; @@ -112,10 +114,6 @@ impl WeightInfo for MockWeightInfo { Weight::from_parts(100, 0) } - fn set_swap_ttl() -> Weight { - Weight::from_parts(100, 0) - } - fn set_minimum_swap_amount() -> Weight { Weight::from_parts(100, 0) } @@ -137,7 +135,7 @@ cf_test_utilities::impl_test_helpers! { Test, RuntimeGenesisConfig { system: Default::default(), - swapping: SwappingConfig { swap_ttl: 5, minimum_swap_amounts: vec![] }, + swapping: SwappingConfig { minimum_swap_amounts: vec![], _phantom: PhantomData }, }, || { >::register_as_broker(&ALICE).unwrap(); diff --git a/state-chain/pallets/cf-swapping/src/tests.rs b/state-chain/pallets/cf-swapping/src/tests.rs index 2e9943a019..4f08a0fb31 100644 --- a/state-chain/pallets/cf-swapping/src/tests.rs +++ b/state-chain/pallets/cf-swapping/src/tests.rs @@ -2,7 +2,7 @@ use crate::{ mock::{RuntimeEvent, *}, CcmFailReason, CcmGasBudget, CcmIdCounter, CcmOutputs, CcmSwap, CcmSwapOutput, CollectedRejectedFunds, EarnedBrokerFees, Error, Event, MinimumSwapAmount, Pallet, PendingCcms, - Swap, SwapChannelExpiries, SwapOrigin, SwapQueue, SwapTTL, SwapType, + Swap, SwapOrigin, SwapQueue, SwapType, }; use cf_chains::{ address::{to_encoded_address, AddressConverter, EncodedAddress, ForeignChainAddress}, @@ -11,11 +11,10 @@ use cf_chains::{ AnyChain, CcmChannelMetadata, CcmDepositMetadata, }; use cf_primitives::{Asset, AssetAmount, ForeignChain, NetworkEnvironment}; -use cf_test_utilities::{assert_event_sequence, assert_events_match}; +use cf_test_utilities::assert_event_sequence; use cf_traits::{ mocks::{ address_converter::MockAddressConverter, - deposit_handler::{MockDepositHandler, SwapChannel}, egress_handler::{MockEgressHandler, MockEgressParameter}, }, CcmHandler, SetSafeMode, SwapDepositHandler, SwappingApi, @@ -23,7 +22,6 @@ use cf_traits::{ use frame_support::{assert_noop, assert_ok, sp_std::iter}; use frame_support::traits::Hooks; -use sp_runtime::traits::BlockNumberProvider; // Returns some test data fn generate_test_swaps() -> Vec { @@ -229,12 +227,11 @@ fn expect_swap_id_to_be_emitted() { RuntimeEvent::Swapping(Event::SwapDepositAddressReady { deposit_address: EncodedAddress::Eth(..), destination_address: EncodedAddress::Eth(..), - expiry_block, source_asset: Asset::Eth, destination_asset: Asset::Usdc, channel_id: 0, .. - }) if expiry_block == SwapTTL::::get() + System::current_block_number(), + }), RuntimeEvent::Swapping(Event::SwapScheduled { swap_id: 1, source_asset: Asset::Eth, @@ -246,7 +243,9 @@ fn expect_swap_id_to_be_emitted() { channel_id: 1, deposit_block_height: 0 }, - swap_type: SwapType::Swap(ForeignChainAddress::Eth(..)), broker_commission: _ }), + swap_type: SwapType::Swap(ForeignChainAddress::Eth(..)), + broker_commission: _ + }), RuntimeEvent::Swapping(Event::SwapExecuted { swap_id: 1, .. }), RuntimeEvent::Swapping(Event::SwapEgressScheduled { swap_id: 1, @@ -315,78 +314,6 @@ fn can_swap_using_witness_origin() { }); } -#[test] -fn swap_expires() { - new_test_ext().execute_with(|| { - let expiry = SwapTTL::::get() + 1; - assert_eq!(expiry, 6); // Expiry = current(1) + TTL (5) - assert_ok!(Swapping::request_swap_deposit_address( - RuntimeOrigin::signed(ALICE), - Asset::Eth, - Asset::Usdc, - EncodedAddress::Eth(Default::default()), - 0, - None - )); - - let deposit_address = assert_events_match!(Test, RuntimeEvent::Swapping(Event::SwapDepositAddressReady { - deposit_address, - .. - }) => deposit_address); - let swap_channel = SwapChannel { - deposit_address: MockAddressConverter::try_from_encoded_address(deposit_address).unwrap(), - source_asset: Asset::Eth, - destination_asset: Asset::Usdc, - destination_address: ForeignChainAddress::Eth(Default::default()), - broker_commission_bps: 0, - broker_id: ALICE, - channel_metadata: None, - expiry, - }; - - assert_eq!( - SwapChannelExpiries::::get(expiry), - vec![(0, ForeignChainAddress::Eth(Default::default()))] - ); - assert_eq!( - MockDepositHandler::::get_swap_channels(), - vec![swap_channel.clone()] - ); - - // Does not expire until expiry block. - Swapping::on_initialize(expiry - 1); - assert_eq!( - SwapChannelExpiries::::get(expiry), - vec![(0, ForeignChainAddress::Eth(Default::default()))] - ); - assert_eq!( - MockDepositHandler::::get_swap_channels(), - vec![swap_channel] - ); - - Swapping::on_initialize(6); - assert_eq!(SwapChannelExpiries::::get(6), vec![]); - System::assert_last_event(RuntimeEvent::Swapping( - Event::::SwapDepositAddressExpired { - deposit_address: EncodedAddress::Eth(Default::default()), - channel_id: 0, - }, - )); - assert!( - MockDepositHandler::::get_swap_channels().is_empty() - ); - }); -} - -#[test] -fn can_set_swap_ttl() { - new_test_ext().execute_with(|| { - assert_eq!(crate::SwapTTL::::get(), 5); - assert_ok!(Swapping::set_swap_ttl(RuntimeOrigin::root(), 10)); - assert_eq!(crate::SwapTTL::::get(), 10); - }); -} - #[test] fn reject_invalid_ccm_deposit() { new_test_ext().execute_with(|| { diff --git a/state-chain/pallets/cf-swapping/src/weights.rs b/state-chain/pallets/cf-swapping/src/weights.rs index e3a61e8c1a..8614b2a5e3 100644 --- a/state-chain/pallets/cf-swapping/src/weights.rs +++ b/state-chain/pallets/cf-swapping/src/weights.rs @@ -36,7 +36,6 @@ pub trait WeightInfo { fn schedule_swap_from_contract() -> Weight; fn ccm_deposit() -> Weight; fn on_initialize(a: u32, ) -> Weight; - fn set_swap_ttl() -> Weight; fn set_minimum_swap_amount() -> Weight; } @@ -48,7 +47,6 @@ impl WeightInfo for PalletWeight { // Storage: EthereumIngressEgress ChannelIdCounter (r:1 w:1) // Storage: Environment EthereumVaultAddress (r:1 w:0) // Storage: Swapping SwapTTL (r:1 w:0) - // Storage: Swapping SwapChannelExpiries (r:1 w:1) // Storage: EthereumIngressEgress ChannelActions (r:0 w:1) // Storage: EthereumIngressEgress FetchParamDetails (r:0 w:1) // Storage: EthereumIngressEgress AddressStatus (r:0 w:1) @@ -91,7 +89,6 @@ impl WeightInfo for PalletWeight { .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: Swapping SwapChannelExpiries (r:1 w:1) // Storage: EthereumIngressEgress AddressStatus (r:1 w:0) // Storage: EthereumIngressEgress DepositAddressDetailsLookup (r:1 w:1) // Storage: EthereumIngressEgress ChannelActions (r:0 w:1) @@ -106,12 +103,6 @@ impl WeightInfo for PalletWeight { .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) } - // Storage: Swapping SwapTTL (r:0 w:1) - fn set_swap_ttl() -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_parts(14_000_000, 0) - .saturating_add(T::DbWeight::get().writes(1)) - } // Storage: AccountRoles SwappingEnabled (r:1 w:0) // Storage: AccountRoles AccountRoles (r:1 w:1) fn register_as_broker() -> Weight { @@ -133,7 +124,6 @@ impl WeightInfo for () { // Storage: EthereumIngressEgress ChannelIdCounter (r:1 w:1) // Storage: Environment EthereumVaultAddress (r:1 w:0) // Storage: Swapping SwapTTL (r:1 w:0) - // Storage: Swapping SwapChannelExpiries (r:1 w:1) // Storage: EthereumIngressEgress ChannelActions (r:0 w:1) // Storage: EthereumIngressEgress FetchParamDetails (r:0 w:1) // Storage: EthereumIngressEgress AddressStatus (r:0 w:1) @@ -176,7 +166,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(5)) } - // Storage: Swapping SwapChannelExpiries (r:1 w:1) // Storage: EthereumIngressEgress AddressStatus (r:1 w:0) // Storage: EthereumIngressEgress DepositAddressDetailsLookup (r:1 w:1) // Storage: EthereumIngressEgress ChannelActions (r:0 w:1) @@ -191,12 +180,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(1)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(a.into()))) } - // Storage: Swapping SwapTTL (r:0 w:1) - fn set_swap_ttl() -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_parts(14_000_000, 0) - .saturating_add(RocksDbWeight::get().writes(1)) - } // Storage: AccountRoles SwappingEnabled (r:1 w:0) // Storage: AccountRoles AccountRoles (r:1 w:1) fn register_as_broker() -> Weight { diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index 4bde2f0d4e..5804088bbb 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -460,7 +460,6 @@ macro_rules! impl_deposit_api_for_anychain { fn request_liquidity_deposit_address( lp_account: Self::AccountId, source_asset: Asset, - expiry: Self::BlockNumber, ) -> Result<(ChannelId, ForeignChainAddress), DispatchError> { match source_asset.into() { $( @@ -468,7 +467,6 @@ macro_rules! impl_deposit_api_for_anychain { $pallet::request_liquidity_deposit_address( lp_account, source_asset.try_into().unwrap(), - expiry, ), )+ } @@ -481,7 +479,6 @@ macro_rules! impl_deposit_api_for_anychain { broker_commission_bps: BasisPoints, broker_id: Self::AccountId, channel_metadata: Option, - expiry: Self::BlockNumber, ) -> Result<(ChannelId, ForeignChainAddress), DispatchError> { match source_asset.into() { $( @@ -492,23 +489,10 @@ macro_rules! impl_deposit_api_for_anychain { broker_commission_bps, broker_id, channel_metadata, - expiry, ), )+ } } - - fn expire_channel(address: ForeignChainAddress) { - match address.chain() { - $( - ForeignChain::$chain => { - <$pallet as DepositApi<$chain>>::expire_channel( - address.try_into().expect("Checked for address compatibility") - ); - }, - )+ - } - } } } } diff --git a/state-chain/traits/src/lib.rs b/state-chain/traits/src/lib.rs index b87c948922..02f53997b5 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -640,7 +640,6 @@ pub trait DepositApi { fn request_liquidity_deposit_address( lp_account: Self::AccountId, source_asset: C::ChainAsset, - expiry: Self::BlockNumber, ) -> Result<(ChannelId, ForeignChainAddress), DispatchError>; /// Issues a channel id and deposit address for a new swap. @@ -651,11 +650,7 @@ pub trait DepositApi { broker_commission_bps: BasisPoints, broker_id: Self::AccountId, channel_metadata: Option, - expiry: Self::BlockNumber, ) -> Result<(ChannelId, ForeignChainAddress), DispatchError>; - - /// Expires a channel. - fn expire_channel(address: C::ChainAccount); } pub trait AccountRoleRegistry { diff --git a/state-chain/traits/src/mocks/deposit_handler.rs b/state-chain/traits/src/mocks/deposit_handler.rs index 185d3f8e56..27b6e19a00 100644 --- a/state-chain/traits/src/mocks/deposit_handler.rs +++ b/state-chain/traits/src/mocks/deposit_handler.rs @@ -30,7 +30,6 @@ pub struct SwapChannel { pub broker_commission_bps: BasisPoints, pub broker_id: ::AccountId, pub channel_metadata: Option, - pub expiry: BlockNumberFor, } #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] @@ -38,7 +37,6 @@ pub struct LpChannel { pub deposit_address: ForeignChainAddress, pub source_asset: ::ChainAsset, pub lp_account: ::AccountId, - pub expiry: BlockNumberFor, } impl MockDepositHandler { @@ -85,7 +83,6 @@ impl DepositApi for MockDepositHandler { fn request_liquidity_deposit_address( lp_account: Self::AccountId, source_asset: ::ChainAsset, - expiry: Self::BlockNumber, ) -> Result<(cf_primitives::ChannelId, ForeignChainAddress), sp_runtime::DispatchError> { let (channel_id, deposit_address) = Self::get_new_deposit_address(SwapOrLp::Lp, source_asset); @@ -98,7 +95,6 @@ impl DepositApi for MockDepositHandler { deposit_address: deposit_address.clone(), source_asset, lp_account, - expiry, }); } }); @@ -112,7 +108,6 @@ impl DepositApi for MockDepositHandler { broker_commission_bps: BasisPoints, broker_id: Self::AccountId, channel_metadata: Option, - expiry: Self::BlockNumber, ) -> Result<(cf_primitives::ChannelId, ForeignChainAddress), sp_runtime::DispatchError> { let (channel_id, deposit_address) = Self::get_new_deposit_address(SwapOrLp::Swap, source_asset); @@ -129,29 +124,9 @@ impl DepositApi for MockDepositHandler { broker_commission_bps, broker_id, channel_metadata, - expiry, }); }; }); Ok((channel_id, deposit_address)) } - - fn expire_channel(address: ::ChainAccount) { - ::mutate_value( - b"SWAP_INGRESS_CHANNELS", - |storage: &mut Option>>| { - if let Some(inner) = storage.as_mut() { - inner.retain(|x| x.deposit_address != address.clone().into()); - } - }, - ); - ::mutate_value( - b"LP_INGRESS_CHANNELS", - |storage: &mut Option>>| { - if let Some(inner) = storage.as_mut() { - inner.retain(|x| x.deposit_address != address.clone().into()); - } - }, - ); - } } From b7df944c37e6cd254d7db4e1226c6b80636efc76 Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 08:37:51 +0200 Subject: [PATCH 02/19] chore: use genesis --- state-chain/pallets/cf-ingress-egress/src/mock.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/mock.rs b/state-chain/pallets/cf-ingress-egress/src/mock.rs index e59f979b58..4f4ea2de35 100644 --- a/state-chain/pallets/cf-ingress-egress/src/mock.rs +++ b/state-chain/pallets/cf-ingress-egress/src/mock.rs @@ -115,17 +115,12 @@ impl crate::Config for Test { pub const ALICE: ::AccountId = 123u64; pub const BROKER: ::AccountId = 456u64; -// TODO: Use genesis config here? -// Configure a mock runtime to test the pallet. impl_test_helpers! { Test, RuntimeGenesisConfig { system: Default::default(), - ingress_egress: Default::default(), - }, - || { - DepositChannelLifetime::::put(100); - }, + ingress_egress: IngressEgressConfig { deposit_channel_lifetime: 100 }, + } } type TestChainAccount = <::TargetChain as Chain>::ChainAccount; From b488488bc1d6e91a9a6deaa7b419f45e1e128b47 Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 08:54:43 +0200 Subject: [PATCH 03/19] feat: CFE filers based on expires_at --- .../chunked_by_vault/deposit_addresses.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/src/witness/common/chunked_chain_source/chunked_by_vault/deposit_addresses.rs b/engine/src/witness/common/chunked_chain_source/chunked_by_vault/deposit_addresses.rs index f943ed77ee..a182c9353f 100644 --- a/engine/src/witness/common/chunked_chain_source/chunked_by_vault/deposit_addresses.rs +++ b/engine/src/witness/common/chunked_chain_source/chunked_by_vault/deposit_addresses.rs @@ -58,7 +58,11 @@ where // FOr a given header we only witness addresses opened at or before the header, the set of // addresses each engine attempts to witness at a given block is consistent fn addresses_for_header(index: Inner::Index, addresses: &Addresses) -> Addresses { - addresses.iter().filter(|details| details.opened_at <= index).cloned().collect() + addresses + .iter() + .filter(|details| details.opened_at <= index && index <= details.expires_at) + .cloned() + .collect() } async fn get_chain_state_and_addresses( From 103c2b6e5bf37700d7e67dc1a1963b675c31e7b9 Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 10:49:11 +0200 Subject: [PATCH 04/19] chore: add weight calculations for on_idle --- .../pallets/cf-ingress-egress/src/lib.rs | 19 ++++++++++++++++--- .../pallets/cf-ingress-egress/src/mock.rs | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index bd653da6af..3a8c2e0393 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -10,6 +10,7 @@ mod mock; #[cfg(test)] mod tests; pub mod weights; +use frame_support::sp_runtime::SaturatedConversion; pub use weights::WeightInfo; use cf_chains::{ @@ -400,7 +401,20 @@ pub mod pallet { let current_target_chain_height = T::ChainTracking::get_block_height(); let channel_lifetime = DepositChannelLifetime::::get(); - for (_, details) in DepositChannelLookup::::iter() { + let read_write_weight = + frame_support::weights::constants::RocksDbWeight::get().reads_writes(1, 1); + + let number_to_recycle = remaining_weight + .ref_time() + .checked_div(read_write_weight.ref_time()) + .unwrap_or_default() + .saturated_into::(); + + let mut number_recycled = 0; + for (_, details) in + DepositChannelLookup::::iter().take(number_to_recycle as usize) + { + number_recycled += 1; // We add an extra lifetime of safety. // The CFEs will stop witnessing the address of this deposit channel at the // expires_at block number. However, because the CFE uses a safety margin, and here @@ -418,8 +432,7 @@ pub mod pallet { } } - // TODO: return the actual weight - remaining_weight + read_write_weight.saturating_mul(number_recycled) } /// Take all scheduled Egress and send them out diff --git a/state-chain/pallets/cf-ingress-egress/src/mock.rs b/state-chain/pallets/cf-ingress-egress/src/mock.rs index 4f4ea2de35..322311fb95 100644 --- a/state-chain/pallets/cf-ingress-egress/src/mock.rs +++ b/state-chain/pallets/cf-ingress-egress/src/mock.rs @@ -1,5 +1,5 @@ pub use crate::{self as pallet_cf_ingress_egress}; -use crate::{DepositBalances, DepositChannelLifetime, DepositWitness}; +use crate::{DepositBalances, DepositWitness}; pub use cf_chains::{ address::{AddressDerivationApi, ForeignChainAddress}, From c742fcbb7367839dca89fe5f82b630763095d81d Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 10:59:26 +0200 Subject: [PATCH 05/19] chore: clean up some comments --- state-chain/pallets/cf-ingress-egress/src/lib.rs | 2 -- state-chain/pallets/cf-ingress-egress/src/tests.rs | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 3a8c2e0393..29dec84af6 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -627,7 +627,6 @@ impl, I: 'static> Pallet { .collect() }); - // Returns Ok(()) if there's nothing to send. if batch_to_send.is_empty() { return TransactionOutcome::Commit(Ok(())) } @@ -638,7 +637,6 @@ impl, I: 'static> Pallet { let mut addresses = vec![]; for request in batch_to_send { - // We need to pull out what we need above. match request { FetchOrTransfer::::Fetch { asset, diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index cd359594fc..83ddd7e456 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -886,7 +886,6 @@ fn channel_reuse_with_different_assets() { asset ); }) - // move forward expired blocks .then_execute_at_next_block(|(_, channel_id, _)| { let recycle_block = BlockHeightProvider::::get_block_height() + DepositChannelLifetime::::get() * 2; @@ -919,8 +918,8 @@ fn channel_reuse_with_different_assets() { /// This is the sequence we're testing. /// 1. Request deposit address -/// 2. Deposit to address when it's almost expired -/// 3. The channel is expired +/// 2. Deposit to address when it's almost recycled +/// 3. The channel is recycled /// 4. We need to finalise the ingress, by fetching /// 5. The fetch should succeed. #[test] From 6980f04b7b015e70aea425c1cda5bb141942e361 Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 11:43:59 +0200 Subject: [PATCH 06/19] chore: add genesis config --- state-chain/node/src/chain_spec.rs | 35 +++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index b698fba6e2..b1abbd455c 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -22,12 +22,13 @@ use sp_core::{ }; use state_chain_runtime::{ chainflip::Offence, opaque::SessionKeys, AccountId, AccountRolesConfig, AuraConfig, - BitcoinChainTrackingConfig, BitcoinThresholdSignerConfig, BitcoinVaultConfig, BlockNumber, - EmissionsConfig, EnvironmentConfig, EthereumChainTrackingConfig, EthereumThresholdSignerConfig, + BitcoinChainTrackingConfig, BitcoinIngressEgressConfig, BitcoinThresholdSignerConfig, + BitcoinVaultConfig, BlockNumber, EmissionsConfig, EnvironmentConfig, + EthereumChainTrackingConfig, EthereumIngressEgressConfig, EthereumThresholdSignerConfig, EthereumVaultConfig, FlipBalance, FlipConfig, FundingConfig, GovernanceConfig, GrandpaConfig, - PolkadotChainTrackingConfig, PolkadotThresholdSignerConfig, PolkadotVaultConfig, - ReputationConfig, RuntimeGenesisConfig, SessionConfig, Signature, SwappingConfig, SystemConfig, - ValidatorConfig, WASM_BINARY, + PolkadotChainTrackingConfig, PolkadotIngressEgressConfig, PolkadotThresholdSignerConfig, + PolkadotVaultConfig, ReputationConfig, RuntimeGenesisConfig, SessionConfig, Signature, + SwappingConfig, SystemConfig, ValidatorConfig, WASM_BINARY, }; use std::{ @@ -269,6 +270,10 @@ pub fn cf_development_config() -> Result { common::THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, common::MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, + // Bitcoin block times on localnets are much faster, so we account for that here. + 500, + 500, + 1200, ) }, // Bootnodes @@ -391,6 +396,10 @@ macro_rules! network_spec { THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, + // deposit channel lifetimes + 12, + 500, + 1200, ) }, // Bootnodes @@ -445,6 +454,9 @@ fn testnet_genesis( threshold_signature_ceremony_timeout_blocks: BlockNumber, minimum_swap_amounts: Vec<(assets::any::Asset, AssetAmount)>, dot_runtime_version: RuntimeVersion, + bitcoin_deposit_channel_lifetime: u32, + ethereum_deposit_channel_lifetime: u32, + polkadot_deposit_channel_lifetime: u32, ) -> RuntimeGenesisConfig { // Sanity Checks for (account_id, aura_id, grandpa_id) in initial_authorities.iter() { @@ -648,9 +660,16 @@ fn testnet_genesis( transaction_payment: Default::default(), liquidity_pools: Default::default(), swapping: SwappingConfig { minimum_swap_amounts, _phantom: PhantomData }, - bitcoin_ingress_egress: Default::default(), - ethereum_ingress_egress: Default::default(), - polkadot_ingress_egress: Default::default(), + // These are set to ~2 hours at average block times. + bitcoin_ingress_egress: BitcoinIngressEgressConfig { + deposit_channel_lifetime: bitcoin_deposit_channel_lifetime.into(), + }, + ethereum_ingress_egress: EthereumIngressEgressConfig { + deposit_channel_lifetime: ethereum_deposit_channel_lifetime.into(), + }, + polkadot_ingress_egress: PolkadotIngressEgressConfig { + deposit_channel_lifetime: polkadot_deposit_channel_lifetime.into(), + }, } } From dbed6f487e868dd496c252888bbf259a70c9cb20 Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 16:07:11 +0200 Subject: [PATCH 07/19] chore: use constants for expiry blocks --- state-chain/node/src/chain_spec.rs | 33 +++++++++++++++------------- state-chain/runtime/src/constants.rs | 14 ++++++++++++ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index b1abbd455c..d62d563c11 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -21,14 +21,18 @@ use sp_core::{ sr25519, Pair, Public, }; use state_chain_runtime::{ - chainflip::Offence, opaque::SessionKeys, AccountId, AccountRolesConfig, AuraConfig, - BitcoinChainTrackingConfig, BitcoinIngressEgressConfig, BitcoinThresholdSignerConfig, - BitcoinVaultConfig, BlockNumber, EmissionsConfig, EnvironmentConfig, - EthereumChainTrackingConfig, EthereumIngressEgressConfig, EthereumThresholdSignerConfig, - EthereumVaultConfig, FlipBalance, FlipConfig, FundingConfig, GovernanceConfig, GrandpaConfig, - PolkadotChainTrackingConfig, PolkadotIngressEgressConfig, PolkadotThresholdSignerConfig, - PolkadotVaultConfig, ReputationConfig, RuntimeGenesisConfig, SessionConfig, Signature, - SwappingConfig, SystemConfig, ValidatorConfig, WASM_BINARY, + chainflip::Offence, + constants::common::{ + DEV_BITCOIN_EXPIRY_BLOCKS, DEV_ETHEREUM_EXPIRY_BLOCKS, DEV_POLKADOT_EXPIRY_BLOCKS, + }, + opaque::SessionKeys, + AccountId, AccountRolesConfig, AuraConfig, BitcoinChainTrackingConfig, + BitcoinIngressEgressConfig, BitcoinThresholdSignerConfig, BitcoinVaultConfig, BlockNumber, + EmissionsConfig, EnvironmentConfig, EthereumChainTrackingConfig, EthereumIngressEgressConfig, + EthereumThresholdSignerConfig, EthereumVaultConfig, FlipBalance, FlipConfig, FundingConfig, + GovernanceConfig, GrandpaConfig, PolkadotChainTrackingConfig, PolkadotIngressEgressConfig, + PolkadotThresholdSignerConfig, PolkadotVaultConfig, ReputationConfig, RuntimeGenesisConfig, + SessionConfig, Signature, SwappingConfig, SystemConfig, ValidatorConfig, WASM_BINARY, }; use std::{ @@ -271,9 +275,9 @@ pub fn cf_development_config() -> Result { common::MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, // Bitcoin block times on localnets are much faster, so we account for that here. - 500, - 500, - 1200, + DEV_BITCOIN_EXPIRY_BLOCKS, + DEV_ETHEREUM_EXPIRY_BLOCKS, + DEV_POLKADOT_EXPIRY_BLOCKS, ) }, // Bootnodes @@ -396,10 +400,9 @@ macro_rules! network_spec { THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, - // deposit channel lifetimes - 12, - 500, - 1200, + MAINNET_BITCOIN_EXPIRY_BLOCKS, + MAINNET_ETHEREUM_EXPIRY_BLOCKS, + MAINNET_POLKADOT_EXPIRY_BLOCKS, ) }, // Bootnodes diff --git a/state-chain/runtime/src/constants.rs b/state-chain/runtime/src/constants.rs index 4edad8d06b..ff26b65d96 100644 --- a/state-chain/runtime/src/constants.rs +++ b/state-chain/runtime/src/constants.rs @@ -101,6 +101,20 @@ pub mod common { /// See https://github.com/chainflip-io/chainflip-backend/issues/1629 pub const TX_FEE_MULTIPLIER: FlipBalance = 10_000; + /// Deposit channel expiries. This is how many external chain blocks after creation can a + /// deposit channel be witnessed. + // These represent approximately 2 hours on localnet block times + // Bitcoin blocks are 5 seconds on localnets. + pub const MAINNET_BITCOIN_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / (10 * 60); + pub const MAINNET_ETHEREUM_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 14; + pub const MAINNET_POLKADOT_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 6; + + // These represent approximately 10 minutes in localnet block times + // Bitcoin blocks are 5 seconds on localnets. + pub const DEV_BITCOIN_EXPIRY_BLOCKS: u32 = 10 * 60 / 5; + pub const DEV_ETHEREUM_EXPIRY_BLOCKS: u32 = 10 * 60 / 14; + pub const DEV_POLKADOT_EXPIRY_BLOCKS: u32 = 10 * 60 / 6; + /// Default supply update interval is 24 hours. pub mod eth { From 67048006312846bb1142cfc52153bd8c5c6247f4 Mon Sep 17 00:00:00 2001 From: kylezs Date: Thu, 21 Sep 2023 16:15:44 +0200 Subject: [PATCH 08/19] chore: use log_or_panic --- .../pallets/cf-ingress-egress/src/lib.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 29dec84af6..eaccab31f6 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -10,6 +10,7 @@ mod mock; #[cfg(test)] mod tests; pub mod weights; +use cf_runtime_utilities::log_or_panic; use frame_support::sp_runtime::SaturatedConversion; pub use weights::WeightInfo; @@ -744,12 +745,16 @@ impl, I: 'static> Pallet { let deposit_channel_details = DepositChannelLookup::::get(&deposit_address) .ok_or(Error::::InvalidDepositAddress)?; - // The address should not be used if it's being recycled - ensure!( - DepositChannelPool::::get(&deposit_channel_details.deposit_channel.channel_id) - .is_none(), - Error::::InvalidDepositAddress - ); + if DepositChannelPool::::get(&deposit_channel_details.deposit_channel.channel_id) + .is_some() + { + log_or_panic!( + "Deposit channel {} should not be in the recycled address pool if it's active", + deposit_channel_details.deposit_channel.channel_id + ); + #[cfg(not(debug_assertions))] + return Err(Error::::InvalidDepositAddress.into()) + } ensure!( deposit_channel_details.deposit_channel.asset == asset, From 4a8d6702c07f146d95f874c9d3084791de71e606 Mon Sep 17 00:00:00 2001 From: kylezs Date: Fri, 22 Sep 2023 11:47:21 +0200 Subject: [PATCH 09/19] fix: use correct expiry --- .../pallets/cf-ingress-egress/src/lib.rs | 83 +++++++++++++----- .../pallets/cf-ingress-egress/src/tests.rs | 86 ++++++++++++++----- 2 files changed, 127 insertions(+), 42 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index eaccab31f6..4cb5be42f7 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -332,6 +332,10 @@ pub mod pallet { pub type DepositBalances, I: 'static = ()> = StorageMap<_, Twox64Concat, TargetChainAsset, DepositTracker, ValueQuery>; + #[pallet::storage] + pub type DepositChannelRecycleBlocks, I: 'static = ()> = + StorageValue<_, Vec<(TargetChainBlockNumber, TargetChainAccount)>, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { @@ -399,31 +403,23 @@ pub mod pallet { impl, I: 'static> Hooks> for Pallet { /// Recycle addresses if we can fn on_idle(_n: BlockNumberFor, remaining_weight: Weight) -> Weight { - let current_target_chain_height = T::ChainTracking::get_block_height(); - let channel_lifetime = DepositChannelLifetime::::get(); - let read_write_weight = frame_support::weights::constants::RocksDbWeight::get().reads_writes(1, 1); - let number_to_recycle = remaining_weight + let maximum_recycle_number = remaining_weight .ref_time() .checked_div(read_write_weight.ref_time()) .unwrap_or_default() .saturated_into::(); - let mut number_recycled = 0; - for (_, details) in - DepositChannelLookup::::iter().take(number_to_recycle as usize) - { - number_recycled += 1; - // We add an extra lifetime of safety. - // The CFEs will stop witnessing the address of this deposit channel at the - // expires_at block number. However, because the CFE uses a safety margin, and here - // we're using teh ChainTracking block number, we don't want to expire - // this too early. Even accounting for the safety margin, there is potential for - // latency, full SC blocks which might block ingress witnesses getting in etc. By - // having this buffer we protect against this probabilistically. - if details.expires_at + channel_lifetime <= current_target_chain_height { + let (can_recycle, cannot_recycle) = Self::can_and_cannot_recycle( + maximum_recycle_number, + DepositChannelRecycleBlocks::::take(), + T::ChainTracking::get_block_height(), + ); + + for address in can_recycle.iter() { + if let Some(details) = DepositChannelLookup::::take(address) { if let Some(state) = details.deposit_channel.state.maybe_recycle() { DepositChannelPool::::insert( details.deposit_channel.channel_id, @@ -433,7 +429,9 @@ pub mod pallet { } } - read_write_weight.saturating_mul(number_recycled) + DepositChannelRecycleBlocks::::put(cannot_recycle); + + read_write_weight.saturating_mul(can_recycle.len() as u64) } /// Take all scheduled Egress and send them out @@ -585,6 +583,34 @@ pub mod pallet { } impl, I: 'static> Pallet { + fn can_and_cannot_recycle( + maximum_recyclable_number: usize, + channel_recycle_blocks: Vec<(TargetChainBlockNumber, TargetChainAccount)>, + current_block_height: TargetChainBlockNumber, + ) -> ( + Vec>, + Vec<(TargetChainBlockNumber, TargetChainAccount)>, + ) { + let partition_point = + channel_recycle_blocks.partition_point(|(block, _)| *block <= current_block_height); + let (ready_to_recycle, not_ready_to_recycle) = + channel_recycle_blocks.split_at(partition_point); + + let ready_to_recycle = ready_to_recycle.to_vec(); + + let (can_recycle, mut cannot_recycle) = + if maximum_recyclable_number < ready_to_recycle.len() { + let (can_recycle, cannot_recycle) = + ready_to_recycle.split_at(maximum_recyclable_number); + (can_recycle.to_vec(), cannot_recycle.to_vec()) + } else { + (ready_to_recycle, Vec::new()) + }; + + cannot_recycle.extend(not_ready_to_recycle.to_vec()); + (can_recycle.into_iter().map(|(_, a)| a).collect(), cannot_recycle) + } + /// Take all scheduled egress requests and send them out in an `AllBatch` call. /// /// Note: Egress transactions with Blacklisted assets are not sent, and kept in storage. @@ -608,6 +634,7 @@ impl, I: 'static> Pallet { .map(|details| { let can_fetch = details.deposit_channel.state.can_fetch(); + if can_fetch { deposit_fetch_id.replace( details.deposit_channel.fetch_id(), @@ -844,6 +871,17 @@ impl, I: 'static> Pallet { Ok(()) } + fn expiry_and_recycle_block_height( + ) -> (TargetChainBlockNumber, TargetChainBlockNumber, TargetChainBlockNumber) + { + let current_height = T::ChainTracking::get_block_height(); + let lifetime = DepositChannelLifetime::::get(); + let expiry_height = current_height + lifetime; + let recycle_height = expiry_height + lifetime; + + (current_height, expiry_height, recycle_height) + } + /// Opens a channel for the given asset and registers it with the given action. /// /// May re-use an existing deposit address, depending on chain configuration. @@ -873,14 +911,17 @@ impl, I: 'static> Pallet { let deposit_address = deposit_channel.address.clone(); - let current_target_chain_height = T::ChainTracking::get_block_height(); + let (current_height, expiry_height, recycle_height) = + Self::expiry_and_recycle_block_height(); + + DepositChannelRecycleBlocks::::append((recycle_height, deposit_address.clone())); DepositChannelLookup::::insert( &deposit_address, DepositChannelDetails { deposit_channel, - opened_at: current_target_chain_height, - expires_at: current_target_chain_height + DepositChannelLifetime::::get(), + opened_at: current_height, + expires_at: expiry_height, action, }, ); diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index 83ddd7e456..41f8cbeaf6 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -1,9 +1,8 @@ use crate::{ mock::*, Call as PalletCall, ChannelAction, ChannelIdCounter, CrossChainMessage, - DepositChannelLifetime, DepositChannelLookup, DepositChannelPool, DepositWitness, - DisabledEgressAssets, Error, Event as PalletEvent, FailedVaultTransfers, FetchOrTransfer, - MinimumDeposit, Pallet, ScheduledEgressCcm, ScheduledEgressFetchOrTransfer, TargetChainAccount, - VaultTransfer, + DepositChannelLookup, DepositChannelPool, DepositWitness, DisabledEgressAssets, Error, + Event as PalletEvent, FailedVaultTransfers, FetchOrTransfer, MinimumDeposit, Pallet, + ScheduledEgressCcm, ScheduledEgressFetchOrTransfer, TargetChainAccount, VaultTransfer, }; use cf_chains::{ address::AddressConverter, evm::EvmFetchId, mocks::MockEthereum, CcmChannelMetadata, @@ -353,8 +352,7 @@ fn addresses_are_getting_reused() { channels }) .then_execute_at_next_block(|channels| { - let recycle_block = BlockHeightProvider::::get_block_height() + - DepositChannelLifetime::::get() * 2; + let recycle_block = IngressEgress::expiry_and_recycle_block_height().2; BlockHeightProvider::::set_block_height(recycle_block); channels[0].clone() @@ -388,8 +386,7 @@ fn proof_address_pool_integrity() { for (_id, address) in channel_details { assert_ok!(IngressEgress::finalise_ingress(RuntimeOrigin::root(), vec![address])); } - let recycle_block = BlockHeightProvider::::get_block_height() + - DepositChannelLifetime::::get() * 2; + let recycle_block = IngressEgress::expiry_and_recycle_block_height().2; BlockHeightProvider::::set_block_height(recycle_block); IngressEgress::on_idle(1, Weight::MAX); @@ -412,8 +409,7 @@ fn create_new_address_while_pool_is_empty() { for (_id, address) in channel_details { assert_ok!(IngressEgress::finalise_ingress(RuntimeOrigin::root(), vec![address])); } - let recycle_block = BlockHeightProvider::::get_block_height() + - DepositChannelLifetime::::get() * 2; + let recycle_block = IngressEgress::expiry_and_recycle_block_height().2; BlockHeightProvider::::set_block_height(recycle_block); IngressEgress::on_idle(1, Weight::MAX); @@ -594,8 +590,7 @@ fn multi_use_deposit_address_different_blocks() { (), Default::default() )); - let recycle_block = BlockHeightProvider::::get_block_height() + - DepositChannelLifetime::::get() * 2; + let recycle_block = IngressEgress::expiry_and_recycle_block_height().2; BlockHeightProvider::::set_block_height(recycle_block); channel @@ -887,8 +882,7 @@ fn channel_reuse_with_different_assets() { ); }) .then_execute_at_next_block(|(_, channel_id, _)| { - let recycle_block = BlockHeightProvider::::get_block_height() + - DepositChannelLifetime::::get() * 2; + let recycle_block = IngressEgress::expiry_and_recycle_block_height().2; BlockHeightProvider::::set_block_height(recycle_block); channel_id }) @@ -918,23 +912,26 @@ fn channel_reuse_with_different_assets() { /// This is the sequence we're testing. /// 1. Request deposit address -/// 2. Deposit to address when it's almost recycled -/// 3. The channel is recycled +/// 2. Deposit to address when it's almost expired +/// 3. The channel is expired /// 4. We need to finalise the ingress, by fetching /// 5. The fetch should succeed. #[test] -fn ingress_finalisation_succeeds_after_channel_recycled() { +fn ingress_finalisation_succeeds_after_channel_expired_but_not_recycled() { new_test_ext().execute_with(|| { + assert!(ScheduledEgressFetchOrTransfer::::get().is_empty(), "Is empty after genesis"); + request_address_and_deposit(ALICE, eth::Asset::Eth); - let recycle_block = BlockHeightProvider::::get_block_height() + - DepositChannelLifetime::::get() * 2; - BlockHeightProvider::::set_block_height(recycle_block); + // Because we're only *expiring* and not recycling, we should still be able to fetch. + let expiry_block = IngressEgress::expiry_and_recycle_block_height().1; + BlockHeightProvider::::set_block_height(expiry_block); + IngressEgress::on_idle(1, Weight::MAX); IngressEgress::on_finalize(1); - assert!(ScheduledEgressFetchOrTransfer::::get().is_empty()); + assert!(ScheduledEgressFetchOrTransfer::::get().is_empty(),); }); } @@ -1025,3 +1022,50 @@ fn basic_balance_tracking() { (eth::Asset::Usdc, USDC_DEPOSIT_AMOUNT), ]); } + +#[test] +fn test_default_empty_amounts() { + let (can_recycle, cannot_recycle) = + IngressEgress::can_and_cannot_recycle(0, Default::default(), 0); + + assert_eq!(can_recycle, vec![]); + assert_eq!(cannot_recycle, vec![]); +} + +#[test] +fn test_cannot_recycle_if_block_number_less_than_current_height() { + let maximum_recyclable_number = 2; + let channel_recycle_blocks = + (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); + let current_block_height = 3; + + let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + maximum_recyclable_number, + channel_recycle_blocks, + current_block_height, + ); + + assert_eq!(can_recycle, vec![H160::from([1u8; 20]), H160::from([2; 20])]); + assert_eq!(cannot_recycle, vec![(3, H160::from([3u8; 20])), (4, H160::from([4u8; 20]))]); +} + +// Same test as above, but lower maximum recyclable number +#[test] +fn test_can_only_recycle_up_to_max_amount() { + let maximum_recyclable_number = 1; + let channel_recycle_blocks = + (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); + let current_block_height = 3; + + let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + maximum_recyclable_number, + channel_recycle_blocks, + current_block_height, + ); + + assert_eq!(can_recycle, vec![H160::from([1u8; 20])]); + assert_eq!( + cannot_recycle, + vec![(2, H160::from([2; 20])), (3, H160::from([3u8; 20])), (4, H160::from([4u8; 20]))] + ); +} From 999ec1cb28c5b2ffef2c356123cec04bd166ca5a Mon Sep 17 00:00:00 2001 From: kylezs Date: Fri, 22 Sep 2023 12:10:20 +0200 Subject: [PATCH 10/19] test: more recycle tests --- .../pallets/cf-ingress-egress/src/tests.rs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index 41f8cbeaf6..fcc23f3e6b 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -1069,3 +1069,48 @@ fn test_can_only_recycle_up_to_max_amount() { vec![(2, H160::from([2; 20])), (3, H160::from([3u8; 20])), (4, H160::from([4u8; 20]))] ); } + +#[test] +fn none_can_be_recycled_due_to_low_block_number() { + let maximum_recyclable_number = 4; + let channel_recycle_blocks = + (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); + let current_block_height = 0; + + let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + maximum_recyclable_number, + channel_recycle_blocks, + current_block_height, + ); + + assert!(can_recycle.is_empty()); + assert_eq!( + cannot_recycle, + vec![ + (1, H160::from([1u8; 20])), + (2, H160::from([2; 20])), + (3, H160::from([3; 20])), + (4, H160::from([4; 20])) + ] + ); +} + +#[test] +fn all_can_be_recycled() { + let maximum_recyclable_number = 4; + let channel_recycle_blocks = + (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); + let current_block_height = 4; + + let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + maximum_recyclable_number, + channel_recycle_blocks, + current_block_height, + ); + + assert_eq!( + can_recycle, + vec![H160::from([1u8; 20]), H160::from([2; 20]), H160::from([3; 20]), H160::from([4; 20])] + ); + assert!(cannot_recycle.is_empty()); +} From a6354559216ce4d7ddd8bb105e6495c0cb1f9146 Mon Sep 17 00:00:00 2001 From: kylezs Date: Fri, 22 Sep 2023 13:33:21 +0200 Subject: [PATCH 11/19] chore: clippy --- state-chain/node/src/chain_spec.rs | 2 +- .../pallets/cf-ingress-egress/src/lib.rs | 26 ++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index d62d563c11..38009cfbdc 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -671,7 +671,7 @@ fn testnet_genesis( deposit_channel_lifetime: ethereum_deposit_channel_lifetime.into(), }, polkadot_ingress_egress: PolkadotIngressEgressConfig { - deposit_channel_lifetime: polkadot_deposit_channel_lifetime.into(), + deposit_channel_lifetime: polkadot_deposit_channel_lifetime, }, } } diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 4cb5be42f7..aa7c9c71fa 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -103,6 +103,9 @@ pub mod pallet { }; use sp_std::vec::Vec; + pub(crate) type RecycleAddressAtBlock = + Vec<(TargetChainBlockNumber, TargetChainAccount)>; + pub(crate) type TargetChainAsset = <>::TargetChain as Chain>::ChainAsset; pub(crate) type TargetChainAccount = <>::TargetChain as Chain>::ChainAccount; @@ -585,24 +588,17 @@ pub mod pallet { impl, I: 'static> Pallet { fn can_and_cannot_recycle( maximum_recyclable_number: usize, - channel_recycle_blocks: Vec<(TargetChainBlockNumber, TargetChainAccount)>, + channel_recycle_blocks: RecycleAddressAtBlock, current_block_height: TargetChainBlockNumber, - ) -> ( - Vec>, - Vec<(TargetChainBlockNumber, TargetChainAccount)>, - ) { - let partition_point = - channel_recycle_blocks.partition_point(|(block, _)| *block <= current_block_height); - let (ready_to_recycle, not_ready_to_recycle) = - channel_recycle_blocks.split_at(partition_point); - - let ready_to_recycle = ready_to_recycle.to_vec(); + ) -> (Vec>, RecycleAddressAtBlock) { + let (ready_to_recycle, not_ready_to_recycle) = channel_recycle_blocks + .into_iter() + .partition::, _>(|(block, _)| *block <= current_block_height); let (can_recycle, mut cannot_recycle) = if maximum_recyclable_number < ready_to_recycle.len() { - let (can_recycle, cannot_recycle) = - ready_to_recycle.split_at(maximum_recyclable_number); - (can_recycle.to_vec(), cannot_recycle.to_vec()) + let (can, cannot) = ready_to_recycle.split_at(maximum_recyclable_number); + (can.to_vec(), cannot.to_vec()) } else { (ready_to_recycle, Vec::new()) }; @@ -772,7 +768,7 @@ impl, I: 'static> Pallet { let deposit_channel_details = DepositChannelLookup::::get(&deposit_address) .ok_or(Error::::InvalidDepositAddress)?; - if DepositChannelPool::::get(&deposit_channel_details.deposit_channel.channel_id) + if DepositChannelPool::::get(deposit_channel_details.deposit_channel.channel_id) .is_some() { log_or_panic!( From c3239108bcf031c1606630293367c7a7f4eedcea Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 22 Sep 2023 15:08:59 +0200 Subject: [PATCH 12/19] chore: move constants into chainspec --- state-chain/node/src/chain_spec.rs | 63 +++++++++---------- state-chain/node/src/chain_spec/devnet.rs | 7 +++ state-chain/node/src/chain_spec/partnernet.rs | 5 +- .../node/src/chain_spec/perseverance.rs | 5 +- state-chain/node/src/chain_spec/sisyphos.rs | 5 +- state-chain/node/src/chain_spec/testnet.rs | 5 ++ state-chain/runtime/src/constants.rs | 16 ----- 7 files changed, 54 insertions(+), 52 deletions(-) create mode 100644 state-chain/node/src/chain_spec/devnet.rs diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index 38009cfbdc..0f2fb42365 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -21,18 +21,14 @@ use sp_core::{ sr25519, Pair, Public, }; use state_chain_runtime::{ - chainflip::Offence, - constants::common::{ - DEV_BITCOIN_EXPIRY_BLOCKS, DEV_ETHEREUM_EXPIRY_BLOCKS, DEV_POLKADOT_EXPIRY_BLOCKS, - }, - opaque::SessionKeys, - AccountId, AccountRolesConfig, AuraConfig, BitcoinChainTrackingConfig, - BitcoinIngressEgressConfig, BitcoinThresholdSignerConfig, BitcoinVaultConfig, BlockNumber, - EmissionsConfig, EnvironmentConfig, EthereumChainTrackingConfig, EthereumIngressEgressConfig, - EthereumThresholdSignerConfig, EthereumVaultConfig, FlipBalance, FlipConfig, FundingConfig, - GovernanceConfig, GrandpaConfig, PolkadotChainTrackingConfig, PolkadotIngressEgressConfig, - PolkadotThresholdSignerConfig, PolkadotVaultConfig, ReputationConfig, RuntimeGenesisConfig, - SessionConfig, Signature, SwappingConfig, SystemConfig, ValidatorConfig, WASM_BINARY, + chainflip::Offence, opaque::SessionKeys, AccountId, AccountRolesConfig, AuraConfig, + BitcoinChainTrackingConfig, BitcoinIngressEgressConfig, BitcoinThresholdSignerConfig, + BitcoinVaultConfig, BlockNumber, EmissionsConfig, EnvironmentConfig, + EthereumChainTrackingConfig, EthereumIngressEgressConfig, EthereumThresholdSignerConfig, + EthereumVaultConfig, FlipBalance, FlipConfig, FundingConfig, GovernanceConfig, GrandpaConfig, + PolkadotChainTrackingConfig, PolkadotIngressEgressConfig, PolkadotThresholdSignerConfig, + PolkadotVaultConfig, ReputationConfig, RuntimeGenesisConfig, SessionConfig, Signature, + SwappingConfig, SystemConfig, ValidatorConfig, WASM_BINARY, }; use std::{ @@ -50,6 +46,7 @@ use sp_runtime::{ }; pub mod common; +pub mod devnet; pub mod partnernet; pub mod perseverance; pub mod sisyphos; @@ -241,7 +238,7 @@ pub fn cf_development_config() -> Result { // Governance account - Snow White snow_white.into(), 1, - common::MAX_AUTHORITIES, + devnet::MAX_AUTHORITIES, EnvironmentConfig { flip_token_address: flip_token_address.into(), eth_usdc_address: eth_usdc_address.into(), @@ -257,27 +254,27 @@ pub fn cf_development_config() -> Result { }, eth_init_agg_key, ethereum_deployment_block, - common::TOTAL_ISSUANCE, + devnet::TOTAL_ISSUANCE, genesis_funding_amount, min_funding, - common::REDEMPTION_TAX, - 8 * common::HOURS, - common::REDEMPTION_DELAY_SECS, - common::CURRENT_AUTHORITY_EMISSION_INFLATION_PERBILL, - common::BACKUP_NODE_EMISSION_INFLATION_PERBILL, - common::EXPIRY_SPAN_IN_SECONDS, - common::ACCRUAL_RATIO, - Percent::from_percent(common::REDEMPTION_PERIOD_AS_PERCENTAGE), - common::SUPPLY_UPDATE_INTERVAL, - common::PENALTIES.to_vec(), - common::KEYGEN_CEREMONY_TIMEOUT_BLOCKS, - common::THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, - common::MINIMUM_SWAP_AMOUNTS.to_vec(), + devnet::REDEMPTION_TAX, + 8 * devnet::HOURS, + devnet::REDEMPTION_DELAY_SECS, + devnet::CURRENT_AUTHORITY_EMISSION_INFLATION_PERBILL, + devnet::BACKUP_NODE_EMISSION_INFLATION_PERBILL, + devnet::EXPIRY_SPAN_IN_SECONDS, + devnet::ACCRUAL_RATIO, + Percent::from_percent(devnet::REDEMPTION_PERIOD_AS_PERCENTAGE), + devnet::SUPPLY_UPDATE_INTERVAL, + devnet::PENALTIES.to_vec(), + devnet::KEYGEN_CEREMONY_TIMEOUT_BLOCKS, + devnet::THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, + devnet::MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, // Bitcoin block times on localnets are much faster, so we account for that here. - DEV_BITCOIN_EXPIRY_BLOCKS, - DEV_ETHEREUM_EXPIRY_BLOCKS, - DEV_POLKADOT_EXPIRY_BLOCKS, + devnet::BITCOIN_EXPIRY_BLOCKS, + devnet::ETHEREUM_EXPIRY_BLOCKS, + devnet::POLKADOT_EXPIRY_BLOCKS, ) }, // Bootnodes @@ -400,9 +397,9 @@ macro_rules! network_spec { THRESHOLD_SIGNATURE_CEREMONY_TIMEOUT_BLOCKS, MINIMUM_SWAP_AMOUNTS.to_vec(), dot_runtime_version, - MAINNET_BITCOIN_EXPIRY_BLOCKS, - MAINNET_ETHEREUM_EXPIRY_BLOCKS, - MAINNET_POLKADOT_EXPIRY_BLOCKS, + BITCOIN_EXPIRY_BLOCKS, + ETHEREUM_EXPIRY_BLOCKS, + POLKADOT_EXPIRY_BLOCKS, ) }, // Bootnodes diff --git a/state-chain/node/src/chain_spec/devnet.rs b/state-chain/node/src/chain_spec/devnet.rs new file mode 100644 index 0000000000..91257d5f82 --- /dev/null +++ b/state-chain/node/src/chain_spec/devnet.rs @@ -0,0 +1,7 @@ +pub use super::common::*; + +// These represent approximately 10 minutes in localnet block times +// Bitcoin blocks are 5 seconds on localnets. +pub const BITCOIN_EXPIRY_BLOCKS: u32 = 10 * 60 / 5; +pub const ETHEREUM_EXPIRY_BLOCKS: u32 = 10 * 60 / 14; +pub const POLKADOT_EXPIRY_BLOCKS: u32 = 10 * 60 / 6; diff --git a/state-chain/node/src/chain_spec/partnernet.rs b/state-chain/node/src/chain_spec/partnernet.rs index 49aadb28df..c7878c8497 100644 --- a/state-chain/node/src/chain_spec/partnernet.rs +++ b/state-chain/node/src/chain_spec/partnernet.rs @@ -1,5 +1,8 @@ -pub use super::common::*; use super::StateChainEnvironment; +pub use super::{ + common::*, + testnet::{BITCOIN_EXPIRY_BLOCKS, ETHEREUM_EXPIRY_BLOCKS, POLKADOT_EXPIRY_BLOCKS}, +}; use cf_chains::{dot::RuntimeVersion, eth::CHAIN_ID_GOERLI}; use cf_primitives::{AccountId, AccountRole, BlockNumber, FlipBalance, NetworkEnvironment}; use sc_service::ChainType; diff --git a/state-chain/node/src/chain_spec/perseverance.rs b/state-chain/node/src/chain_spec/perseverance.rs index 0c45e1052f..e8f99ac836 100644 --- a/state-chain/node/src/chain_spec/perseverance.rs +++ b/state-chain/node/src/chain_spec/perseverance.rs @@ -1,4 +1,7 @@ -pub use super::common::*; +pub use super::{ + common::*, + testnet::{BITCOIN_EXPIRY_BLOCKS, ETHEREUM_EXPIRY_BLOCKS, POLKADOT_EXPIRY_BLOCKS}, +}; use super::{parse_account, StateChainEnvironment}; use cf_chains::{dot::RuntimeVersion, eth::CHAIN_ID_GOERLI}; use cf_primitives::{AccountId, AccountRole, BlockNumber, FlipBalance, NetworkEnvironment}; diff --git a/state-chain/node/src/chain_spec/sisyphos.rs b/state-chain/node/src/chain_spec/sisyphos.rs index ef69392f3a..d910d0bb0f 100644 --- a/state-chain/node/src/chain_spec/sisyphos.rs +++ b/state-chain/node/src/chain_spec/sisyphos.rs @@ -1,5 +1,8 @@ -pub use super::common::*; use super::StateChainEnvironment; +pub use super::{ + common::*, + testnet::{BITCOIN_EXPIRY_BLOCKS, ETHEREUM_EXPIRY_BLOCKS, POLKADOT_EXPIRY_BLOCKS}, +}; use cf_chains::{dot::RuntimeVersion, eth::CHAIN_ID_GOERLI}; use cf_primitives::{AccountId, AccountRole, BlockNumber, FlipBalance, NetworkEnvironment}; use sc_service::ChainType; diff --git a/state-chain/node/src/chain_spec/testnet.rs b/state-chain/node/src/chain_spec/testnet.rs index 178e67622b..b95779814a 100644 --- a/state-chain/node/src/chain_spec/testnet.rs +++ b/state-chain/node/src/chain_spec/testnet.rs @@ -12,6 +12,11 @@ pub const CHAIN_TYPE: ChainType = ChainType::Development; pub const NETWORK_ENVIRONMENT: NetworkEnvironment = NetworkEnvironment::Development; pub const PROTOCOL_ID: &str = "flip-test"; +// These represent approximately 2 hours on testnet block times +pub const BITCOIN_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / (10 * 60); +pub const ETHEREUM_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 14; +pub const POLKADOT_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 6; + pub const ENV: StateChainEnvironment = StateChainEnvironment { flip_token_address: hex_literal::hex!("Cf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"), eth_usdc_address: hex_literal::hex!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), diff --git a/state-chain/runtime/src/constants.rs b/state-chain/runtime/src/constants.rs index ff26b65d96..ba49101312 100644 --- a/state-chain/runtime/src/constants.rs +++ b/state-chain/runtime/src/constants.rs @@ -101,22 +101,6 @@ pub mod common { /// See https://github.com/chainflip-io/chainflip-backend/issues/1629 pub const TX_FEE_MULTIPLIER: FlipBalance = 10_000; - /// Deposit channel expiries. This is how many external chain blocks after creation can a - /// deposit channel be witnessed. - // These represent approximately 2 hours on localnet block times - // Bitcoin blocks are 5 seconds on localnets. - pub const MAINNET_BITCOIN_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / (10 * 60); - pub const MAINNET_ETHEREUM_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 14; - pub const MAINNET_POLKADOT_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 6; - - // These represent approximately 10 minutes in localnet block times - // Bitcoin blocks are 5 seconds on localnets. - pub const DEV_BITCOIN_EXPIRY_BLOCKS: u32 = 10 * 60 / 5; - pub const DEV_ETHEREUM_EXPIRY_BLOCKS: u32 = 10 * 60 / 14; - pub const DEV_POLKADOT_EXPIRY_BLOCKS: u32 = 10 * 60 / 6; - - /// Default supply update interval is 24 hours. - pub mod eth { use cf_chains::{eth::Ethereum, Chain}; From e19d25661962f98162da5ed97c656392d622e9e0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 22 Sep 2023 16:22:36 +0200 Subject: [PATCH 13/19] fix: use mutate, rename --- .../pallets/cf-ingress-egress/src/lib.rs | 44 ++++++++----------- .../pallets/cf-ingress-egress/src/tests.rs | 41 +++++++++-------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index aa7c9c71fa..772942567a 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -103,7 +103,7 @@ pub mod pallet { }; use sp_std::vec::Vec; - pub(crate) type RecycleAddressAtBlock = + pub(crate) type ChannelRecycleQueue = Vec<(TargetChainBlockNumber, TargetChainAccount)>; pub(crate) type TargetChainAsset = <>::TargetChain as Chain>::ChainAsset; @@ -337,7 +337,7 @@ pub mod pallet { #[pallet::storage] pub type DepositChannelRecycleBlocks, I: 'static = ()> = - StorageValue<_, Vec<(TargetChainBlockNumber, TargetChainAccount)>, ValueQuery>; + StorageValue<_, ChannelRecycleQueue, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -415,11 +415,13 @@ pub mod pallet { .unwrap_or_default() .saturated_into::(); - let (can_recycle, cannot_recycle) = Self::can_and_cannot_recycle( - maximum_recycle_number, - DepositChannelRecycleBlocks::::take(), - T::ChainTracking::get_block_height(), - ); + let can_recycle = DepositChannelRecycleBlocks::::mutate(|recycle_queue| { + Self::can_and_cannot_recycle( + recycle_queue, + maximum_recycle_number, + T::ChainTracking::get_block_height(), + ) + }); for address in can_recycle.iter() { if let Some(details) = DepositChannelLookup::::take(address) { @@ -432,8 +434,6 @@ pub mod pallet { } } - DepositChannelRecycleBlocks::::put(cannot_recycle); - read_write_weight.saturating_mul(can_recycle.len() as u64) } @@ -587,24 +587,18 @@ pub mod pallet { impl, I: 'static> Pallet { fn can_and_cannot_recycle( + channel_recycle_blocks: &mut ChannelRecycleQueue, maximum_recyclable_number: usize, - channel_recycle_blocks: RecycleAddressAtBlock, current_block_height: TargetChainBlockNumber, - ) -> (Vec>, RecycleAddressAtBlock) { - let (ready_to_recycle, not_ready_to_recycle) = channel_recycle_blocks - .into_iter() - .partition::, _>(|(block, _)| *block <= current_block_height); - - let (can_recycle, mut cannot_recycle) = - if maximum_recyclable_number < ready_to_recycle.len() { - let (can, cannot) = ready_to_recycle.split_at(maximum_recyclable_number); - (can.to_vec(), cannot.to_vec()) - } else { - (ready_to_recycle, Vec::new()) - }; - - cannot_recycle.extend(not_ready_to_recycle.to_vec()); - (can_recycle.into_iter().map(|(_, a)| a).collect(), cannot_recycle) + ) -> Vec> { + let partition_point = sp_std::cmp::min( + channel_recycle_blocks.partition_point(|(block, _)| *block <= current_block_height), + maximum_recyclable_number, + ); + channel_recycle_blocks + .drain(..partition_point) + .map(|(_, address)| address) + .collect() } /// Take all scheduled egress requests and send them out in an `AllBatch` call. diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index fcc23f3e6b..de0e03a7d2 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -1025,47 +1025,50 @@ fn basic_balance_tracking() { #[test] fn test_default_empty_amounts() { - let (can_recycle, cannot_recycle) = - IngressEgress::can_and_cannot_recycle(0, Default::default(), 0); + let mut channel_recycle_blocks = Default::default(); + let can_recycle = IngressEgress::can_and_cannot_recycle(&mut channel_recycle_blocks, 0, 0); assert_eq!(can_recycle, vec![]); - assert_eq!(cannot_recycle, vec![]); + assert_eq!(channel_recycle_blocks, vec![]); } #[test] fn test_cannot_recycle_if_block_number_less_than_current_height() { let maximum_recyclable_number = 2; - let channel_recycle_blocks = + let mut channel_recycle_blocks = (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); let current_block_height = 3; - let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + let can_recycle = IngressEgress::can_and_cannot_recycle( + &mut channel_recycle_blocks, maximum_recyclable_number, - channel_recycle_blocks, current_block_height, ); assert_eq!(can_recycle, vec![H160::from([1u8; 20]), H160::from([2; 20])]); - assert_eq!(cannot_recycle, vec![(3, H160::from([3u8; 20])), (4, H160::from([4u8; 20]))]); + assert_eq!( + channel_recycle_blocks, + vec![(3, H160::from([3u8; 20])), (4, H160::from([4u8; 20]))] + ); } // Same test as above, but lower maximum recyclable number #[test] fn test_can_only_recycle_up_to_max_amount() { let maximum_recyclable_number = 1; - let channel_recycle_blocks = + let mut channel_recycle_blocks = (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); let current_block_height = 3; - let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + let can_recycle = IngressEgress::can_and_cannot_recycle( + &mut channel_recycle_blocks, maximum_recyclable_number, - channel_recycle_blocks, current_block_height, ); assert_eq!(can_recycle, vec![H160::from([1u8; 20])]); assert_eq!( - cannot_recycle, + channel_recycle_blocks, vec![(2, H160::from([2; 20])), (3, H160::from([3u8; 20])), (4, H160::from([4u8; 20]))] ); } @@ -1073,19 +1076,19 @@ fn test_can_only_recycle_up_to_max_amount() { #[test] fn none_can_be_recycled_due_to_low_block_number() { let maximum_recyclable_number = 4; - let channel_recycle_blocks = + let mut channel_recycle_blocks = (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); let current_block_height = 0; - let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + let can_recycle = IngressEgress::can_and_cannot_recycle( + &mut channel_recycle_blocks, maximum_recyclable_number, - channel_recycle_blocks, current_block_height, ); assert!(can_recycle.is_empty()); assert_eq!( - cannot_recycle, + channel_recycle_blocks, vec![ (1, H160::from([1u8; 20])), (2, H160::from([2; 20])), @@ -1098,13 +1101,13 @@ fn none_can_be_recycled_due_to_low_block_number() { #[test] fn all_can_be_recycled() { let maximum_recyclable_number = 4; - let channel_recycle_blocks = + let mut channel_recycle_blocks = (1u64..5).map(|i| (i, H160::from([i as u8; 20]))).collect::>(); let current_block_height = 4; - let (can_recycle, cannot_recycle) = IngressEgress::can_and_cannot_recycle( + let can_recycle = IngressEgress::can_and_cannot_recycle( + &mut channel_recycle_blocks, maximum_recyclable_number, - channel_recycle_blocks, current_block_height, ); @@ -1112,5 +1115,5 @@ fn all_can_be_recycled() { can_recycle, vec![H160::from([1u8; 20]), H160::from([2; 20]), H160::from([3; 20]), H160::from([4; 20])] ); - assert!(cannot_recycle.is_empty()); + assert!(channel_recycle_blocks.is_empty()); } From 6c68be5dbe103582c7529e189d45cbf8dea7d2fd Mon Sep 17 00:00:00 2001 From: kylezs Date: Mon, 25 Sep 2023 11:54:25 +0200 Subject: [PATCH 14/19] chore: ingress-egress pallet migrations --- Cargo.lock | 1 + state-chain/pallets/cf-broadcast/src/lib.rs | 2 + .../pallets/cf-ingress-egress/Cargo.toml | 7 +- .../pallets/cf-ingress-egress/src/lib.rs | 4 + .../cf-ingress-egress/src/migrations.rs | 6 + .../src/migrations/ingress_expiry.rs | 119 ++++++++++++++++++ 6 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 state-chain/pallets/cf-ingress-egress/src/migrations.rs create mode 100644 state-chain/pallets/cf-ingress-egress/src/migrations/ingress_expiry.rs diff --git a/Cargo.lock b/Cargo.lock index 49d92053d8..b5590f0b2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7185,6 +7185,7 @@ version = "0.1.0" dependencies = [ "cf-chains", "cf-primitives", + "cf-runtime-upgrade-utilities", "cf-runtime-utilities", "cf-test-utilities", "cf-traits", diff --git a/state-chain/pallets/cf-broadcast/src/lib.rs b/state-chain/pallets/cf-broadcast/src/lib.rs index 781f1ca971..d787a65b1d 100644 --- a/state-chain/pallets/cf-broadcast/src/lib.rs +++ b/state-chain/pallets/cf-broadcast/src/lib.rs @@ -243,6 +243,8 @@ pub mod pallet { >; /// Lookup table between TransactionOutId -> Broadcast. + /// This storage item is used by the CFE to track which broadcasts/egresses it needs to + /// witness. #[pallet::storage] pub type TransactionOutIdToBroadcastId, I: 'static = ()> = StorageMap< _, diff --git a/state-chain/pallets/cf-ingress-egress/Cargo.toml b/state-chain/pallets/cf-ingress-egress/Cargo.toml index 1ab44ee32d..933bbcde12 100644 --- a/state-chain/pallets/cf-ingress-egress/Cargo.toml +++ b/state-chain/pallets/cf-ingress-egress/Cargo.toml @@ -17,6 +17,7 @@ cf-traits = { path = '../../traits', default-features = false } cf-runtime-utilities = { path = '../../runtime-utilities', default-features = false, features = [ 'derive', ] } +cf-runtime-upgrade-utilities = { path = '../../runtime-upgrade-utilities', default-features = false } log = { version = '0.4.16', default-features = false } @@ -47,6 +48,7 @@ std = [ 'cf-chains/std', 'cf-primitives/std', 'cf-traits/std', + 'cf-runtime-upgrade-utilities/std', 'codec/std', 'frame-benchmarking?/std', 'frame-support/std', @@ -64,4 +66,7 @@ runtime-benchmarks = [ 'frame-system/runtime-benchmarks', 'pallet-cf-governance/runtime-benchmarks', ] -try-runtime = ['frame-support/try-runtime'] +try-runtime = [ + 'frame-support/try-runtime', + 'cf-runtime-upgrade-utilities/try-runtime', +] diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 772942567a..6006c84ddb 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -5,6 +5,7 @@ mod benchmarking; +pub mod migrations; #[cfg(test)] mod mock; #[cfg(test)] @@ -90,6 +91,8 @@ pub struct VaultTransfer { destination_address: C::ChainAccount, } +pub const PALLET_VERSION: StorageVersion = StorageVersion::new(1); + #[frame_support::pallet] pub mod pallet { use super::*; @@ -222,6 +225,7 @@ pub mod pallet { } #[pallet::pallet] + #[pallet::storage_version(PALLET_VERSION)] #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); diff --git a/state-chain/pallets/cf-ingress-egress/src/migrations.rs b/state-chain/pallets/cf-ingress-egress/src/migrations.rs new file mode 100644 index 0000000000..f632b58ce8 --- /dev/null +++ b/state-chain/pallets/cf-ingress-egress/src/migrations.rs @@ -0,0 +1,6 @@ +pub mod ingress_expiry; + +use cf_runtime_upgrade_utilities::VersionedMigration; + +pub type PalletMigration = + (VersionedMigration, ingress_expiry::Migration, 0, 1>,); diff --git a/state-chain/pallets/cf-ingress-egress/src/migrations/ingress_expiry.rs b/state-chain/pallets/cf-ingress-egress/src/migrations/ingress_expiry.rs new file mode 100644 index 0000000000..3c5c33943b --- /dev/null +++ b/state-chain/pallets/cf-ingress-egress/src/migrations/ingress_expiry.rs @@ -0,0 +1,119 @@ +use crate::*; +use frame_support::traits::OnRuntimeUpgrade; +use sp_std::marker::PhantomData; + +#[cfg(feature = "try-runtime")] +use codec::{Decode, Encode}; +#[cfg(feature = "try-runtime")] +use frame_support::dispatch::DispatchError; + +// Copied from state-chain/node/src/chain_spec/testnet.rs: +// These represent approximately 2 hours on testnet block times +pub const BITCOIN_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / (10 * 60); +pub const ETHEREUM_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 14; +pub const POLKADOT_EXPIRY_BLOCKS: u32 = 2 * 60 * 60 / 6; + +pub struct Migration, I: 'static>(PhantomData<(T, I)>); + +// These were removed in 0.9.4 +mod old { + + use super::*; + + #[derive( + CloneNoBound, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen, + )] + #[scale_info(skip_type_params(T, I))] + pub struct DepositChannelDetails, I: 'static> { + pub deposit_channel: DepositChannel, + /// The block number at which the deposit channel was opened, expressed as a block number + /// on the external Chain. + pub opened_at: ::ChainBlockNumber, + // *State Chain block number* + pub expires_at: BlockNumberFor, + } + + #[frame_support::storage_alias] + pub type ChannelActions, I: 'static> = StorageMap< + Pallet, + Twox64Concat, + TargetChainAccount, + ChannelAction<::AccountId>, + OptionQuery, + >; + + #[frame_support::storage_alias] + pub type DepositChannelLookup, I: 'static> = StorageMap< + Pallet, + Twox64Concat, + TargetChainAccount, + DepositChannelDetails, + OptionQuery, + >; +} + +impl, I: 'static> OnRuntimeUpgrade for Migration { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let lifetime: TargetChainBlockNumber = match T::TargetChain::NAME { + "Bitcoin" => BITCOIN_EXPIRY_BLOCKS.into(), + "Ethereum" => ETHEREUM_EXPIRY_BLOCKS.into(), + "Polkadot" => POLKADOT_EXPIRY_BLOCKS.into(), + _ => unreachable!("Unsupported chain"), + }; + + DepositChannelLifetime::::put(lifetime); + + let channel_lifetime = DepositChannelLifetime::::get(); + let current_external_block_height = T::ChainTracking::get_block_height(); + let expiry_block = current_external_block_height.saturating_add(channel_lifetime); + let recycle_block = expiry_block.saturating_add(channel_lifetime); + + let old_channel_lookup = old::DepositChannelLookup::::drain().collect::>(); + + for (address, old_channel) in old_channel_lookup { + if let Some(action) = old::ChannelActions::::take(&address) { + DepositChannelLookup::::insert( + address.clone(), + DepositChannelDetails { + deposit_channel: old_channel.deposit_channel, + opened_at: old_channel.opened_at, + expires_at: expiry_block, + action, + }, + ); + } + + // We're just going to recycle them 2 hours from when we did the migration. + DepositChannelRecycleBlocks::::append((recycle_block, address)); + + // Remove any we missed above. + let _ = old::ChannelActions::::drain().collect::>(); + } + + Weight::zero() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + let number_of_channels_in_lookup = + old::DepositChannelLookup::::iter_keys().count() as u32; + + Ok(number_of_channels_in_lookup.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), DispatchError> { + let number_of_channels_in_lookup_pre_migration = ::decode(&mut &state[..]).unwrap(); + ensure!( + DepositChannelLookup::::iter_keys().count() as u32 == + number_of_channels_in_lookup_pre_migration, + "DepositChannelLookup migration failed." + ); + ensure!( + DepositChannelRecycleBlocks::::decode_len().unwrap_or_default() as u32 == + number_of_channels_in_lookup_pre_migration, + "DepositChannelRecycleBlocks migration failed." + ); + Ok(()) + } +} From 1b271e79dda7737c9206d0a92586f8dd47ceb721 Mon Sep 17 00:00:00 2001 From: kylezs Date: Mon, 25 Sep 2023 13:57:11 +0200 Subject: [PATCH 15/19] chore: use TargetChainBlockNumber --- state-chain/pallets/cf-ingress-egress/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 6006c84ddb..fe475c2c6f 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -518,7 +518,7 @@ pub mod pallet { pub fn process_deposits( origin: OriginFor, deposit_witnesses: Vec>, - block_height: ::ChainBlockNumber, + block_height: TargetChainBlockNumber, ) -> DispatchResult { T::EnsureWitnessed::ensure_origin(origin)?; @@ -761,7 +761,7 @@ impl, I: 'static> Pallet { asset: TargetChainAsset, amount: TargetChainAmount, deposit_details: ::DepositDetails, - block_height: ::ChainBlockNumber, + block_height: TargetChainBlockNumber, ) -> DispatchResult { let deposit_channel_details = DepositChannelLookup::::get(&deposit_address) .ok_or(Error::::InvalidDepositAddress)?; From ca470a1663b93037536bddd4775835fb9d29bcdc Mon Sep 17 00:00:00 2001 From: kylezs Date: Mon, 25 Sep 2023 14:26:18 +0200 Subject: [PATCH 16/19] chore: remove the expiry storage from the swapping and lp pallets expiry is now done in the ingress-egress pallet --- Cargo.lock | 2 ++ state-chain/pallets/cf-lp/Cargo.toml | 7 +++- state-chain/pallets/cf-lp/src/lib.rs | 4 +++ state-chain/pallets/cf-lp/src/migrations.rs | 6 ++++ .../cf-lp/src/migrations/remove_expiries.rs | 35 +++++++++++++++++++ state-chain/pallets/cf-swapping/Cargo.toml | 7 +++- state-chain/pallets/cf-swapping/src/lib.rs | 4 +++ .../pallets/cf-swapping/src/migrations.rs | 6 ++++ .../src/migrations/remove_expiries.rs | 34 ++++++++++++++++++ 9 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 state-chain/pallets/cf-lp/src/migrations.rs create mode 100644 state-chain/pallets/cf-lp/src/migrations/remove_expiries.rs create mode 100644 state-chain/pallets/cf-swapping/src/migrations.rs create mode 100644 state-chain/pallets/cf-swapping/src/migrations/remove_expiries.rs diff --git a/Cargo.lock b/Cargo.lock index b5590f0b2c..f09ad58c8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7209,6 +7209,7 @@ version = "0.1.0" dependencies = [ "cf-chains", "cf-primitives", + "cf-runtime-upgrade-utilities", "cf-test-utilities", "cf-traits", "frame-benchmarking", @@ -7275,6 +7276,7 @@ version = "0.1.0" dependencies = [ "cf-chains", "cf-primitives", + "cf-runtime-upgrade-utilities", "cf-test-utilities", "cf-traits", "frame-benchmarking", diff --git a/state-chain/pallets/cf-lp/Cargo.toml b/state-chain/pallets/cf-lp/Cargo.toml index 163d8d508e..bc5c0cea57 100644 --- a/state-chain/pallets/cf-lp/Cargo.toml +++ b/state-chain/pallets/cf-lp/Cargo.toml @@ -17,6 +17,7 @@ targets = ['x86_64-unknown-linux-gnu'] cf-chains = { path = '../../chains', default-features = false } cf-primitives = { path = '../../primitives', default-features = false } cf-traits = { path = '../../traits', default-features = false } +cf-runtime-upgrade-utilities = { path = '../../runtime-upgrade-utilities', default-features = false } serde = { version = '1.0.126', default_features = false, features = [ 'alloc', @@ -49,6 +50,7 @@ std = [ 'cf-chains/std', 'cf-primitives/std', 'cf-traits/std', + 'cf-runtime-upgrade-utilities/std', 'codec/std', 'frame-benchmarking/std', 'frame-support/std', @@ -66,4 +68,7 @@ runtime-benchmarks = [ 'frame-system/runtime-benchmarks', 'pallet-cf-account-roles/runtime-benchmarks', ] -try-runtime = ['frame-support/try-runtime'] +try-runtime = [ + 'cf-runtime-upgrade-utilities/try-runtime', + 'frame-support/try-runtime', +] diff --git a/state-chain/pallets/cf-lp/src/lib.rs b/state-chain/pallets/cf-lp/src/lib.rs index 8f781fe184..2685f602ba 100644 --- a/state-chain/pallets/cf-lp/src/lib.rs +++ b/state-chain/pallets/cf-lp/src/lib.rs @@ -18,9 +18,12 @@ mod mock; #[cfg(test)] mod tests; +pub mod migrations; pub mod weights; pub use weights::WeightInfo; +pub const PALLET_VERSION: StorageVersion = StorageVersion::new(1); + impl_pallet_safe_mode!(PalletSafeMode; deposit_enabled, withdrawal_enabled); #[frame_support::pallet] @@ -113,6 +116,7 @@ pub mod pallet { } #[pallet::pallet] + #[pallet::storage_version(PALLET_VERSION)] #[pallet::without_storage_info] pub struct Pallet(PhantomData); diff --git a/state-chain/pallets/cf-lp/src/migrations.rs b/state-chain/pallets/cf-lp/src/migrations.rs new file mode 100644 index 0000000000..78943f89a1 --- /dev/null +++ b/state-chain/pallets/cf-lp/src/migrations.rs @@ -0,0 +1,6 @@ +pub mod remove_expiries; + +use cf_runtime_upgrade_utilities::VersionedMigration; + +pub type PalletMigration = + (VersionedMigration, remove_expiries::Migration, 0, 1>,); diff --git a/state-chain/pallets/cf-lp/src/migrations/remove_expiries.rs b/state-chain/pallets/cf-lp/src/migrations/remove_expiries.rs new file mode 100644 index 0000000000..b5c20e8fd7 --- /dev/null +++ b/state-chain/pallets/cf-lp/src/migrations/remove_expiries.rs @@ -0,0 +1,35 @@ +use crate::*; +use frame_support::{dispatch::Vec, traits::OnRuntimeUpgrade}; +use sp_std::marker::PhantomData; + +pub struct Migration(PhantomData); + +mod old { + + use super::*; + + use cf_primitives::ChannelId; + use frame_support::pallet_prelude::ValueQuery; + + #[frame_support::storage_alias] + pub type SwapTTL = StorageValue, BlockNumberFor, ValueQuery>; + + #[frame_support::storage_alias] + pub type SwapChannelExpiries = StorageMap< + Pallet, + Twox64Concat, + BlockNumberFor, + Vec<(ChannelId, ForeignChainAddress)>, + ValueQuery, + >; +} + +impl OnRuntimeUpgrade for Migration { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let _ = old::SwapChannelExpiries::::drain().collect::>(); + + let _ = old::SwapTTL::::take(); + + Weight::zero() + } +} diff --git a/state-chain/pallets/cf-swapping/Cargo.toml b/state-chain/pallets/cf-swapping/Cargo.toml index f018f19a52..8911154184 100644 --- a/state-chain/pallets/cf-swapping/Cargo.toml +++ b/state-chain/pallets/cf-swapping/Cargo.toml @@ -16,6 +16,7 @@ targets = ['x86_64-unknown-linux-gnu'] # Internal dependencies cf-chains = { path = '../../chains', default-features = false } cf-primitives = { path = '../../primitives', default-features = false } +cf-runtime-upgrade-utilities = { path = '../../runtime-upgrade-utilities', default-features = false } cf-traits = { path = '../../traits', default-features = false } log = { version = '0.4.16', default-features = false } @@ -48,6 +49,7 @@ default = ['std'] std = [ 'cf-chains/std', 'cf-primitives/std', + 'cf-runtime-upgrade-utilities/std', 'cf-traits/std', 'codec/std', 'frame-benchmarking/std', @@ -68,4 +70,7 @@ runtime-benchmarks = [ 'frame-system/runtime-benchmarks', 'pallet-cf-account-roles/runtime-benchmarks', ] -try-runtime = ['frame-support/try-runtime'] +try-runtime = [ + 'cf-runtime-upgrade-utilities/try-runtime', + 'frame-support/try-runtime', +] diff --git a/state-chain/pallets/cf-swapping/src/lib.rs b/state-chain/pallets/cf-swapping/src/lib.rs index 3cd21a0370..e3ee7b7084 100644 --- a/state-chain/pallets/cf-swapping/src/lib.rs +++ b/state-chain/pallets/cf-swapping/src/lib.rs @@ -28,9 +28,12 @@ mod tests; mod benchmarking; +pub mod migrations; pub mod weights; pub use weights::WeightInfo; +pub const PALLET_VERSION: StorageVersion = StorageVersion::new(1); + const BASIS_POINTS_PER_MILLION: u32 = 100; #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] @@ -202,6 +205,7 @@ pub mod pallet { } #[pallet::pallet] + #[pallet::storage_version(PALLET_VERSION)] #[pallet::without_storage_info] pub struct Pallet(PhantomData); diff --git a/state-chain/pallets/cf-swapping/src/migrations.rs b/state-chain/pallets/cf-swapping/src/migrations.rs new file mode 100644 index 0000000000..78943f89a1 --- /dev/null +++ b/state-chain/pallets/cf-swapping/src/migrations.rs @@ -0,0 +1,6 @@ +pub mod remove_expiries; + +use cf_runtime_upgrade_utilities::VersionedMigration; + +pub type PalletMigration = + (VersionedMigration, remove_expiries::Migration, 0, 1>,); diff --git a/state-chain/pallets/cf-swapping/src/migrations/remove_expiries.rs b/state-chain/pallets/cf-swapping/src/migrations/remove_expiries.rs new file mode 100644 index 0000000000..d4071992d8 --- /dev/null +++ b/state-chain/pallets/cf-swapping/src/migrations/remove_expiries.rs @@ -0,0 +1,34 @@ +use crate::*; +use frame_support::traits::OnRuntimeUpgrade; +use sp_std::marker::PhantomData; + +pub struct Migration(PhantomData); + +mod old { + + use super::*; + + use frame_support::pallet_prelude::ValueQuery; + + #[frame_support::storage_alias] + pub type SwapTTL = StorageValue, BlockNumberFor, ValueQuery>; + + #[frame_support::storage_alias] + pub type SwapChannelExpiries = StorageMap< + Pallet, + Twox64Concat, + BlockNumberFor, + Vec<(ChannelId, ForeignChainAddress)>, + ValueQuery, + >; +} + +impl OnRuntimeUpgrade for Migration { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let _ = old::SwapChannelExpiries::::drain().collect::>(); + + let _ = old::SwapTTL::::take(); + + Weight::zero() + } +} From 27d1541b9658565df617b1c2a2e63877b9fc967d Mon Sep 17 00:00:00 2001 From: kylezs Date: Tue, 26 Sep 2023 16:06:36 +0200 Subject: [PATCH 17/19] chore: update SDK version --- bouncer/package.json | 2 +- bouncer/pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bouncer/package.json b/bouncer/package.json index 717c7097d7..2f7b9db764 100644 --- a/bouncer/package.json +++ b/bouncer/package.json @@ -6,7 +6,7 @@ "prettier:write": "prettier --write ." }, "dependencies": { - "@chainflip-io/cli": "^0.1.3", + "@chainflip-io/cli": "^0.1.4", "@polkadot/api": "10.7.2", "@polkadot/keyring": "12.2.1", "@polkadot/util": "12.2.1", diff --git a/bouncer/pnpm-lock.yaml b/bouncer/pnpm-lock.yaml index f6040fb378..adba5506a4 100644 --- a/bouncer/pnpm-lock.yaml +++ b/bouncer/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@chainflip-io/cli': - specifier: ^0.1.3 - version: 0.1.3 + specifier: ^0.1.4 + version: 0.1.4 '@polkadot/api': specifier: 10.7.2 version: 10.7.2 @@ -95,8 +95,8 @@ packages: resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==} dev: false - /@chainflip-io/cli@0.1.3: - resolution: {integrity: sha512-bCd9xMUy1VghAMjPB3hMNzNk59OOF9nLvS/gO6O9CBhohwlzOO2hf08mui0u36SerQBOYfCsRuM4OjuwzlefnQ==, tarball: https://npm.pkg.github.com/download/@chainflip-io/cli/0.1.3/60e786fbf686f278bd78b531b7675de9ca671558} + /@chainflip-io/cli@0.1.4: + resolution: {integrity: sha512-c91GIZvyBWYYzARuJpJZzKnw00n+i++kNl3GLSyorUuMsFLEz5qkyMf0i1bFTIp/No0nvWI0MAOgnofP0zTOrQ==, tarball: https://npm.pkg.github.com/download/@chainflip-io/cli/0.1.4/52273fa90fdea2826c552689fe54e909b57e39a5} hasBin: true dependencies: ethers: 6.7.1 From 29f2fb6be2a5d440432510cc927c2b7d3064f4b9 Mon Sep 17 00:00:00 2001 From: kylezs Date: Wed, 27 Sep 2023 11:11:28 +0200 Subject: [PATCH 18/19] chore: remove now-invalid bouncer test --- bouncer/shared/lp_deposit_expiry.ts | 47 --------------------------- bouncer/tests/all_concurrent_tests.ts | 2 -- bouncer/tests/lp_deposit_expiry.ts | 13 -------- 3 files changed, 62 deletions(-) delete mode 100644 bouncer/shared/lp_deposit_expiry.ts delete mode 100755 bouncer/tests/lp_deposit_expiry.ts diff --git a/bouncer/shared/lp_deposit_expiry.ts b/bouncer/shared/lp_deposit_expiry.ts deleted file mode 100644 index 065a17767e..0000000000 --- a/bouncer/shared/lp_deposit_expiry.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Keyring } from '@polkadot/keyring'; -import { cryptoWaitReady } from '@polkadot/util-crypto'; -import { observeEvent, getChainflipApi, lpMutex } from '../shared/utils'; -import { sendBtc } from '../shared/send_btc'; -import { submitGovernanceExtrinsic } from '../shared/cf_governance'; - -export async function testLpDepositExpiry() { - await cryptoWaitReady(); - const keyring = new Keyring({ type: 'sr25519' }); - const lpUri = process.env.LP_URI ?? '//LP_1'; - const lp = keyring.createFromUri(lpUri); - - const chainflip = await getChainflipApi(); - - console.log('=== Testing expiry of funded LP deposit address ==='); - const originalExpiryTime = Number(await chainflip.query.liquidityProvider.lpTTL()); - console.log('Setting expiry time for LP addresses to 10 blocks'); - - await submitGovernanceExtrinsic(chainflip.tx.liquidityProvider.setLpTtl(10)); - await observeEvent('liquidityProvider:LpTtlSet', chainflip); - - console.log('Requesting new BTC LP deposit address'); - lpMutex.runExclusive(async () => { - await chainflip.tx.liquidityProvider - .requestLiquidityDepositAddress('Btc') - .signAndSend(lp, { nonce: -1 }); - }); - - const depositEventResult = await observeEvent( - 'liquidityProvider:LiquidityDepositAddressReady', - chainflip, - (event) => event.data.depositAddress.Btc, - ); - const ingressAddress = depositEventResult.data.depositAddress.Btc; - - console.log('Funding BTC LP deposit address of ' + ingressAddress + ' with 1 BTC'); - - await sendBtc(ingressAddress, 1); - await observeEvent('liquidityProvider:LiquidityDepositAddressExpired', chainflip); - - console.log('Restoring expiry time for LP addresses to ' + originalExpiryTime + ' blocks'); - await submitGovernanceExtrinsic(chainflip.tx.liquidityProvider.setLpTtl(originalExpiryTime)); - - await observeEvent('liquidityProvider:LpTtlSet', chainflip); - - console.log('=== LP deposit expiry test complete ==='); -} diff --git a/bouncer/tests/all_concurrent_tests.ts b/bouncer/tests/all_concurrent_tests.ts index 3dc31084bf..692dda3569 100755 --- a/bouncer/tests/all_concurrent_tests.ts +++ b/bouncer/tests/all_concurrent_tests.ts @@ -1,5 +1,4 @@ #!/usr/bin/env -S pnpm tsx -import { testLpDepositExpiry } from '../shared/lp_deposit_expiry'; import { testAllSwaps } from '../shared/swapping'; import { testEthereumDeposits } from '../shared/ethereum_deposits'; import { runWithTimeout, observeBadEvents } from '../shared/utils'; @@ -13,7 +12,6 @@ async function runAllConcurrentTests() { await Promise.all([ testAllSwaps(), - testLpDepositExpiry(), testEthereumDeposits(), testFundRedeem('redeem'), testMultipleMembersGovernance(), diff --git a/bouncer/tests/lp_deposit_expiry.ts b/bouncer/tests/lp_deposit_expiry.ts deleted file mode 100755 index 7820d2a145..0000000000 --- a/bouncer/tests/lp_deposit_expiry.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env -S pnpm tsx -import { testLpDepositExpiry } from '../shared/lp_deposit_expiry'; -import { runWithTimeout } from '../shared/utils'; - -async function main(): Promise { - await testLpDepositExpiry(); - process.exit(0); -} - -runWithTimeout(main(), 120000).catch((error) => { - console.error(error); - process.exit(-1); -}); From b510befb808ed7d464f40987fb1cf373107d4a07 Mon Sep 17 00:00:00 2001 From: kylezs Date: Wed, 27 Sep 2023 13:10:19 +0200 Subject: [PATCH 19/19] chore: do the migrations --- .../pallets/cf-ingress-egress/src/lib.rs | 15 ++++++++++++- state-chain/pallets/cf-lp/src/lib.rs | 21 ++++++++++++++++++- state-chain/pallets/cf-swapping/src/lib.rs | 16 +++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index fe475c2c6f..37b068eee7 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -12,7 +12,7 @@ mod mock; mod tests; pub mod weights; use cf_runtime_utilities::log_or_panic; -use frame_support::sp_runtime::SaturatedConversion; +use frame_support::{sp_runtime::SaturatedConversion, traits::OnRuntimeUpgrade}; pub use weights::WeightInfo; use cf_chains::{ @@ -451,6 +451,19 @@ pub mod pallet { // Egress all scheduled Cross chain messages Self::do_egress_scheduled_ccm(); } + + fn on_runtime_upgrade() -> Weight { + migrations::PalletMigration::::on_runtime_upgrade() + } + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + migrations::PalletMigration::::pre_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: sp_std::vec::Vec) -> Result<(), DispatchError> { + migrations::PalletMigration::::post_upgrade(state) + } } #[pallet::call] diff --git a/state-chain/pallets/cf-lp/src/lib.rs b/state-chain/pallets/cf-lp/src/lib.rs index 2685f602ba..c95ae66a4c 100644 --- a/state-chain/pallets/cf-lp/src/lib.rs +++ b/state-chain/pallets/cf-lp/src/lib.rs @@ -7,7 +7,9 @@ use cf_traits::{ impl_pallet_safe_mode, liquidity::LpBalanceApi, AccountRoleRegistry, Chainflip, DepositApi, EgressApi, }; -use frame_support::{pallet_prelude::*, sp_runtime::DispatchResult}; +use frame_support::{ + dispatch::Vec, pallet_prelude::*, sp_runtime::DispatchResult, traits::OnRuntimeUpgrade, +}; use frame_system::pallet_prelude::*; pub use pallet::*; @@ -136,6 +138,23 @@ pub mod pallet { ForeignChainAddress, >; + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> Weight { + migrations::PalletMigration::::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + migrations::PalletMigration::::pre_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), DispatchError> { + migrations::PalletMigration::::post_upgrade(state) + } + } + #[pallet::call] impl Pallet { /// For when the user wants to deposit assets into the Chain. diff --git a/state-chain/pallets/cf-swapping/src/lib.rs b/state-chain/pallets/cf-swapping/src/lib.rs index e3ee7b7084..7f8bca1cef 100644 --- a/state-chain/pallets/cf-swapping/src/lib.rs +++ b/state-chain/pallets/cf-swapping/src/lib.rs @@ -14,12 +14,12 @@ use frame_support::{ DispatchError, Permill, }, storage::with_storage_layer, + traits::OnRuntimeUpgrade, }; use frame_system::pallet_prelude::*; pub use pallet::*; use sp_arithmetic::{helpers_128bit::multiply_by_rational_with_rounding, traits::Zero, Rounding}; use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec}; - #[cfg(test)] mod mock; @@ -465,6 +465,20 @@ pub mod pallet { } } } + + fn on_runtime_upgrade() -> Weight { + migrations::PalletMigration::::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + migrations::PalletMigration::::pre_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), DispatchError> { + migrations::PalletMigration::::post_upgrade(state) + } } #[pallet::call]